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

200 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-06 06:03 +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 retring 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 if self.details: 

146 return "{} {} {}".format(self.code, self.message, self.details) 

147 else: 

148 return "{} {}".format(self.code, self.message) 

149 

150 @property 

151 def reason(self): 

152 """The reason of the error. 

153 

154 Reference: 

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

156 

157 Returns: 

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

159 """ 

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

161 

162 @property 

163 def domain(self): 

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

165 

166 Reference: 

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

168 

169 Returns: 

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

171 """ 

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

173 

174 @property 

175 def metadata(self): 

176 """Additional structured details about this error. 

177 

178 Reference: 

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

180 

181 Returns: 

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

183 """ 

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

185 

186 @property 

187 def errors(self): 

188 """Detailed error information. 

189 

190 Returns: 

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

192 """ 

193 return list(self._errors) 

194 

195 @property 

196 def details(self): 

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

198 

199 Reference: 

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

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

202 

203 Returns: 

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

205 """ 

206 return list(self._details) 

207 

208 @property 

209 def response(self): 

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

211 gRPC call metadata.""" 

212 return self._response 

213 

214 

215class Redirection(GoogleAPICallError): 

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

217 

218 

219class MovedPermanently(Redirection): 

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

221 

222 code = http.client.MOVED_PERMANENTLY 

223 

224 

225class NotModified(Redirection): 

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

227 

228 code = http.client.NOT_MODIFIED 

229 

230 

231class TemporaryRedirect(Redirection): 

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

233 

234 code = http.client.TEMPORARY_REDIRECT 

235 

236 

237class ResumeIncomplete(Redirection): 

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

239 

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

241 APIs differ in their use of this status code. 

242 """ 

243 

244 code = 308 

245 

246 

247class ClientError(GoogleAPICallError): 

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

249 

250 

251class BadRequest(ClientError): 

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

253 

254 code = http.client.BAD_REQUEST 

255 

256 

257class InvalidArgument(BadRequest): 

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

259 

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

261 

262 

263class FailedPrecondition(BadRequest): 

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

265 error.""" 

266 

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

268 

269 

270class OutOfRange(BadRequest): 

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

272 

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

274 

275 

276class Unauthorized(ClientError): 

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

278 

279 code = http.client.UNAUTHORIZED 

280 

281 

282class Unauthenticated(Unauthorized): 

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

284 

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

286 

287 

288class Forbidden(ClientError): 

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

290 

291 code = http.client.FORBIDDEN 

292 

293 

294class PermissionDenied(Forbidden): 

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

296 

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

298 

299 

300class NotFound(ClientError): 

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

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

303 

304 code = http.client.NOT_FOUND 

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

306 

307 

308class MethodNotAllowed(ClientError): 

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

310 

311 code = http.client.METHOD_NOT_ALLOWED 

312 

313 

314class Conflict(ClientError): 

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

316 

317 code = http.client.CONFLICT 

318 

319 

320class AlreadyExists(Conflict): 

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

322 

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

324 

325 

326class Aborted(Conflict): 

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

328 

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

330 

331 

332class LengthRequired(ClientError): 

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

334 

335 code = http.client.LENGTH_REQUIRED 

336 

337 

338class PreconditionFailed(ClientError): 

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

340 

341 code = http.client.PRECONDITION_FAILED 

342 

343 

344class RequestRangeNotSatisfiable(ClientError): 

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

346 

347 code = http.client.REQUESTED_RANGE_NOT_SATISFIABLE 

348 

349 

350class TooManyRequests(ClientError): 

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

352 

353 code = http.client.TOO_MANY_REQUESTS 

354 

355 

356class ResourceExhausted(TooManyRequests): 

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

358 

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

360 

361 

362class Cancelled(ClientError): 

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

364 

365 # This maps to HTTP status code 499. See 

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

367 code = 499 

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

369 

370 

371class ServerError(GoogleAPICallError): 

372 """Base for 5xx responses.""" 

373 

374 

375class InternalServerError(ServerError): 

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

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

378 

379 code = http.client.INTERNAL_SERVER_ERROR 

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

381 

382 

383class Unknown(ServerError): 

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

385 

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

387 

388 

389class DataLoss(ServerError): 

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

391 

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

393 

394 

395class MethodNotImplemented(ServerError): 

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

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

398 

399 code = http.client.NOT_IMPLEMENTED 

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

401 

402 

403class BadGateway(ServerError): 

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

405 

406 code = http.client.BAD_GATEWAY 

407 

408 

409class ServiceUnavailable(ServerError): 

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

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

412 

413 code = http.client.SERVICE_UNAVAILABLE 

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

415 

416 

417class GatewayTimeout(ServerError): 

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

419 

420 code = http.client.GATEWAY_TIMEOUT 

421 

422 

423class DeadlineExceeded(GatewayTimeout): 

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

425 

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

427 

428 

429def exception_class_for_http_status(status_code): 

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

431 

432 Args: 

433 status_code (int): The HTTP status code. 

434 

435 Returns: 

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

437 """ 

