Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/api_core/exceptions.py: 63%

205 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:37 +0000

1# Copyright 2014 Google LLC 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15"""Exceptions raised by Google API core & clients. 

16 

17This module provides base classes for all errors raised by libraries based 

18on :mod:`google.api_core`, including both HTTP and gRPC clients. 

19""" 

20 

21from __future__ import absolute_import 

22from __future__ import unicode_literals 

23 

24import http.client 

25from typing import Dict 

26from typing import Union 

27import warnings 

28 

29from google.rpc import error_details_pb2 

30 

31try: 

32 import grpc 

33 

34 try: 

35 from grpc_status import rpc_status 

36 except ImportError: # pragma: NO COVER 

37 warnings.warn( 

38 "Please install grpcio-status to obtain helpful grpc error messages.", 

39 ImportWarning, 

40 ) 

41 rpc_status = None 

42except ImportError: # pragma: NO COVER 

43 grpc = None 

44 

45# Lookup tables for mapping exceptions from HTTP and gRPC transports. 

46# Populated by _GoogleAPICallErrorMeta 

47_HTTP_CODE_TO_EXCEPTION: Dict[int, Exception] = {} 

48_GRPC_CODE_TO_EXCEPTION: Dict[int, Exception] = {} 

49 

50# Additional lookup table to map integer status codes to grpc status code 

51# grpc does not currently support initializing enums from ints 

52# i.e., grpc.StatusCode(5) raises an error 

53_INT_TO_GRPC_CODE = {} 

54if grpc is not None: # pragma: no branch 

55 for x in grpc.StatusCode: 

56 _INT_TO_GRPC_CODE[x.value[0]] = x 

57 

58 

59class GoogleAPIError(Exception): 

60 """Base class for all exceptions raised by Google API Clients.""" 

61 

62 pass 

63 

64 

65class DuplicateCredentialArgs(GoogleAPIError): 

66 """Raised when multiple credentials are passed.""" 

67 

68 pass 

69 

70 

71class RetryError(GoogleAPIError): 

72 """Raised when a function has exhausted all of its available retries. 

73 

74 Args: 

75 message (str): The exception message. 

76 cause (Exception): The last exception raised when retrying the 

77 function. 

78 """ 

79 

80 def __init__(self, message, cause): 

81 super(RetryError, self).__init__(message) 

82 self.message = message 

83 self._cause = cause 

84 

85 @property 

86 def cause(self): 

87 """The last exception raised when retrying the function.""" 

88 return self._cause 

89 

90 def __str__(self): 

91 return "{}, last exception: {}".format(self.message, self.cause) 

92 

93 

94class _GoogleAPICallErrorMeta(type): 

95 """Metaclass for registering GoogleAPICallError subclasses.""" 

96 

97 def __new__(mcs, name, bases, class_dict): 

98 cls = type.__new__(mcs, name, bases, class_dict) 

99 if cls.code is not None: 

100 _HTTP_CODE_TO_EXCEPTION.setdefault(cls.code, cls) 

101 if cls.grpc_status_code is not None: 

102 _GRPC_CODE_TO_EXCEPTION.setdefault(cls.grpc_status_code, cls) 

103 return cls 

104 

105 

106class GoogleAPICallError(GoogleAPIError, metaclass=_GoogleAPICallErrorMeta): 

107 """Base class for exceptions raised by calling API methods. 

108 

109 Args: 

110 message (str): The exception message. 

111 errors (Sequence[Any]): An optional list of error details. 

112 details (Sequence[Any]): An optional list of objects defined in google.rpc.error_details. 

113 response (Union[requests.Request, grpc.Call]): The response or 

114 gRPC call metadata. 

115 error_info (Union[error_details_pb2.ErrorInfo, None]): An optional object containing error info 

116 (google.rpc.error_details.ErrorInfo). 

117 """ 

118 

119 code: Union[int, None] = None 

120 """Optional[int]: The HTTP status code associated with this error. 

121 

122 This may be ``None`` if the exception does not have a direct mapping 

123 to an HTTP error. 

124 

125 See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 

126 """ 

127 

128 grpc_status_code = None 

129 """Optional[grpc.StatusCode]: The gRPC status code associated with this 

130 error. 

131 

132 This may be ``None`` if the exception does not match up to a gRPC error. 

