diff options
author | Masood Malekghassemi <atash@google.com> | 2016-10-28 13:30:16 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-28 13:30:16 -0700 |
commit | a12e6d8037d90269d91bd8d557ac66da4201b629 (patch) | |
tree | 10aaf4d4c2aa5552bbdee4810a2e92652c1dc771 /src/python | |
parent | eb44fd68fbbe47527046576f8850e4c7244c4562 (diff) | |
parent | aff6936cf7500da3ebf7c2282160405fdf8ec36b (diff) |
Merge pull request #8367 from soltanmm-google/blurb
Partially implement gRPC Python server reflection
Diffstat (limited to 'src/python')
-rw-r--r-- | src/python/.gitignore | 2 | ||||
-rw-r--r-- | src/python/grpcio_reflection/.gitignore | 5 | ||||
-rw-r--r-- | src/python/grpcio_reflection/grpc/__init__.py | 30 | ||||
-rw-r--r-- | src/python/grpcio_reflection/grpc/reflection/__init__.py | 29 | ||||
-rw-r--r-- | src/python/grpcio_reflection/grpc/reflection/v1alpha/__init__.py | 29 | ||||
-rw-r--r-- | src/python/grpcio_reflection/grpc/reflection/v1alpha/reflection.py | 143 | ||||
-rw-r--r-- | src/python/grpcio_reflection/grpc_version.py | 32 | ||||
-rw-r--r-- | src/python/grpcio_reflection/reflection_commands.py | 78 | ||||
-rw-r--r-- | src/python/grpcio_reflection/setup.py | 73 | ||||
-rw-r--r-- | src/python/grpcio_tests/tests/reflection/__init__.py | 28 | ||||
-rw-r--r-- | src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py | 185 | ||||
-rw-r--r-- | src/python/grpcio_tests/tests/tests.json | 37 |
12 files changed, 653 insertions, 18 deletions
diff --git a/src/python/.gitignore b/src/python/.gitignore index f158efa4bf..7b520579a0 100644 --- a/src/python/.gitignore +++ b/src/python/.gitignore @@ -1 +1,3 @@ gens/ +*_pb2.py +*_pb2_grpc.py diff --git a/src/python/grpcio_reflection/.gitignore b/src/python/grpcio_reflection/.gitignore new file mode 100644 index 0000000000..c0befdc8ea --- /dev/null +++ b/src/python/grpcio_reflection/.gitignore @@ -0,0 +1,5 @@ +*.proto +*_pb2.py +build/ +grpcio_reflection.egg-info/ +dist/ diff --git a/src/python/grpcio_reflection/grpc/__init__.py b/src/python/grpcio_reflection/grpc/__init__.py new file mode 100644 index 0000000000..70ac5edd48 --- /dev/null +++ b/src/python/grpcio_reflection/grpc/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2016, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +__import__('pkg_resources').declare_namespace(__name__) diff --git a/src/python/grpcio_reflection/grpc/reflection/__init__.py b/src/python/grpcio_reflection/grpc/reflection/__init__.py new file mode 100644 index 0000000000..d5ad73a74a --- /dev/null +++ b/src/python/grpcio_reflection/grpc/reflection/__init__.py @@ -0,0 +1,29 @@ +# Copyright 2016, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/src/python/grpcio_reflection/grpc/reflection/v1alpha/__init__.py b/src/python/grpcio_reflection/grpc/reflection/v1alpha/__init__.py new file mode 100644 index 0000000000..d5ad73a74a --- /dev/null +++ b/src/python/grpcio_reflection/grpc/reflection/v1alpha/__init__.py @@ -0,0 +1,29 @@ +# Copyright 2016, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/src/python/grpcio_reflection/grpc/reflection/v1alpha/reflection.py b/src/python/grpcio_reflection/grpc/reflection/v1alpha/reflection.py new file mode 100644 index 0000000000..3c399b0d79 --- /dev/null +++ b/src/python/grpcio_reflection/grpc/reflection/v1alpha/reflection.py @@ -0,0 +1,143 @@ +# Copyright 2016, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Reference implementation for reflection in gRPC Python.""" + +import threading + +import grpc +from google.protobuf import descriptor_pb2 +from google.protobuf import descriptor_pool + +from grpc.reflection.v1alpha import reflection_pb2 + +_POOL = descriptor_pool.Default() + +def _not_found_error(): + return reflection_pb2.ServerReflectionResponse( + error_response=reflection_pb2.ErrorResponse( + error_code=grpc.StatusCode.NOT_FOUND.value[0], + error_message=grpc.StatusCode.NOT_FOUND.value[1].encode(), + ) + ) + +def _file_descriptor_response(descriptor): + proto = descriptor_pb2.FileDescriptorProto() + descriptor.CopyToProto(proto) + serialized_proto = proto.SerializeToString() + return reflection_pb2.ServerReflectionResponse( + file_descriptor_response=reflection_pb2.FileDescriptorResponse( + file_descriptor_proto=(serialized_proto,) + ), + ) + + +class ReflectionServicer(reflection_pb2.ServerReflectionServicer): + """Servicer handling RPCs for service statuses.""" + + def __init__(self, service_names, pool=None): + """Constructor. + + Args: + service_names: Iterable of fully-qualified service names available. + """ + self._service_names = list(service_names) + self._pool = _POOL if pool is None else pool + + def _file_by_filename(self, filename): + try: + descriptor = self._pool.FindFileByName(filename) + except KeyError: + return _not_found_error() + else: + return _file_descriptor_response(descriptor) + + def _file_containing_symbol(self, fully_qualified_name): + try: + descriptor = self._pool.FindFileContainingSymbol(fully_qualified_name) + except KeyError: + return _not_found_error() + else: + return _file_descriptor_response(descriptor) + + def _file_containing_extension(containing_type, extension_number): + # TODO(atash) Python protobuf currently doesn't support querying extensions. + # https://github.com/google/protobuf/issues/2248 + return reflection_pb2.ServerReflectionResponse( + error_response=reflection_pb2.ErrorResponse( + error_code=grpc.StatusCode.UNIMPLEMENTED.value[0], + error_message=grpc.StatusCode.UNIMPLMENTED.value[1].encode(), + ) + ) + + def _extension_numbers_of_type(fully_qualified_name): + # TODO(atash) We're allowed to leave this unsupported according to the + # protocol, but we should still eventually implement it. Hits the same issue + # as `_file_containing_extension`, however. + # https://github.com/google/protobuf/issues/2248 + return reflection_pb2.ServerReflectionResponse( + error_response=reflection_pb2.ErrorResponse( + error_code=grpc.StatusCode.UNIMPLEMENTED.value[0], + error_message=grpc.StatusCode.UNIMPLMENTED.value[1].encode(), + ) + ) + + def _list_services(self): + return reflection_pb2.ServerReflectionResponse( + list_services_response=reflection_pb2.ListServiceResponse( + service=[ + reflection_pb2.ServiceResponse(name=service_name) + for service_name in self._service_names + ] + ) + ) + + def ServerReflectionInfo(self, request_iterator, context): + for request in request_iterator: + if request.HasField('file_by_filename'): + yield self._file_by_filename(request.file_by_filename) + elif request.HasField('file_containing_symbol'): + yield self._file_containing_symbol(request.file_containing_symbol) + elif request.HasField('file_containing_extension'): + yield self._file_containing_extension( + request.file_containing_extension.containing_type, + request.file_containing_extension.extension_number) + elif request.HasField('all_extension_numbers_of_type'): + yield _all_extension_numbers_of_type( + request.all_extension_numbers_of_type) + elif request.HasField('list_services'): + yield self._list_services() + else: + yield reflection_pb2.ServerReflectionResponse( + error_response=reflection_pb2.ErrorResponse( + error_code=grpc.StatusCode.INVALID_ARGUMENT.value[0], + error_message=grpc.StatusCode.INVALID_ARGUMENT.value[1].encode(), + ) + ) + diff --git a/src/python/grpcio_reflection/grpc_version.py b/src/python/grpcio_reflection/grpc_version.py new file mode 100644 index 0000000000..9b3c44c022 --- /dev/null +++ b/src/python/grpcio_reflection/grpc_version.py @@ -0,0 +1,32 @@ +# Copyright 2016, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_reflection/grpc_version.py.template`!!! + +VERSION='1.1.0.dev0' diff --git a/src/python/grpcio_reflection/reflection_commands.py b/src/python/grpcio_reflection/reflection_commands.py new file mode 100644 index 0000000000..d189aee577 --- /dev/null +++ b/src/python/grpcio_reflection/reflection_commands.py @@ -0,0 +1,78 @@ +# Copyright 2016, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Provides distutils command classes for the GRPC Python setup process.""" + +import os +import shutil + +import setuptools + +ROOT_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__))) +HEALTH_PROTO = os.path.join(ROOT_DIR, '../../proto/grpc/reflection/v1alpha/reflection.proto') + + +class CopyProtoModules(setuptools.Command): + """Command to copy proto modules from grpc/src/proto.""" + + description = '' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + if os.path.isfile(HEALTH_PROTO): + shutil.copyfile( + HEALTH_PROTO, + os.path.join(ROOT_DIR, 'grpc/reflection/v1alpha/reflection.proto')) + + +class BuildPackageProtos(setuptools.Command): + """Command to generate project *_pb2.py modules from proto files.""" + + description = 'build grpc protobuf modules' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + # due to limitations of the proto generator, we require that only *one* + # directory is provided as an 'include' directory. We assume it's the '' key + # to `self.distribution.package_dir` (and get a key error if it's not + # there). + from grpc.tools import command + command.build_package_protos(self.distribution.package_dir['']) diff --git a/src/python/grpcio_reflection/setup.py b/src/python/grpcio_reflection/setup.py new file mode 100644 index 0000000000..df95af4de1 --- /dev/null +++ b/src/python/grpcio_reflection/setup.py @@ -0,0 +1,73 @@ +# Copyright 2016, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Setup module for the GRPC Python package's optional reflection.""" + +import os +import sys + +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 commands module. +import reflection_commands +import grpc_version + +PACKAGE_DIRECTORIES = { + '': '.', +} + +SETUP_REQUIRES = ( + 'grpcio-tools>={version}'.format(version=grpc_version.VERSION), +) + +INSTALL_REQUIRES = ( + 'protobuf>=3.0.0', + 'grpcio>={version}'.format(version=grpc_version.VERSION), +) + +COMMAND_CLASS = { + # Run preprocess from the repository *before* doing any packaging! + 'preprocess': reflection_commands.CopyProtoModules, + 'build_package_protos': reflection_commands.BuildPackageProtos, +} + +setuptools.setup( + name='grpcio-reflection', + version=grpc_version.VERSION, + license='3-clause BSD', + package_dir=PACKAGE_DIRECTORIES, + packages=setuptools.find_packages('.'), + namespace_packages=['grpc'], + install_requires=INSTALL_REQUIRES, + setup_requires=SETUP_REQUIRES, + cmdclass=COMMAND_CLASS +) diff --git a/src/python/grpcio_tests/tests/reflection/__init__.py b/src/python/grpcio_tests/tests/reflection/__init__.py new file mode 100644 index 0000000000..100a624dc9 --- /dev/null +++ b/src/python/grpcio_tests/tests/reflection/__init__.py @@ -0,0 +1,28 @@ +# Copyright 2016, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py b/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py new file mode 100644 index 0000000000..87264cf9ba --- /dev/null +++ b/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py @@ -0,0 +1,185 @@ +# Copyright 2016, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Tests of grpc.reflection.v1alpha.reflection.""" + +import unittest + +import grpc +from grpc.framework.foundation import logging_pool +from grpc.reflection.v1alpha import reflection +from grpc.reflection.v1alpha import reflection_pb2 + +from google.protobuf import descriptor_pool +from google.protobuf import descriptor_pb2 + +from src.proto.grpc.testing.proto2 import empty2_extensions_pb2 +from src.proto.grpc.testing import empty_pb2 +from tests.unit.framework.common import test_constants + +_EMPTY_PROTO_FILE_NAME = 'src/proto/grpc/testing/empty.proto' +_EMPTY_PROTO_SYMBOL_NAME = 'grpc.testing.Empty' +_SERVICE_NAMES = ( + 'Angstrom', 'Bohr', 'Curie', 'Dyson', 'Einstein', 'Feynman', 'Galilei') + +def _file_descriptor_to_proto(descriptor): + proto = descriptor_pb2.FileDescriptorProto() + descriptor.CopyToProto(proto) + return proto.SerializeToString() + +class ReflectionServicerTest(unittest.TestCase): + + def setUp(self): + servicer = reflection.ReflectionServicer(service_names=_SERVICE_NAMES) + server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY) + self._server = grpc.server(server_pool) + port = self._server.add_insecure_port('[::]:0') + reflection_pb2.add_ServerReflectionServicer_to_server(servicer, self._server) + self._server.start() + + channel = grpc.insecure_channel('localhost:%d' % port) + self._stub = reflection_pb2.ServerReflectionStub(channel) + + def testFileByName(self): + requests = ( + reflection_pb2.ServerReflectionRequest( + file_by_filename=_EMPTY_PROTO_FILE_NAME + ), + reflection_pb2.ServerReflectionRequest( + file_by_filename='i-donut-exist' + ), + ) + responses = tuple(self._stub.ServerReflectionInfo(requests)) + expected_responses = ( + reflection_pb2.ServerReflectionResponse( + valid_host='', + file_descriptor_response=reflection_pb2.FileDescriptorResponse( + file_descriptor_proto=( + _file_descriptor_to_proto(empty_pb2.DESCRIPTOR), + ) + ) + ), + reflection_pb2.ServerReflectionResponse( + valid_host='', + error_response=reflection_pb2.ErrorResponse( + error_code=grpc.StatusCode.NOT_FOUND.value[0], + error_message=grpc.StatusCode.NOT_FOUND.value[1].encode(), + ) + ), + ) + self.assertEqual(expected_responses, responses) + + def testFileBySymbol(self): + requests = ( + reflection_pb2.ServerReflectionRequest( + file_containing_symbol=_EMPTY_PROTO_SYMBOL_NAME + ), + reflection_pb2.ServerReflectionRequest( + file_containing_symbol='i.donut.exist.co.uk.org.net.me.name.foo' + ), + ) + responses = tuple(self._stub.ServerReflectionInfo(requests)) + expected_responses = ( + reflection_pb2.ServerReflectionResponse( + valid_host='', + file_descriptor_response=reflection_pb2.FileDescriptorResponse( + file_descriptor_proto=( + _file_descriptor_to_proto(empty_pb2.DESCRIPTOR), + ) + ) + ), + reflection_pb2.ServerReflectionResponse( + valid_host='', + error_response=reflection_pb2.ErrorResponse( + error_code=grpc.StatusCode.NOT_FOUND.value[0], + error_message=grpc.StatusCode.NOT_FOUND.value[1].encode(), + ) + ), + ) + self.assertEqual(expected_responses, responses) + + @unittest.skip('TODO(atash): implement file-containing-extension reflection ' + '(see https://github.com/google/protobuf/issues/2248)') + def testFileContainingExtension(self): + requests = ( + reflection_pb2.ServerReflectionRequest( + file_containing_extension=reflection_pb2.ExtensionRequest( + containing_type='grpc.testing.proto2.Empty', + extension_number=125, + ), + ), + reflection_pb2.ServerReflectionRequest( + file_containing_extension=reflection_pb2.ExtensionRequest( + containing_type='i.donut.exist.co.uk.org.net.me.name.foo', + extension_number=55, + ), + ), + ) + responses = tuple(self._stub.ServerReflectionInfo(requests)) + expected_responses = ( + reflection_pb2.ServerReflectionResponse( + valid_host='', + file_descriptor_response=reflection_pb2.FileDescriptorResponse( + file_descriptor_proto=( + _file_descriptor_to_proto(empty_extensions_pb2.DESCRIPTOR), + ) + ) + ), + reflection_pb2.ServerReflectionResponse( + valid_host='', + error_response=reflection_pb2.ErrorResponse( + error_code=grpc.StatusCode.NOT_FOUND.value[0], + error_message=grpc.StatusCode.NOT_FOUND.value[1].encode(), + ) + ), + ) + self.assertEqual(expected_responses, responses) + + def testListServices(self): + requests = ( + reflection_pb2.ServerReflectionRequest( + list_services='', + ), + ) + responses = tuple(self._stub.ServerReflectionInfo(requests)) + expected_responses = ( + reflection_pb2.ServerReflectionResponse( + valid_host='', + list_services_response=reflection_pb2.ListServiceResponse( + service=tuple( + reflection_pb2.ServiceResponse(name=name) + for name in _SERVICE_NAMES + ) + ) + ), + ) + self.assertEqual(expected_responses, responses) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json index 2071a33e13..2e85f1f397 100644 --- a/src/python/grpcio_tests/tests/tests.json +++ b/src/python/grpcio_tests/tests/tests.json @@ -4,41 +4,42 @@ "_api_test.ChannelTest", "_auth_test.AccessTokenCallCredentialsTest", "_auth_test.GoogleCallCredentialsTest", - "_beta_features_test.BetaFeaturesTest", - "_beta_features_test.ContextManagementAndLifecycleTest", + "_beta_features_test.BetaFeaturesTest", + "_beta_features_test.ContextManagementAndLifecycleTest", "_cancel_many_calls_test.CancelManyCallsTest", "_channel_args_test.ChannelArgsTest", "_channel_connectivity_test.ChannelConnectivityTest", "_channel_ready_future_test.ChannelReadyFutureTest", - "_channel_test.ChannelTest", + "_channel_test.ChannelTest", "_compression_test.CompressionTest", "_connectivity_channel_test.ConnectivityStatesTest", "_credentials_test.CredentialsTest", "_empty_message_test.EmptyMessageTest", "_exit_test.ExitTest", - "_face_interface_test.DynamicInvokerBlockingInvocationInlineServiceTest", - "_face_interface_test.DynamicInvokerFutureInvocationAsynchronousEventServiceTest", - "_face_interface_test.GenericInvokerBlockingInvocationInlineServiceTest", - "_face_interface_test.GenericInvokerFutureInvocationAsynchronousEventServiceTest", - "_face_interface_test.MultiCallableInvokerBlockingInvocationInlineServiceTest", + "_face_interface_test.DynamicInvokerBlockingInvocationInlineServiceTest", + "_face_interface_test.DynamicInvokerFutureInvocationAsynchronousEventServiceTest", + "_face_interface_test.GenericInvokerBlockingInvocationInlineServiceTest", + "_face_interface_test.GenericInvokerFutureInvocationAsynchronousEventServiceTest", + "_face_interface_test.MultiCallableInvokerBlockingInvocationInlineServiceTest", "_face_interface_test.MultiCallableInvokerFutureInvocationAsynchronousEventServiceTest", "_health_servicer_test.HealthServicerTest", "_implementations_test.CallCredentialsTest", - "_implementations_test.ChannelCredentialsTest", - "_insecure_interop_test.InsecureInteropTest", - "_logging_pool_test.LoggingPoolTest", + "_implementations_test.ChannelCredentialsTest", + "_insecure_interop_test.InsecureInteropTest", + "_logging_pool_test.LoggingPoolTest", "_metadata_code_details_test.MetadataCodeDetailsTest", "_metadata_test.MetadataTest", - "_not_found_test.NotFoundTest", + "_not_found_test.NotFoundTest", "_python_plugin_test.PythonPluginTest", "_read_some_but_not_all_responses_test.ReadSomeButNotAllResponsesTest", + "_reflection_servicer_test.ReflectionServicerTest", "_rpc_test.RPCTest", - "_sanity_test.Sanity", - "_secure_interop_test.SecureInteropTest", + "_sanity_test.Sanity", + "_secure_interop_test.SecureInteropTest", "_thread_cleanup_test.CleanupThreadTest", - "_utilities_test.ChannelConnectivityTest", - "beta_python_plugin_test.PythonPluginTest", - "cygrpc_test.InsecureServerInsecureClient", - "cygrpc_test.SecureServerSecureClient", + "_utilities_test.ChannelConnectivityTest", + "beta_python_plugin_test.PythonPluginTest", + "cygrpc_test.InsecureServerInsecureClient", + "cygrpc_test.SecureServerSecureClient", "cygrpc_test.TypeSmokeTest" ] |