Skip to content

Commit 45b3230

Browse files
committed
Add grpcio-status extension package
* The new package has 2 API `from_call` and `to_status` * Utilize the experimental API `abort_with_status` * Add 5 unit test cases
1 parent e9cae6b commit 45b3230

File tree

18 files changed

+475
-1
lines changed

18 files changed

+475
-1
lines changed

requirements.bazel.txt

+1
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ urllib3>=1.23
1313
chardet==3.0.4
1414
certifi==2017.4.17
1515
idna==2.7
16+
googleapis-common-protos==1.5.5

src/python/grpcio_status/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
build/
2+
grpcio_status.egg-info/
3+
dist/

src/python/grpcio_status/MANIFEST.in

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
include grpc_version.py
2+
recursive-include grpc_status *.py
3+
global-exclude *.pyc

src/python/grpcio_status/README.rst

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
gRPC Python Status Proto
2+
===========================
3+
4+
Reference package for GRPC Python status proto mapping.
5+
6+
Dependencies
7+
------------
8+
9+
Depends on the `grpcio` package, available from PyPI via `pip install grpcio`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
load("@grpc_python_dependencies//:requirements.bzl", "requirement")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
py_library(
6+
name = "grpc_status",
7+
srcs = ["rpc_status.py",],
8+
deps = [
9+
"//src/python/grpcio/grpc:grpcio",
10+
requirement('protobuf'),
11+
requirement('googleapis-common-protos'),
12+
],
13+
imports=["../",],
14+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2018 The gRPC Authors
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Copyright 2018 The gRPC Authors
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+
"""Reference implementation for status mapping in gRPC Python."""
15+
16+
import collections
17+
18+
import grpc
19+
20+
# TODO(https://github.com/bazelbuild/bazel/issues/6844)
21+
# Due to Bazel issue, the namespace packages won't resolve correctly.
22+
# Adding this unused-import as a workaround to avoid module-not-found error
23+
# under Bazel builds.
24+
import google.protobuf # pylint: disable=unused-import
25+
from google.rpc import status_pb2
26+
27+
_CODE_TO_GRPC_CODE_MAPPING = dict([(x.value[0], x) for x in grpc.StatusCode])
28+
29+
_GRPC_DETAILS_METADATA_KEY = 'grpc-status-details-bin'
30+
31+
32+
class _Status(
33+
collections.namedtuple(
34+
'_Status', ('code', 'details', 'trailing_metadata')), grpc.Status):
35+
pass
36+
37+
38+
def _code_to_grpc_status_code(code):
39+
try:
40+
return _CODE_TO_GRPC_CODE_MAPPING[code]
41+
except KeyError:
42+
raise ValueError('Invalid status code %s' % code)
43+
44+
45+
def from_call(call):
46+
"""Returns a google.rpc.status.Status message corresponding to a given grpc.Call.
47+
48+
Args:
49+
call: A grpc.Call instance.
50+
51+
Returns:
52+
A google.rpc.status.Status message representing the status of the RPC.
53+
54+
Raises:
55+
ValueError: If the status code, status message is inconsistent with the rich status
56+
inside of the google.rpc.status.Status.
57+
"""
58+
for key, value in call.trailing_metadata():
59+
if key == _GRPC_DETAILS_METADATA_KEY:
60+
rich_status = status_pb2.Status.FromString(value)
61+
if call.code().value[0] != rich_status.code:
62+
raise ValueError(
63+
'Code in Status proto (%s) doesn\'t match status code (%s)'
64+
% (_code_to_grpc_status_code(rich_status.code),
65+
call.code()))
66+
if call.details() != rich_status.message:
67+
raise ValueError(
68+
'Message in Status proto (%s) doesn\'t match status details (%s)'
69+
% (rich_status.message, call.details()))
70+
return rich_status
71+
return None
72+
73+
74+
def to_status(status):
75+
"""Convert a google.rpc.status.Status message to grpc.Status.
76+
77+
Args:
78+
status: a google.rpc.status.Status message representing the non-OK status
79+
to terminate the RPC with and communicate it to the client.
80+
81+
Returns:
82+
A grpc.Status instance.
83+
"""
84+
return _Status(
85+
code=_code_to_grpc_status_code(status.code),
86+
details=status.message,
87+
trailing_metadata=((_GRPC_DETAILS_METADATA_KEY,
88+
status.SerializeToString()),))
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2018 The gRPC Authors
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+
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_status/grpc_version.py.template`!!!
16+
17+
VERSION = '1.18.0.dev0'

src/python/grpcio_status/setup.py

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Copyright 2018 The gRPC Authors
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+
"""Setup module for the GRPC Python package's status mapping."""
15+
16+
import os
17+
18+
import setuptools
19+
20+
# Ensure we're in the proper directory whether or not we're being used by pip.
21+
os.chdir(os.path.dirname(os.path.abspath(__file__)))
22+
23+
# Break import-style to ensure we can actually find our local modules.
24+
import grpc_version
25+
26+
27+
class _NoOpCommand(setuptools.Command):
28+
"""No-op command."""
29+
30+
description = ''
31+
user_options = []
32+
33+
def initialize_options(self):
34+
pass
35+
36+
def finalize_options(self):
37+
pass
38+
39+
def run(self):
40+
pass
41+
42+
43+
CLASSIFIERS = [
44+
'Development Status :: 5 - Production/Stable',
45+
'Programming Language :: Python',
46+
'Programming Language :: Python :: 2',
47+
'Programming Language :: Python :: 2.7',
48+
'Programming Language :: Python :: 3',
49+
'Programming Language :: Python :: 3.4',
50+
'Programming Language :: Python :: 3.5',
51+
'Programming Language :: Python :: 3.6',
52+
'Programming Language :: Python :: 3.7',
53+
'License :: OSI Approved :: Apache Software License',
54+
]
55+
56+
PACKAGE_DIRECTORIES = {
57+
'': '.',
58+
}
59+
60+
INSTALL_REQUIRES = (
61+
'protobuf>=3.6.0',
62+
'grpcio>={version}'.format(version=grpc_version.VERSION),
63+
'googleapis-common-protos>=1.5.5',
64+
)
65+
66+
SETUP_REQUIRES = ()
67+
COMMAND_CLASS = {
68+
# wire up commands to no-op not to break the external dependencies
69+
'preprocess': _NoOpCommand,
70+
'build_package_protos': _NoOpCommand,
71+
}
72+
73+
setuptools.setup(
74+
name='grpcio-status',
75+
version=grpc_version.VERSION,
76+
description='Status proto mapping for gRPC',
77+
author='The gRPC Authors',
78+
author_email='[email protected]',
79+
url='https://grpc.io',
80+
license='Apache License 2.0',
81+
classifiers=CLASSIFIERS,
82+
package_dir=PACKAGE_DIRECTORIES,
83+
packages=setuptools.find_packages('.'),
84+
install_requires=INSTALL_REQUIRES,
85+
setup_requires=SETUP_REQUIRES,
86+
cmdclass=COMMAND_CLASS)

src/python/grpcio_tests/setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
'coverage>=4.0', 'enum34>=1.0.4',
4141
'grpcio>={version}'.format(version=grpc_version.VERSION),
4242
'grpcio-channelz>={version}'.format(version=grpc_version.VERSION),
43+
'grpcio-status>={version}'.format(version=grpc_version.VERSION),
4344
'grpcio-tools>={version}'.format(version=grpc_version.VERSION),
4445
'grpcio-health-checking>={version}'.format(version=grpc_version.VERSION),
4546
'oauth2client>=1.4.7', 'protobuf>=3.6.0', 'six>=1.10', 'google-auth>=1.0.0',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
load("@grpc_python_dependencies//:requirements.bzl", "requirement")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
py_test(
6+
name = "grpc_status_test",
7+
srcs = ["_grpc_status_test.py"],
8+
main = "_grpc_status_test.py",
9+
size = "small",
10+
deps = [
11+
"//src/python/grpcio/grpc:grpcio",
12+
"//src/python/grpcio_status/grpc_status:grpc_status",
13+
"//src/python/grpcio_tests/tests/unit:test_common",
14+
"//src/python/grpcio_tests/tests/unit/framework/common:common",
15+
requirement('protobuf'),
16+
requirement('googleapis-common-protos'),
17+
],
18+
imports = ["../../",],
19+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2018 The gRPC Authors
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.

0 commit comments

Comments
 (0)