133 """ 

134 

135 def __init__(self, message, errors=(), details=(), response=None, error_info=None): 

136 super(GoogleAPICallError, self).__init__(message) 

137 self.message = message 

138 """str: The exception message.""" 

139 self._errors = errors 

140 self._details = details 

141 self._response = response 

142 self._error_info = error_info 

143 

144 def __str__(self): 

145 error_msg = "{} {}".format(self.code, self.message) 

146 if self.details: 

147 error_msg = "{} {}".format(error_msg, self.details) 

148 # Note: This else condition can be removed once proposal A from 

149 # b/284179390 is implemented. 

150 else: 

151 if self.errors: 

152 errors = [ 

153 f"{error.code}: {error.message}" 

154 for error in self.errors 

155 if hasattr(error, "code") and hasattr(error, "message") 

156 ] 

157 if errors: 

158 error_msg = "{} {}".format(error_msg, "\n".join(errors)) 

159 return error_msg 

160 

161 @property 

162 def reason(self): 

163 """The reason of the error. 

164 

165 Reference: 

166 https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto#L112 

167 

168 Returns: 

169 Union[str, None]: An optional string containing reason of the error. 

170 """ 

171 return self._error_info.reason if self._error_info else None 

172 

173 @property 

174 def domain(self): 

175 """The logical grouping to which the "reason" belongs. 

176 

177 Reference: 

178 https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto#L112 

179 

180 Returns: 

181 Union[str, None]: An optional string containing a logical grouping to which the "reason" belongs. 

182 """ 

183 return self._error_info.domain if self._error_info else None 

184 

185 @property 

186 def metadata(self): 

187 """Additional structured details about this error. 

188 

189 Reference: 

190 https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto#L112 

191 

192 Returns: 

193 Union[Dict[str, str], None]: An optional object containing structured details about the error. 

194 """ 

195 return self._error_info.metadata if self._error_info else None 

196 

197 @property 

198 def errors(self): 

199 """Detailed error information. 

200 

201 Returns: 

202 Sequence[Any]: A list of additional error details. 

203 """ 

204 return list(self._errors) 

205 

206 @property 

207 def details(self): 

208 """Information contained in google.rpc.status.details. 

209 

210 Reference: 

211 https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto 

212 https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto 

213 

214 Returns: 

215 Sequence[Any]: A list of structured objects from error_details.proto 

216 """ 

217 return list(self._details) 

218 

219 @property 

220 def response(self): 

221 """Optional[Union[requests.Request, grpc.Call]]: The response or 

222 gRPC call metadata.""" 

223 return self._response 

224 

225 

226class Redirection(GoogleAPICallError): 

227 """Base class for for all redirection (HTTP 3xx) responses.""" 

228 

229 

230class MovedPermanently(Redirection): 

231 """Exception mapping a ``301 Moved Permanently`` response.""" 

232 

233 code = http.client.MOVED_PERMANENTLY 

234 

235 

236class NotModified(Redirection): 

237 """Exception mapping a ``304 Not Modified`` response.""" 

238 

239 code = http.client.NOT_MODIFIED 

240 

241 

242class TemporaryRedirect(Redirection): 

243 """Exception mapping a ``307 Temporary Redirect`` response.""" 

244 

245 code = http.client.TEMPORARY_REDIRECT 

246 

247 

248class ResumeIncomplete(Redirection): 

249 """Exception mapping a ``308 Resume Incomplete`` response. 

250 

251 .. note:: :attr:`http.client.PERMANENT_REDIRECT` is ``308``, but Google 

252 APIs differ in their use of this status code. 

253 """ 

254 

255 code = 308 

256 

257 

258class ClientError(GoogleAPICallError): 

259 """Base class for all client error (HTTP 4xx) responses.""" 

260 

261 

262class BadRequest(ClientError): 

263 """Exception mapping a ``400 Bad Request`` response.""" 

264 

265 code = http.client.BAD_REQUEST 

266 

267 

268class InvalidArgument(BadRequest): 

269 """Exception mapping a :attr:`grpc.StatusCode.INVALID_ARGUMENT` error.""" 

270 

271 grpc_status_code = grpc.StatusCode.INVALID_ARGUMENT if grpc is not None else None 

272 

273 

274class FailedPrecondition(BadRequest): 

275 """Exception mapping a :attr:`grpc.StatusCode.FAILED_PRECONDITION` 

