Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move '_catch_remap_gax_error' to 'core.exceptions'. #3444

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion bigtable/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@

REQUIREMENTS = [
'google-cloud-core >= 0.24.0, < 0.25dev',
'google-gax>=0.15.7, <0.16dev',
]

setup(
Expand Down
55 changes: 50 additions & 5 deletions core/google/cloud/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,22 @@
# Avoid the grpc and google.cloud.grpc collision.
from __future__ import absolute_import

import contextlib
import copy
import json
import sys

import six

from google.gax.errors import GaxError
from google.gax.grpc import exc_to_code
from grpc import StatusCode
from grpc._channel import _Rendezvous

from google.cloud._helpers import _to_bytes

_HTTP_CODE_TO_EXCEPTION = {} # populated at end of module

try:
from grpc._channel import _Rendezvous
except ImportError: # pragma: NO COVER
_Rendezvous = None


# pylint: disable=invalid-name
GrpcRendezvous = _Rendezvous
Expand Down Expand Up @@ -250,3 +253,45 @@ def _walk_subclasses(klass):
code = getattr(_eklass, 'code', None)
if code is not None:
_HTTP_CODE_TO_EXCEPTION[code] = _eklass


_GRPC_ERROR_MAPPING = {

This comment was marked as spam.

StatusCode.UNKNOWN: InternalServerError,
StatusCode.INVALID_ARGUMENT: BadRequest,
StatusCode.DEADLINE_EXCEEDED: GatewayTimeout,
StatusCode.NOT_FOUND: NotFound,
StatusCode.ALREADY_EXISTS: Conflict,
StatusCode.PERMISSION_DENIED: Forbidden,
StatusCode.UNAUTHENTICATED: Unauthorized,
StatusCode.RESOURCE_EXHAUSTED: TooManyRequests,
StatusCode.FAILED_PRECONDITION: PreconditionFailed,
StatusCode.ABORTED: Conflict,
StatusCode.OUT_OF_RANGE: BadRequest,
StatusCode.UNIMPLEMENTED: MethodNotImplemented,
StatusCode.INTERNAL: InternalServerError,
StatusCode.UNAVAILABLE: ServiceUnavailable,
StatusCode.DATA_LOSS: InternalServerError,
}


@contextlib.contextmanager
def _catch_remap_gax_error():

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

"""Remap GAX exceptions that happen in context.

.. _code.proto: https://github.com/googleapis/googleapis/blob/\
master/google/rpc/code.proto

Remaps gRPC exceptions to the classes defined in
:mod:`~google.cloud.exceptions` (according to the description
in `code.proto`_).
"""
try:
yield
except GaxError as exc:
error_code = exc_to_code(exc.cause)
error_class = _GRPC_ERROR_MAPPING.get(error_code)
if error_class is None:
raise
else:
new_exc = error_class(exc.cause.details())
six.reraise(error_class, new_exc, sys.exc_info()[2])
1 change: 1 addition & 0 deletions core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
'protobuf >= 3.0.0',
'google-auth >= 0.4.0, < 2.0.0dev',
'google-auth-httplib2',
'google-gax>=0.15.7, <0.16dev',

This comment was marked as spam.

This comment was marked as spam.

'six',
]

Expand Down
62 changes: 62 additions & 0 deletions core/tests/unit/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,68 @@ def test_without_use_json(self):
self.assertEqual(list(exception.errors), [])


class Test__catch_remap_gax_error(unittest.TestCase):

def _call_fut(self):
from google.cloud.exceptions import _catch_remap_gax_error

return _catch_remap_gax_error()

@staticmethod
def _fake_method(exc, result=None):
if exc is None:
return result
else:
raise exc

@staticmethod
def _make_rendezvous(status_code, details):
from grpc._channel import _RPCState
from google.cloud.exceptions import GrpcRendezvous

exc_state = _RPCState((), None, None, status_code, details)
return GrpcRendezvous(exc_state, None, None, None)

def test_success(self):
expected = object()
with self._call_fut():
result = self._fake_method(None, expected)
self.assertIs(result, expected)

def test_non_grpc_err(self):
exc = RuntimeError('Not a gRPC error')
with self.assertRaises(RuntimeError):
with self._call_fut():
self._fake_method(exc)

def test_gax_error(self):
from google.gax.errors import GaxError
from grpc import StatusCode
from google.cloud.exceptions import Forbidden

# First, create low-level GrpcRendezvous exception.
details = 'Some error details.'
cause = self._make_rendezvous(StatusCode.PERMISSION_DENIED, details)
# Then put it into a high-level GaxError.
msg = 'GAX Error content.'
exc = GaxError(msg, cause=cause)

with self.assertRaises(Forbidden):
with self._call_fut():
self._fake_method(exc)

def test_gax_error_not_mapped(self):
from google.gax.errors import GaxError
from grpc import StatusCode

cause = self._make_rendezvous(StatusCode.CANCELLED, None)
exc = GaxError(None, cause=cause)

with self.assertRaises(GaxError):
with self._call_fut():
self._fake_method(exc)


class _Response(object):
def __init__(self, status):
self.status = status
49 changes: 1 addition & 48 deletions datastore/google/cloud/datastore/_gax.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,14 @@

"""Helpers for making API requests via GAX / gRPC."""


import contextlib
import sys