438 return _HTTP_CODE_TO_EXCEPTION.get(status_code, GoogleAPICallError) 

439 

440 

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

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

443 

444 Args: 

445 status_code (int): The HTTP status code. 

446 message (str): The exception message. 

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

448 constructor. 

449 

450 Returns: 

451 GoogleAPICallError: An instance of the appropriate subclass of 

452 :class:`GoogleAPICallError`. 

453 """ 

454 error_class = exception_class_for_http_status(status_code) 

455 error = error_class(message, **kwargs) 

456 

457 if error.code is None: 

458 error.code = status_code 

459 

460 return error 

461 

462 

463def from_http_response(response): 

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

465 

466 Args: 

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

468 

469 Returns: 

470 GoogleAPICallError: An instance of the appropriate subclass of 

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

472 from the response. 

473 """ 

474 try: 

475 payload = response.json() 

476 except ValueError: 

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

478 

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

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

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

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

483 error_info = list( 

484 filter( 

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

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

487 details, 

488 ) 

489 ) 

490 error_info = error_info[0] if error_info else None 

491 

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

493 method=response.request.method, 

494 url=response.request.url, 

495 error=error_message, 

496 ) 

497 

498 exception = from_http_status( 

499 response.status_code, 

500 message, 

501 errors=errors, 

502 details=details, 

503 response=response, 

504 error_info=error_info, 

505 ) 

506 return exception 

507 

508 

509def exception_class_for_grpc_status(status_code): 

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

511 

512 Args: 

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

514 

515 Returns: 

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

517 """ 

518 return _GRPC_CODE_TO_EXCEPTION.get(status_code, GoogleAPICallError) 

519 

520 

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

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

523 

524 Args: 

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

526 message (str): The exception message. 

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

528 constructor. 

529 

530 Returns: 

531 GoogleAPICallError: An instance of the appropriate subclass of 

532 :class:`GoogleAPICallError`. 

533 """ 

534 

535 if isinstance(status_code, int): 

536 status_code = _INT_TO_GRPC_CODE.get(status_code, status_code) 

537 

538 error_class = exception_class_for_grpc_status(status_code) 

539 error = error_class(message, **kwargs) 

540 

541 if error.grpc_status_code is None: 

542 error.grpc_status_code = status_code 

543 

544 return error 

545 

546 

547def _is_informative_grpc_error(rpc_exc): 

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

549 

550 

551def _parse_grpc_error_details(rpc_exc): 

552 try: 

553 status = rpc_status.from_call(rpc_exc) 

554 except NotImplementedError: # workaround 

555 return [], None 

556 

557 if not status: 

558 return [], None 

559 

560 possible_errors = [ 

561 error_details_pb2.BadRequest, 

562 error_details_pb2.PreconditionFailure, 

563 error_details_pb2.QuotaFailure, 

564 error_details_pb2.ErrorInfo, 

565 error_details_pb2.RetryInfo, 

566 error_details_pb2.ResourceInfo, 

567 error_details_pb2.RequestInfo, 

568 error_details_pb2.DebugInfo, 

569 error_details_pb2.Help, 

570 error_details_pb2.LocalizedMessage, 

571 ] 

572 error_info = None 

573 error_details = [] 

574 for detail in status.details: 

575 matched_detail_cls = list( 

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

577 ) 

578 # If nothing matched, use detail directly. 

579 if len(matched_detail_cls) == 0: 

580 info = detail 

581 else: 

582 info = matched_detail_cls[0]() 

583 detail.Unpack(info) 

584 error_details.append(info) 

585 if isinstance(info, error_details_pb2.ErrorInfo): 

586 error_info = info 

587 return error_details, error_info 

588 

589 

590def from_grpc_error(rpc_exc): 

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

592 

593 Args: 

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

595 

596 Returns: 

597 GoogleAPICallError: An instance of the appropriate subclass of 

598 :class:`GoogleAPICallError`. 

599 """ 

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

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

602 if ( 

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

604 ) or _is_informative_grpc_error(rpc_exc): 

605 details, err_info = _parse_grpc_error_details(rpc_exc) 

606 return from_grpc_status( 

607 rpc_exc.code(), 

608 rpc_exc.details(), 

609 errors=(rpc_exc,), 

610 details=details, 

611 response=rpc_exc, 

612 error_info=err_info, 

613 ) 

614 else: 

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