276 error.""" 

277 

278 grpc_status_code = grpc.StatusCode.FAILED_PRECONDITION if grpc is not None else None 

279 

280 

281class OutOfRange(BadRequest): 

282 """Exception mapping a :attr:`grpc.StatusCode.OUT_OF_RANGE` error.""" 

283 

284 grpc_status_code = grpc.StatusCode.OUT_OF_RANGE if grpc is not None else None 

285 

286 

287class Unauthorized(ClientError): 

288 """Exception mapping a ``401 Unauthorized`` response.""" 

289 

290 code = http.client.UNAUTHORIZED 

291 

292 

293class Unauthenticated(Unauthorized): 

294 """Exception mapping a :attr:`grpc.StatusCode.UNAUTHENTICATED` error.""" 

295 

296 grpc_status_code = grpc.StatusCode.UNAUTHENTICATED if grpc is not None else None 

297 

298 

299class Forbidden(ClientError): 

300 """Exception mapping a ``403 Forbidden`` response.""" 

301 

302 code = http.client.FORBIDDEN 

303 

304 

305class PermissionDenied(Forbidden): 

306 """Exception mapping a :attr:`grpc.StatusCode.PERMISSION_DENIED` error.""" 

307 

308 grpc_status_code = grpc.StatusCode.PERMISSION_DENIED if grpc is not None else None 

309 

310 

311class NotFound(ClientError): 

312 """Exception mapping a ``404 Not Found`` response or a 

313 :attr:`grpc.StatusCode.NOT_FOUND` error.""" 

314 

315 code = http.client.NOT_FOUND 

316 grpc_status_code = grpc.StatusCode.NOT_FOUND if grpc is not None else None 

317 

318 

319class MethodNotAllowed(ClientError): 

320 """Exception mapping a ``405 Method Not Allowed`` response.""" 

321 

322 code = http.client.METHOD_NOT_ALLOWED 

323 

324 

325class Conflict(ClientError): 

326 """Exception mapping a ``409 Conflict`` response.""" 

327 

328 code = http.client.CONFLICT 

329 

330 

331class AlreadyExists(Conflict): 

332 """Exception mapping a :attr:`grpc.StatusCode.ALREADY_EXISTS` error.""" 

333 

334 grpc_status_code = grpc.StatusCode.ALREADY_EXISTS if grpc is not None else None 

335 

336 

337class Aborted(Conflict): 

338 """Exception mapping a :attr:`grpc.StatusCode.ABORTED` error.""" 

339 

340 grpc_status_code = grpc.StatusCode.ABORTED if grpc is not None else None 

341 

342 

343class LengthRequired(ClientError): 

344 """Exception mapping a ``411 Length Required`` response.""" 

345 

346 code = http.client.LENGTH_REQUIRED 

347 

348 

349class PreconditionFailed(ClientError): 

350 """Exception mapping a ``412 Precondition Failed`` response.""" 

351 

352 code = http.client.PRECONDITION_FAILED 

353 

354 

355class RequestRangeNotSatisfiable(ClientError): 

356 """Exception mapping a ``416 Request Range Not Satisfiable`` response.""" 

357 

358 code = http.client.REQUESTED_RANGE_NOT_SATISFIABLE 

359 

360 

361class TooManyRequests(ClientError): 

362 """Exception mapping a ``429 Too Many Requests`` response.""" 

363 

364 code = http.client.TOO_MANY_REQUESTS 

365 

366 

367class ResourceExhausted(TooManyRequests): 

368 """Exception mapping a :attr:`grpc.StatusCode.RESOURCE_EXHAUSTED` error.""" 

369 

370 grpc_status_code = grpc.StatusCode.RESOURCE_EXHAUSTED if grpc is not None else None 

371 

372 

373class Cancelled(ClientError): 

374 """Exception mapping a :attr:`grpc.StatusCode.CANCELLED` error.""" 

375 

376 # This maps to HTTP status code 499. See 

377 # https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto 

378 code = 499 

379 grpc_status_code = grpc.StatusCode.CANCELLED if grpc is not None else None 

380 

381 

382class ServerError(GoogleAPICallError): 

383 """Base for 5xx responses.""" 

384 

385 

386class InternalServerError(ServerError): 

387 """Exception mapping a ``500 Internal Server Error`` response. or a 