from google.cloud.gapic.datastore.v1 import datastore_client
from google.gax.errors import GaxError
from google.gax.grpc import exc_to_code
from google.gax.utils import metrics
from grpc import insecure_channel
from grpc import StatusCode
import six

from google.cloud._helpers import make_secure_channel
from google.cloud._http import DEFAULT_USER_AGENT
from google.cloud import exceptions
from google.cloud.exceptions import _catch_remap_gax_error

from google.cloud.datastore import __version__

Expand All @@ -40,46 +33,6 @@
_GRPC_EXTRA_OPTIONS = (
('x-goog-api-client', _HEADER_STR),
)
_GRPC_ERROR_MAPPING = {
StatusCode.UNKNOWN: exceptions.InternalServerError,
StatusCode.INVALID_ARGUMENT: exceptions.BadRequest,
StatusCode.DEADLINE_EXCEEDED: exceptions.GatewayTimeout,
StatusCode.NOT_FOUND: exceptions.NotFound,
StatusCode.ALREADY_EXISTS: exceptions.Conflict,
StatusCode.PERMISSION_DENIED: exceptions.Forbidden,
StatusCode.UNAUTHENTICATED: exceptions.Unauthorized,
StatusCode.RESOURCE_EXHAUSTED: exceptions.TooManyRequests,
StatusCode.FAILED_PRECONDITION: exceptions.PreconditionFailed,
StatusCode.ABORTED: exceptions.Conflict,
StatusCode.OUT_OF_RANGE: exceptions.BadRequest,
StatusCode.UNIMPLEMENTED: exceptions.MethodNotImplemented,
StatusCode.INTERNAL: exceptions.InternalServerError,
StatusCode.UNAVAILABLE: exceptions.ServiceUnavailable,
StatusCode.DATA_LOSS: exceptions.InternalServerError,
}


@contextlib.contextmanager
def _catch_remap_gax_error():
"""Remap GAX exceptions that happen in context.

.. _code.proto: https://github.com/googleapis/googleapis/blob/\
master/google/rpc/code.proto

Remaps gRPC exceptions to the classes defined in
:mod:`~google.cloud.exceptions` (according to the description
in `code.proto`_).
"""
try:
yield
except GaxError as exc:
error_code = exc_to_code(exc.cause)
error_class = _GRPC_ERROR_MAPPING.get(error_code)
if error_class is None:
raise
else:
new_exc = error_class(exc.cause.details())
six.reraise(error_class, new_exc, sys.exc_info()[2])


class GAPICDatastoreAPI(datastore_client.DatastoreClient):
Expand Down
1 change: 0 additions & 1 deletion datastore/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@

REQUIREMENTS = [
'google-cloud-core >= 0.24.0, < 0.25dev',
'google-gax>=0.15.7, <0.16dev',
'gapic-google-cloud-datastore-v1 >= 0.15.0, < 0.16dev',
]

Expand Down
63 changes: 0 additions & 63 deletions datastore/tests/unit/test__gax.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,69 +19,6 @@
from google.cloud.datastore.client import _HAVE_GRPC


@unittest.skipUnless(_HAVE_GRPC, 'No gRPC')
class Test__catch_remap_gax_error(unittest.TestCase):

def _call_fut(self):
from google.cloud.datastore._gax import _catch_remap_gax_error

return _catch_remap_gax_error()

@staticmethod
def _fake_method(exc, result=None):
if exc is None:
return result
else:
raise exc

@staticmethod
def _make_rendezvous(status_code, details):
from grpc._channel import _RPCState
from google.cloud.exceptions import GrpcRendezvous

exc_state = _RPCState((), None, None, status_code, details)
return GrpcRendezvous(exc_state, None, None, None)

def test_success(self):
expected = object()
with self._call_fut():
result = self._fake_method(None, expected)
self.assertIs(result, expected)

def test_non_grpc_err(self):
exc = RuntimeError('Not a gRPC error')
with self.assertRaises(RuntimeError):
with self._call_fut():
self._fake_method(exc)

def test_gax_error(self):
from google.gax.errors import GaxError
from grpc import StatusCode
from google.cloud.exceptions import Forbidden

# First, create low-level GrpcRendezvous exception.
details = 'Some error details.'
cause = self._make_rendezvous(StatusCode.PERMISSION_DENIED, details)
# Then put it into a high-level GaxError.
msg = 'GAX Error content.'
exc = GaxError(msg, cause=cause)

with self.assertRaises(Forbidden):
with self._call_fut():
self._fake_method(exc)

def test_gax_error_not_mapped(self):
from google.gax.errors import GaxError
from grpc import StatusCode

cause = self._make_rendezvous(StatusCode.CANCELLED, None)
exc = GaxError(None, cause=cause)

with self.assertRaises(GaxError):
with self._call_fut():
self._fake_method(exc)


@unittest.skipUnless(_HAVE_GRPC, 'No gRPC')
class TestGAPICDatastoreAPI(unittest.TestCase):

Expand Down
1 change: 0 additions & 1 deletion videointelligence/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
packages=find_packages(exclude=('tests*',)),
install_requires=(
'googleapis-common-protos >= 1.5.2, < 2.0dev',
'google-gax >= 0.15.12, < 0.16dev',
'six >= 1.10.0',
),
url='https://github.com/GoogleCloudPlatform/google-cloud-python',
Expand Down