From 41b1895cf440badcd2e8172b7254e56da0d1bc4e Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 6 Oct 2020 00:20:18 +0000 Subject: [PATCH 1/6] fix: map LRO errors to library exception types --- google/api_core/exceptions.py | 15 ++++++++++++++- google/api_core/operation.py | 5 +++-- tests/unit/test_operation.py | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/google/api_core/exceptions.py b/google/api_core/exceptions.py index b9c46ca0..5a152cfc 100644 --- a/google/api_core/exceptions.py +++ b/google/api_core/exceptions.py @@ -26,6 +26,7 @@ try: import grpc + except ImportError: # pragma: NO COVER grpc = None @@ -34,6 +35,14 @@ _HTTP_CODE_TO_EXCEPTION = {} _GRPC_CODE_TO_EXCEPTION = {} +# Additional lookup table to map integer status codes to grpc status code +# grpc does not currently support initializing enums from ints +# i.e., grpc.StatusCode(5) raises an error +if grpc is not None: + _INT_TO_GRPC_CODE = {} + for x in grpc.StatusCode: + _INT_TO_GRPC_CODE[x.value[0]] = x + class GoogleAPIError(Exception): """Base class for all exceptions raised by Google API Clients.""" @@ -432,7 +441,7 @@ def from_grpc_status(status_code, message, **kwargs): """Create a :class:`GoogleAPICallError` from a :class:`grpc.StatusCode`. Args: - status_code (grpc.StatusCode): The gRPC status code. + status_code (Union[grpc.StatusCode, int]): The gRPC status code. message (str): The exception message. kwargs: Additional arguments passed to the :class:`GoogleAPICallError` constructor. @@ -441,6 +450,10 @@ def from_grpc_status(status_code, message, **kwargs): GoogleAPICallError: An instance of the appropriate subclass of :class:`GoogleAPICallError`. """ + + if isinstance(status_code, int): + status_code = _INT_TO_GRPC_CODE[status_code] + error_class = exception_class_for_grpc_status(status_code) error = error_class(message, **kwargs) diff --git a/google/api_core/operation.py b/google/api_core/operation.py index e6407b8c..55adbdd8 100644 --- a/google/api_core/operation.py +++ b/google/api_core/operation.py @@ -132,8 +132,9 @@ def _set_result_from_operation(self): ) self.set_result(response) elif self._operation.HasField("error"): - exception = exceptions.GoogleAPICallError( - self._operation.error.message, + exception = exceptions.from_grpc_status( + status_code=self._operation.error.code, + message=self._operation.error.message, errors=(self._operation.error,), response=self._operation, ) diff --git a/tests/unit/test_operation.py b/tests/unit/test_operation.py index 14b95cbb..7b0c7b1f 100644 --- a/tests/unit/test_operation.py +++ b/tests/unit/test_operation.py @@ -145,6 +145,22 @@ def test_exception(): assert expected_exception.message in "{!r}".format(exception) +def test_exception_with_error_code(): + expected_exception = status_pb2.Status(message="meep", code=5) + responses = [ + make_operation_proto(), + # Second operation response includes the error. + make_operation_proto(done=True, error=expected_exception), + ] + future, _, _ = make_operation_future(responses) + + exception = future.exception() + + assert expected_exception.message in "{!r}".format(exception) + # Status Code 5 maps to Not Found + # https://developers.google.com/maps-booking/reference/grpc-api/status_codes + assert isinstance(exception, exceptions.NotFound) + def test_unexpected_result(): responses = [ From 83758ccaf5099cadd9ff35333e7e528e1390696b Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 6 Oct 2020 16:19:02 +0000 Subject: [PATCH 2/6] chore: fix lint --- tests/unit/test_operation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/test_operation.py b/tests/unit/test_operation.py index 7b0c7b1f..829a3f3b 100644 --- a/tests/unit/test_operation.py +++ b/tests/unit/test_operation.py @@ -145,6 +145,7 @@ def test_exception(): assert expected_exception.message in "{!r}".format(exception) + def test_exception_with_error_code(): expected_exception = status_pb2.Status(message="meep", code=5) responses = [ From b26a95ca9ecf0a92894a4bf44ceb5f05b36e08cc Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 6 Oct 2020 16:28:59 +0000 Subject: [PATCH 3/6] test: add test for status code as int --- tests/unit/test_exceptions.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py index 040ac8ac..fb29015f 100644 --- a/tests/unit/test_exceptions.py +++ b/tests/unit/test_exceptions.py @@ -161,6 +161,17 @@ def test_from_grpc_status(): assert exception.errors == [] +def test_from_grpc_status_as_int(): + message = "message" + exception = exceptions.from_grpc_status(11, message) + assert isinstance(exception, exceptions.BadRequest) + assert isinstance(exception, exceptions.OutOfRange) + assert exception.code == http_client.BAD_REQUEST + assert exception.grpc_status_code == grpc.StatusCode.OUT_OF_RANGE + assert exception.message == message + assert exception.errors == [] + + def test_from_grpc_status_with_errors_and_response(): message = "message" response = mock.sentinel.response From 3f759789844fed8e1bb02a9cc1162376681a8ed6 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 6 Oct 2020 16:44:59 +0000 Subject: [PATCH 4/6] chore: add branch ignore for no grpc --- google/api_core/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/api_core/exceptions.py b/google/api_core/exceptions.py index 5a152cfc..30b8fdd9 100644 --- a/google/api_core/exceptions.py +++ b/google/api_core/exceptions.py @@ -38,7 +38,7 @@ # Additional lookup table to map integer status codes to grpc status code # grpc does not currently support initializing enums from ints # i.e., grpc.StatusCode(5) raises an error -if grpc is not None: +if grpc is not None: # pragma: no branch _INT_TO_GRPC_CODE = {} for x in grpc.StatusCode: _INT_TO_GRPC_CODE[x.value[0]] = x From 5c94ec356a2ba71542ea8267785da367b1f00af5 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Date: Tue, 6 Oct 2020 13:27:46 -0600 Subject: [PATCH 5/6] Update google/api_core/exceptions.py Co-authored-by: Tres Seaver --- google/api_core/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/api_core/exceptions.py b/google/api_core/exceptions.py index 30b8fdd9..991fe3ee 100644 --- a/google/api_core/exceptions.py +++ b/google/api_core/exceptions.py @@ -38,8 +38,8 @@ # Additional lookup table to map integer status codes to grpc status code # grpc does not currently support initializing enums from ints # i.e., grpc.StatusCode(5) raises an error +_INT_TO_GRPC_CODE = {} if grpc is not None: # pragma: no branch - _INT_TO_GRPC_CODE = {} for x in grpc.StatusCode: _INT_TO_GRPC_CODE[x.value[0]] = x From 31530928cc1342456b0703400d701591a9e8e78e Mon Sep 17 00:00:00 2001 From: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Date: Tue, 6 Oct 2020 13:30:25 -0600 Subject: [PATCH 6/6] Update google/api_core/exceptions.py Co-authored-by: Tres Seaver --- google/api_core/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/api_core/exceptions.py b/google/api_core/exceptions.py index 991fe3ee..a29266db 100644 --- a/google/api_core/exceptions.py +++ b/google/api_core/exceptions.py @@ -452,7 +452,7 @@ def from_grpc_status(status_code, message, **kwargs): """ if isinstance(status_code, int): - status_code = _INT_TO_GRPC_CODE[status_code] + status_code = _INT_TO_GRPC_CODE.get(status_code, status_code) error_class = exception_class_for_grpc_status(status_code) error = error_class(message, **kwargs)