388 :attr:`grpc.StatusCode.INTERNAL` error.""" 

389 

390 code = http.client.INTERNAL_SERVER_ERROR 

391 grpc_status_code = grpc.StatusCode.INTERNAL if grpc is not None else None 

392 

393 

394class Unknown(ServerError): 

395 """Exception mapping a :attr:`grpc.StatusCode.UNKNOWN` error.""" 

396 

397 grpc_status_code = grpc.StatusCode.UNKNOWN if grpc is not None else None 

398 

399 

400class DataLoss(ServerError): 

401 """Exception mapping a :attr:`grpc.StatusCode.DATA_LOSS` error.""" 

402 

403 grpc_status_code = grpc.StatusCode.DATA_LOSS if grpc is not None else None 

404 

405 

406class MethodNotImplemented(ServerError): 

407 """Exception mapping a ``501 Not Implemented`` response or a 

408 :attr:`grpc.StatusCode.UNIMPLEMENTED` error.""" 

409 

410 code = http.client.NOT_IMPLEMENTED 

411 grpc_status_code = grpc.StatusCode.UNIMPLEMENTED if grpc is not None else None 

412 

413 

414class BadGateway(ServerError): 

415 """Exception mapping a ``502 Bad Gateway`` response.""" 

416 

417 code = http.client.BAD_GATEWAY 

418 

419 

420class ServiceUnavailable(ServerError): 

421 """Exception mapping a ``503 Service Unavailable`` response or a 

422 :attr:`grpc.StatusCode.UNAVAILABLE` error.""" 

423 

424 code = http.client.SERVICE_UNAVAILABLE 

425 grpc_status_code = grpc.StatusCode.UNAVAILABLE if grpc is not None else None 

426 

427 

428class GatewayTimeout(ServerError): 

429 """Exception mapping a ``504 Gateway Timeout`` response.""" 

430 

431 code = http.client.GATEWAY_TIMEOUT 

432 

433 

434class DeadlineExceeded(GatewayTimeout): 

435 """Exception mapping a :attr:`grpc.StatusCode.DEADLINE_EXCEEDED` error.""" 

436 

437 grpc_status_code = grpc.StatusCode.DEADLINE_EXCEEDED if grpc is not None else None 

438 

439 

440def exception_class_for_http_status(status_code): 

441 """Return the exception class for a specific HTTP status code. 

442 

443 Args: 

444 status_code (int): The HTTP status code. 

445 

446 Returns: 

447 :func:`type`: the appropriate subclass of :class:`GoogleAPICallError`. 

448 """ 

449 return _HTTP_CODE_TO_EXCEPTION.get(status_code, GoogleAPICallError) 

450 

451 

452def from_http_status(status_code, message, **kwargs): 

453 """Create a :class:`GoogleAPICallError` from an HTTP status code. 

454 

455 Args: 

456 status_code (int): The HTTP status code. 

457 message (str): The exception message. 

458 kwargs: Additional arguments passed to the :class:`GoogleAPICallError` 

459 constructor. 

460 

461 Returns: 

462 GoogleAPICallError: An instance of the appropriate subclass of 

463 :class:`GoogleAPICallError`. 

464 """ 

465 error_class = exception_class_for_http_status(status_code) 

466 error = error_class(message, **kwargs) 

467 

468 if error.code is None: 

469 error.code = status_code 

470 

471 return error 

472 

473 

474def from_http_response(response): 

475 """Create a :class:`GoogleAPICallError` from a :class:`requests.Response`. 

476 

477 Args: 

478 response (requests.Response): The HTTP response. 

479 

480 Returns: 

481 GoogleAPICallError: An instance of the appropriate subclass of 

482 :class:`GoogleAPICallError`, with the message and errors populated 

483 from the response. 

484 """ 

485 try: 

486 payload = response.json() 

487 except ValueError: 

488 payload = {"error": {"message": response.text or "unknown error"}} 

489 

490 error_message = payload.get("error", {}).get("message", "unknown error") 

491 errors = payload.get("error", {}).get("errors", ()) 

492 # In JSON, details are already formatted in developer-friendly way. 

493 details = payload.get("error", {}).get("details", ()) 

494 error_info = list( 

495 filter( 

496 lambda detail: detail.get("@type", "") 

497 == "type.googleapis.com/google.rpc.ErrorInfo", 

498 details, 

499 ) 

500 ) 

501 error_info = error_info[0] if error_info else None 

502 

503 message = "{method} {url}: {error}".format( 

504 method=response.request.method, 

505 url=response.request.url, 

506 error=error_message, 

507 ) 

508 

509 exception = from_http_status( 

510 response.status_code, 

511 message, 

512 errors=errors, 

513 details=details, 

514 response=response, 

515 error_info=error_info, 

516 ) 

517 return exception 

518 

519 

520def exception_class_for_grpc_status(status_code): 

521 """Return the exception class for a specific :class:`grpc.StatusCode`. 

