Skip to content

Commit

Permalink
Add grpcio-status extension package
Browse files Browse the repository at this point in the history
* The new package has 2 API `from_call` and `to_status`
* Utilize the experimental API `abort_with_status`
* Add 5 unit test cases
  • Loading branch information
lidizheng committed Dec 13, 2018
1 parent e9cae6b commit 45b3230
Show file tree
Hide file tree
Showing 18 changed files with 475 additions and 1 deletion.
1 change: 1 addition & 0 deletions requirements.bazel.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ urllib3>=1.23
chardet==3.0.4
certifi==2017.4.17
idna==2.7
googleapis-common-protos==1.5.5
3 changes: 3 additions & 0 deletions src/python/grpcio_status/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build/
grpcio_status.egg-info/
dist/
3 changes: 3 additions & 0 deletions src/python/grpcio_status/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include grpc_version.py
recursive-include grpc_status *.py
global-exclude *.pyc
9 changes: 9 additions & 0 deletions src/python/grpcio_status/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
gRPC Python Status Proto
===========================

Reference package for GRPC Python status proto mapping.

Dependencies
------------

Depends on the `grpcio` package, available from PyPI via `pip install grpcio`.
14 changes: 14 additions & 0 deletions src/python/grpcio_status/grpc_status/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
load("@grpc_python_dependencies//:requirements.bzl", "requirement")

package(default_visibility = ["//visibility:public"])

py_library(
name = "grpc_status",
srcs = ["rpc_status.py",],
deps = [
"//src/python/grpcio/grpc:grpcio",
requirement('protobuf'),
requirement('googleapis-common-protos'),
],
imports=["../",],
)
13 changes: 13 additions & 0 deletions src/python/grpcio_status/grpc_status/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2018 The gRPC Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
88 changes: 88 additions & 0 deletions src/python/grpcio_status/grpc_status/rpc_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright 2018 The gRPC Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Reference implementation for status mapping in gRPC Python."""

import collections

import grpc

# TODO(https://github.com/bazelbuild/bazel/issues/6844)
# Due to Bazel issue, the namespace packages won't resolve correctly.
# Adding this unused-import as a workaround to avoid module-not-found error
# under Bazel builds.
import google.protobuf # pylint: disable=unused-import
from google.rpc import status_pb2

_CODE_TO_GRPC_CODE_MAPPING = dict([(x.value[0], x) for x in grpc.StatusCode])

_GRPC_DETAILS_METADATA_KEY = 'grpc-status-details-bin'


class _Status(
collections.namedtuple(
'_Status', ('code', 'details', 'trailing_metadata')), grpc.Status):
pass


def _code_to_grpc_status_code(code):
try:
return _CODE_TO_GRPC_CODE_MAPPING[code]
except KeyError:
raise ValueError('Invalid status code %s' % code)


def from_call(call):
"""Returns a google.rpc.status.Status message corresponding to a given grpc.Call.
Args:
call: A grpc.Call instance.
Returns:
A google.rpc.status.Status message representing the status of the RPC.
Raises:
ValueError: If the status code, status message is inconsistent with the rich status
inside of the google.rpc.status.Status.
"""
for key, value in call.trailing_metadata():
if key == _GRPC_DETAILS_METADATA_KEY:
rich_status = status_pb2.Status.FromString(value)
if call.code().value[0] != rich_status.code:
raise ValueError(
'Code in Status proto (%s) doesn\'t match status code (%s)'
% (_code_to_grpc_status_code(rich_status.code),
call.code()))
if call.details() != rich_status.message:
raise ValueError(
'Message in Status proto (%s) doesn\'t match status details (%s)'
% (rich_status.message, call.details()))
return rich_status
return None


def to_status(status):
"""Convert a google.rpc.status.Status message to grpc.Status.
Args:
status: a google.rpc.status.Status message representing the non-OK status
to terminate the RPC with and communicate it to the client.
Returns:
A grpc.Status instance.
"""
return _Status(
code=_code_to_grpc_status_code(status.code),
details=status.message,
trailing_metadata=((_GRPC_DETAILS_METADATA_KEY,
status.SerializeToString()),))
17 changes: 17 additions & 0 deletions src/python/grpcio_status/grpc_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2018 The gRPC Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_status/grpc_version.py.template`!!!

VERSION = '1.18.0.dev0'
86 changes: 86 additions & 0 deletions src/python/grpcio_status/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright 2018 The gRPC Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Setup module for the GRPC Python package's status mapping."""

import os

import setuptools

# Ensure we're in the proper directory whether or not we're being used by pip.
os.chdir(os.path.dirname(os.path.abspath(__file__)))

# Break import-style to ensure we can actually find our local modules.
import grpc_version


class _NoOpCommand(setuptools.Command):
"""No-op command."""

description = ''
user_options = []

def initialize_options(self):
pass

def finalize_options(self):
pass

def run(self):
pass


CLASSIFIERS = [
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'License :: OSI Approved :: Apache Software License',
]

PACKAGE_DIRECTORIES = {
'': '.',
}

INSTALL_REQUIRES = (
'protobuf>=3.6.0',
'grpcio>={version}'.format(version=grpc_version.VERSION),
'googleapis-common-protos>=1.5.5',
)

SETUP_REQUIRES = ()
COMMAND_CLASS = {
# wire up commands to no-op not to break the external dependencies
'preprocess': _NoOpCommand,
'build_package_protos': _NoOpCommand,
}

setuptools.setup(
name='grpcio-status',
version=grpc_version.VERSION,
description='Status proto mapping for gRPC',
author='The gRPC Authors',
author_email='grpc-io@googlegroups.com',
url='https://grpc.io',
license='Apache License 2.0',
classifiers=CLASSIFIERS,
package_dir=PACKAGE_DIRECTORIES,
packages=setuptools.find_packages('.'),
install_requires=INSTALL_REQUIRES,
setup_requires=SETUP_REQUIRES,
cmdclass=COMMAND_CLASS)
1 change: 1 addition & 0 deletions src/python/grpcio_tests/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
'coverage>=4.0', 'enum34>=1.0.4',
'grpcio>={version}'.format(version=grpc_version.VERSION),
'grpcio-channelz>={version}'.format(version=grpc_version.VERSION),
'grpcio-status>={version}'.format(version=grpc_version.VERSION),
'grpcio-tools>={version}'.format(version=grpc_version.VERSION),
'grpcio-health-checking>={version}'.format(version=grpc_version.VERSION),
'oauth2client>=1.4.7', 'protobuf>=3.6.0', 'six>=1.10', 'google-auth>=1.0.0',
Expand Down
19 changes: 19 additions & 0 deletions src/python/grpcio_tests/tests/status/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
load("@grpc_python_dependencies//:requirements.bzl", "requirement")

package(default_visibility = ["//visibility:public"])

py_test(
name = "grpc_status_test",
srcs = ["_grpc_status_test.py"],
main = "_grpc_status_test.py",
size = "small",
deps = [
"//src/python/grpcio/grpc:grpcio",
"//src/python/grpcio_status/grpc_status:grpc_status",
"//src/python/grpcio_tests/tests/unit:test_common",
"//src/python/grpcio_tests/tests/unit/framework/common:common",
requirement('protobuf'),
requirement('googleapis-common-protos'),
],
imports = ["../../",],
)
13 changes: 13 additions & 0 deletions src/python/grpcio_tests/tests/status/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2018 The gRPC Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Loading

0 comments on commit 45b3230

Please sign in to comment.