522 

523 Args: 

524 status_code (grpc.StatusCode): The gRPC status code. 

525 

526 Returns: 

527 :func:`type`: the appropriate subclass of :class:`GoogleAPICallError`. 

528 """ 

529 return _GRPC_CODE_TO_EXCEPTION.get(status_code, GoogleAPICallError) 

530 

531 

532def from_grpc_status(status_code, message, **kwargs): 

533 """Create a :class:`GoogleAPICallError` from a :class:`grpc.StatusCode`. 

534 

535 Args: 

536 status_code (Union[grpc.StatusCode, int]): The gRPC status code. 

537 message (str): The exception message. 

538 kwargs: Additional arguments passed to the :class:`GoogleAPICallError` 

539 constructor. 

540 

541 Returns: 

542 GoogleAPICallError: An instance of the appropriate subclass of 

543 :class:`GoogleAPICallError`. 

544 """ 

545 

546 if isinstance(status_code, int): 

547 status_code = _INT_TO_GRPC_CODE.get(status_code, status_code) 

548 

549 error_class = exception_class_for_grpc_status(status_code) 

550 error = error_class(message, **kwargs) 

551 

552 if error.grpc_status_code is None: 

553 error.grpc_status_code = status_code 

554 

555 return error 

556 

557 

558def _is_informative_grpc_error(rpc_exc): 

559 return hasattr(rpc_exc, "code") and hasattr(rpc_exc, "details") 

560 

561 

562def _parse_grpc_error_details(rpc_exc): 

563 try: 

564 status = rpc_status.from_call(rpc_exc) 

565 except NotImplementedError: # workaround 

566 return [], None 

567 

568 if not status: 

569 return [], None 

570 

571 possible_errors = [ 

572 error_details_pb2.BadRequest, 

573 error_details_pb2.PreconditionFailure, 

574 error_details_pb2.QuotaFailure, 

575 error_details_pb2.ErrorInfo, 

576 error_details_pb2.RetryInfo, 

577 error_details_pb2.ResourceInfo, 

578 error_details_pb2.RequestInfo, 

579 error_details_pb2.DebugInfo, 

580 error_details_pb2.Help, 

581 error_details_pb2.LocalizedMessage, 

582 ] 

583 error_info = None 

584 error_details = [] 

585 for detail in status.details: 

586 matched_detail_cls = list( 

587 filter(lambda x: detail.Is(x.DESCRIPTOR), possible_errors) 

588 ) 

589 # If nothing matched, use detail directly. 

590 if len(matched_detail_cls) == 0: 

591 info = detail 

592 else: 

593 info = matched_detail_cls[0]() 

594 detail.Unpack(info) 

595 error_details.append(info) 

596 if isinstance(info, error_details_pb2.ErrorInfo): 

597 error_info = info 

598 return error_details, error_info 

599 

600 

601def from_grpc_error(rpc_exc): 

602 """Create a :class:`GoogleAPICallError` from a :class:`grpc.RpcError`. 

603 

604 Args: 

605 rpc_exc (grpc.RpcError): The gRPC error. 

606 

607 Returns: 

608 GoogleAPICallError: An instance of the appropriate subclass of 

609 :class:`GoogleAPICallError`. 

610 """ 

611 # NOTE(lidiz) All gRPC error shares the parent class grpc.RpcError. 

612 # However, check for grpc.RpcError breaks backward compatibility. 

613 if ( 

614 grpc is not None and isinstance(rpc_exc, grpc.Call) 

615 ) or _is_informative_grpc_error(rpc_exc): 

616 details, err_info = _parse_grpc_error_details(rpc_exc) 

617 return from_grpc_status( 

618 rpc_exc.code(), 

619 rpc_exc.details(), 

620 errors=(rpc_exc,), 

621 details=details, 

622 response=rpc_exc, 

623 error_info=err_info, 

624 ) 

625 else: 

626 return GoogleAPICallError(str(rpc_exc), errors=(rpc_exc,), response=rpc_exc)