aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/python
diff options
context:
space:
mode:
Diffstat (limited to 'src/python')
-rw-r--r--src/python/.gitignore2
-rw-r--r--src/python/grpcio/.gitignore1
-rw-r--r--src/python/grpcio/commands.py69
-rw-r--r--src/python/grpcio/grpc/__init__.py33
-rw-r--r--src/python/grpcio/grpc/_channel.py17
-rw-r--r--src/python/grpcio/grpc/_common.py10
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi5
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi37
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi1
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi51
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi2
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi4
-rw-r--r--src/python/grpcio/grpc/_cython/cygrpc.pxd16
-rw-r--r--src/python/grpcio/grpc/_cython/cygrpc.pyx16
-rw-r--r--src/python/grpcio/grpc/_server.py5
-rw-r--r--src/python/grpcio/grpc/_utilities.py6
-rw-r--r--src/python/grpcio/grpc_core_dependencies.py84
-rw-r--r--src/python/grpcio/grpc_version.py2
-rw-r--r--src/python/grpcio/support.py13
-rw-r--r--src/python/grpcio_health_checking/grpc_version.py2
-rw-r--r--src/python/grpcio_reflection/.gitignore5
-rw-r--r--src/python/grpcio_reflection/grpc_reflection/__init__.py29
-rw-r--r--src/python/grpcio_reflection/grpc_reflection/v1alpha/__init__.py29
-rw-r--r--src/python/grpcio_reflection/grpc_reflection/v1alpha/reflection.py143
-rw-r--r--src/python/grpcio_reflection/grpc_version.py32
-rw-r--r--src/python/grpcio_reflection/reflection_commands.py78
-rw-r--r--src/python/grpcio_reflection/setup.py72
-rw-r--r--src/python/grpcio_tests/grpc_version.py2
-rw-r--r--src/python/grpcio_tests/setup.py10
-rw-r--r--src/python/grpcio_tests/tests/_loader.py4
-rw-r--r--src/python/grpcio_tests/tests/http2/_negative_http2_client.py153
-rw-r--r--src/python/grpcio_tests/tests/interop/_insecure_intraop_test.py (renamed from src/python/grpcio_tests/tests/interop/_insecure_interop_test.py)6
-rw-r--r--src/python/grpcio_tests/tests/interop/_intraop_test_case.py (renamed from src/python/grpcio_tests/tests/interop/_interop_test_case.py)2
-rw-r--r--src/python/grpcio_tests/tests/interop/_secure_intraop_test.py (renamed from src/python/grpcio_tests/tests/interop/_secure_interop_test.py)6
-rw-r--r--src/python/grpcio_tests/tests/interop/client.py13
-rw-r--r--src/python/grpcio_tests/tests/interop/methods.py188
-rw-r--r--src/python/grpcio_tests/tests/reflection/__init__.py28
-rw-r--r--src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py185
-rw-r--r--src/python/grpcio_tests/tests/stress/client.py26
-rw-r--r--src/python/grpcio_tests/tests/tests.json96
-rw-r--r--src/python/grpcio_tests/tests/unit/_api_test.py1
-rw-r--r--src/python/grpcio_tests/tests/unit/_channel_args_test.py60
-rw-r--r--src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py18
-rw-r--r--src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py8
-rw-r--r--src/python/grpcio_tests/tests/unit/_cython/_cancel_many_calls_test.py5
-rw-r--r--src/python/grpcio_tests/tests/unit/_cython/_read_some_but_not_all_responses_test.py5
-rw-r--r--src/python/grpcio_tests/tests/unit/_cython/cygrpc_test.py10
-rw-r--r--src/python/grpcio_tests/tests/unit/_exit_test.py1
-rw-r--r--src/python/grpcio_tests/tests/unit/_rpc_test.py29
-rw-r--r--src/python/grpcio_tests/tests/unit/_thread_pool.py48
50 files changed, 1434 insertions, 234 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/.gitignore b/src/python/grpcio/.gitignore
index 7cd8fab273..3309795948 100644
--- a/src/python/grpcio/.gitignore
+++ b/src/python/grpcio/.gitignore
@@ -14,3 +14,4 @@ doc/
_grpcio_metadata.py
htmlcov/
grpc/_cython/_credentials
+poison.c
diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py
index 86a73fa836..701c6af017 100644
--- a/src/python/grpcio/commands.py
+++ b/src/python/grpcio/commands.py
@@ -53,6 +53,7 @@ PYTHON_STEM = os.path.dirname(os.path.abspath(__file__))
GRPC_STEM = os.path.abspath(PYTHON_STEM + '../../../../')
PROTO_STEM = os.path.join(GRPC_STEM, 'src', 'proto')
PROTO_GEN_STEM = os.path.join(GRPC_STEM, 'src', 'python', 'gens')
+CYTHON_STEM = os.path.join(PYTHON_STEM, 'grpc', '_cython')
CONF_PY_ADDENDUM = """
extensions.append('sphinx.ext.napoleon')
@@ -61,6 +62,7 @@ napoleon_numpy_docstring = True
napoleon_include_special_with_doc = True
html_theme = 'sphinx_rtd_theme'
+copyright = "2016, The gRPC Authors"
"""
API_GLOSSARY = """
@@ -184,6 +186,71 @@ class BuildPy(build_py.build_py):
build_py.build_py.run(self)
+def _poison_extensions(extensions, message):
+ """Includes a file that will always fail to compile in all extensions."""
+ poison_filename = os.path.join(PYTHON_STEM, 'poison.c')
+ with open(poison_filename, 'w') as poison:
+ poison.write('#error {}'.format(message))
+ for extension in extensions:
+ extension.sources = [poison_filename]
+
+def check_and_update_cythonization(extensions):
+ """Replace .pyx files with their generated counterparts and return whether or
+ not cythonization still needs to occur."""
+ for extension in extensions:
+ generated_pyx_sources = []
+ other_sources = []
+ for source in extension.sources:
+ base, file_ext = os.path.splitext(source)
+ if file_ext == '.pyx':
+ generated_pyx_source = next(
+ (base + gen_ext for gen_ext in ('.c', '.cpp',)
+ if os.path.isfile(base + gen_ext)), None)
+ if generated_pyx_source:
+ generated_pyx_sources.append(generated_pyx_source)
+ else:
+ sys.stderr.write('Cython-generated files are missing...\n')
+ return False
+ else:
+ other_sources.append(source)
+ extension.sources = generated_pyx_sources + other_sources
+ sys.stderr.write('Found cython-generated files...\n')
+ return True
+
+def try_cythonize(extensions, linetracing=False, mandatory=True):
+ """Attempt to cythonize the extensions.
+
+ Args:
+ extensions: A list of `distutils.extension.Extension`.
+ linetracing: A bool indicating whether or not to enable linetracing.
+ mandatory: Whether or not having Cython-generated files is mandatory. If it
+ is, extensions will be poisoned when they can't be fully generated.
+ """
+ try:
+ # Break import style to ensure we have access to Cython post-setup_requires
+ import Cython.Build
+ except ImportError:
+ if mandatory:
+ sys.stderr.write(
+ "This package needs to generate C files with Cython but it cannot. "
+ "Poisoning extension sources to disallow extension commands...")
+ _poison_extensions(
+ extensions,
+ "Extensions have been poisoned due to missing Cython-generated code.")
+ return extensions
+ cython_compiler_directives = {}
+ if linetracing:
+ additional_define_macros = [('CYTHON_TRACE_NOGIL', '1')]
+ cython_compiler_directives['linetrace'] = True
+ return Cython.Build.cythonize(
+ extensions,
+ include_path=[
+ include_dir for extension in extensions for include_dir in extension.include_dirs
+ ] + [CYTHON_STEM],
+ compiler_directives=cython_compiler_directives
+ )
+
+
class BuildExt(build_ext.build_ext):
"""Custom build_ext command to enable compiler-specific flags."""
@@ -201,6 +268,8 @@ class BuildExt(build_ext.build_ext):
if compiler in BuildExt.LINK_OPTIONS:
for extension in self.extensions:
extension.extra_link_args += list(BuildExt.LINK_OPTIONS[compiler])
+ if not check_and_update_cythonization(self.extensions):
+ self.extensions = try_cythonize(self.extensions)
try:
build_ext.build_ext.build_extensions(self)
except Exception as error:
diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py
index f801dd632a..e3c10156d0 100644
--- a/src/python/grpcio/grpc/__init__.py
+++ b/src/python/grpcio/grpc/__init__.py
@@ -850,6 +850,26 @@ class GenericRpcHandler(six.with_metaclass(abc.ABCMeta)):
raise NotImplementedError()
+class ServiceRpcHandler(six.with_metaclass(abc.ABCMeta, GenericRpcHandler)):
+ """An implementation of RPC methods belonging to a service.
+
+ A service handles RPC methods with structured names of the form
+ '/Service.Name/Service.MethodX', where 'Service.Name' is the value
+ returned by service_name(), and 'Service.MethodX' is the service method
+ name. A service can have multiple service methods names, but only a single
+ service name.
+ """
+
+ @abc.abstractmethod
+ def service_name(self):
+ """Returns this services name.
+
+ Returns:
+ The service name.
+ """
+ raise NotImplementedError()
+
+
############################# Server Interface ###############################
@@ -1212,7 +1232,7 @@ def insecure_channel(target, options=None):
A Channel to the target through which RPCs may be conducted.
"""
from grpc import _channel
- return _channel.Channel(target, options, None)
+ return _channel.Channel(target, () if options is None else options, None)
def secure_channel(target, credentials, options=None):
@@ -1228,10 +1248,11 @@ def secure_channel(target, credentials, options=None):
A Channel to the target through which RPCs may be conducted.
"""
from grpc import _channel
- return _channel.Channel(target, options, credentials._credentials)
+ return _channel.Channel(target, () if options is None else options,
+ credentials._credentials)
-def server(thread_pool, handlers=None):
+def server(thread_pool, handlers=None, options=None):
"""Creates a Server with which RPCs can be serviced.
Args:
@@ -1242,12 +1263,15 @@ def server(thread_pool, handlers=None):
only handlers the server will use to service RPCs; other handlers may
later be added by calling add_generic_rpc_handlers any time before the
returned Server is started.
+ options: A sequence of string-value pairs according to which to configure
+ the created server.
Returns:
A Server with which RPCs can be serviced.
"""
from grpc import _server
- return _server.Server(thread_pool, () if handlers is None else handlers)
+ return _server.Server(thread_pool, () if handlers is None else handlers,
+ () if options is None else options)
################################### __all__ #################################
@@ -1277,6 +1301,7 @@ __all__ = (
'RpcMethodHandler',
'HandlerCallDetails',
'GenericRpcHandler',
+ 'ServiceRpcHandler',
'Server',
'unary_unary_rpc_method_handler',
'unary_stream_rpc_method_handler',
diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py
index f07142aad3..e8c6a99cb1 100644
--- a/src/python/grpcio/grpc/_channel.py
+++ b/src/python/grpcio/grpc/_channel.py
@@ -891,18 +891,8 @@ def _unsubscribe(state, callback):
def _options(options):
- if options is None:
- pairs = ((cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT),)
- else:
- pairs = list(options) + [
- (cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT)]
- encoded_pairs = [
- (_common.encode(arg_name), arg_value) if isinstance(arg_value, int)
- else (_common.encode(arg_name), _common.encode(arg_value))
- for arg_name, arg_value in pairs]
- return cygrpc.ChannelArgs([
- cygrpc.ChannelArg(arg_name, arg_value)
- for arg_name, arg_value in encoded_pairs])
+ return list(options) + [
+ (cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT)]
class Channel(grpc.Channel):
@@ -917,7 +907,8 @@ class Channel(grpc.Channel):
credentials: A cygrpc.ChannelCredentials or None.
"""
self._channel = cygrpc.Channel(
- _common.encode(target), _options(options), credentials)
+ _common.encode(target), _common.channel_args(_options(options)),
+ credentials)
self._call_state = _ChannelCallState(self._channel)
self._connectivity_state = _ChannelConnectivityState(self._channel)
diff --git a/src/python/grpcio/grpc/_common.py b/src/python/grpcio/grpc/_common.py
index 4d7d521419..cc0984c8c6 100644
--- a/src/python/grpcio/grpc/_common.py
+++ b/src/python/grpcio/grpc/_common.py
@@ -94,6 +94,16 @@ def decode(b):
return b.decode('latin1')
+def channel_args(options):
+ channel_args = []
+ for key, value in options:
+ if isinstance(value, six.string_types):
+ channel_args.append(cygrpc.ChannelArg(encode(key), encode(value)))
+ else:
+ channel_args.append(cygrpc.ChannelArg(encode(key), value))
+ return cygrpc.ChannelArgs(channel_args)
+
+
def cygrpc_metadata(application_metadata):
return _EMPTY_METADATA if application_metadata is None else cygrpc.Metadata(
cygrpc.Metadatum(encode(key), encode(value))
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
index 3df937eb14..73d1ff7b97 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
@@ -32,15 +32,16 @@ cimport cpython
cdef class Channel:
- def __cinit__(self, bytes target, ChannelArgs arguments=None,
+ def __cinit__(self, bytes target, ChannelArgs arguments,
ChannelCredentials channel_credentials=None):
grpc_init()
cdef grpc_channel_args *c_arguments = NULL
cdef char *c_target = NULL
self.c_channel = NULL
self.references = []
- if arguments is not None:
+ if len(arguments) > 0:
c_arguments = &arguments.c_args
+ self.references.append(arguments)
c_target = target
if channel_credentials is None:
with nogil:
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index 42fced6545..ad766186bd 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -53,23 +53,23 @@ cdef extern from "grpc/byte_buffer_reader.h":
cdef extern from "grpc/grpc.h":
- ctypedef struct gpr_slice:
- # don't worry about writing out the members of gpr_slice; we never access
+ ctypedef struct grpc_slice:
+ # don't worry about writing out the members of grpc_slice; we never access
# them directly.
pass
- gpr_slice gpr_slice_ref(gpr_slice s) nogil
- void gpr_slice_unref(gpr_slice s) nogil
- gpr_slice gpr_slice_new(void *p, size_t len, void (*destroy)(void *)) nogil
- gpr_slice gpr_slice_new_with_len(
+ grpc_slice grpc_slice_ref(grpc_slice s) nogil
+ void grpc_slice_unref(grpc_slice s) nogil
+ grpc_slice grpc_slice_new(void *p, size_t len, void (*destroy)(void *)) nogil
+ grpc_slice grpc_slice_new_with_len(
void *p, size_t len, void (*destroy)(void *, size_t)) nogil
- gpr_slice gpr_slice_malloc(size_t length) nogil
- gpr_slice gpr_slice_from_copied_string(const char *source) nogil
- gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len) nogil
+ grpc_slice grpc_slice_malloc(size_t length) nogil
+ grpc_slice grpc_slice_from_copied_string(const char *source) nogil
+ grpc_slice grpc_slice_from_copied_buffer(const char *source, size_t len) nogil
# Declare functions for function-like macros (because Cython)...
- void *gpr_slice_start_ptr "GPR_SLICE_START_PTR" (gpr_slice s) nogil
- size_t gpr_slice_length "GPR_SLICE_LENGTH" (gpr_slice s) nogil
+ void *grpc_slice_start_ptr "GRPC_SLICE_START_PTR" (grpc_slice s) nogil
+ size_t grpc_slice_length "GRPC_SLICE_LENGTH" (grpc_slice s) nogil
ctypedef enum gpr_clock_type:
GPR_CLOCK_MONOTONIC
@@ -101,7 +101,7 @@ cdef extern from "grpc/grpc.h":
# We don't care about the internals.
pass
- grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices,
+ grpc_byte_buffer *grpc_raw_byte_buffer_create(grpc_slice *slices,
size_t nslices) nogil
size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) nogil
void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer) nogil
@@ -109,7 +109,7 @@ cdef extern from "grpc/grpc.h":
int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
grpc_byte_buffer *buffer) nogil
int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
- gpr_slice *slice) nogil
+ grpc_slice *slice) nogil
void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader) nogil
ctypedef enum grpc_status_code:
@@ -135,7 +135,8 @@ cdef extern from "grpc/grpc.h":
const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
const char *GRPC_ARG_ENABLE_CENSUS
const char *GRPC_ARG_MAX_CONCURRENT_STREAMS
- const char *GRPC_ARG_MAX_MESSAGE_LENGTH
+ const char *GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH
+ const char *GRPC_ARG_MAX_SEND_MESSAGE_LENGTH
const char *GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
const char *GRPC_ARG_DEFAULT_AUTHORITY
const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
@@ -172,10 +173,14 @@ cdef extern from "grpc/grpc.h":
GRPC_ARG_INTEGER
GRPC_ARG_POINTER
- ctypedef struct grpc_arg_value_pointer:
- void *address "p"
+ ctypedef struct grpc_arg_pointer_vtable:
void *(*copy)(void *)
void (*destroy)(void *)
+ int (*cmp)(void *, void *)
+
+ ctypedef struct grpc_arg_value_pointer:
+ void *address "p"
+ grpc_arg_pointer_vtable *vtable
union grpc_arg_value:
char *string
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi
index 96c5b02bc2..00ec91b131 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi
@@ -84,6 +84,7 @@ cdef class SslPemKeyCertPair:
cdef class ChannelArg:
cdef grpc_arg c_arg
+ cdef grpc_arg_pointer_vtable ptr_vtable
cdef readonly object key, value
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
index 834a44123d..cadfce6ee6 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
@@ -27,6 +27,7 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+from libc.stdint cimport intptr_t
class ConnectivityState:
idle = GRPC_CHANNEL_IDLE
@@ -39,7 +40,8 @@ class ConnectivityState:
class ChannelArgKey:
enable_census = GRPC_ARG_ENABLE_CENSUS
max_concurrent_streams = GRPC_ARG_MAX_CONCURRENT_STREAMS
- max_message_length = GRPC_ARG_MAX_MESSAGE_LENGTH
+ max_receive_message_length = GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH
+ max_send_message_length = GRPC_ARG_MAX_SEND_MESSAGE_LENGTH
http2_initial_sequence_number = GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
default_authority = GRPC_ARG_DEFAULT_AUTHORITY
primary_user_agent_string = GRPC_ARG_PRIMARY_USER_AGENT_STRING
@@ -240,19 +242,19 @@ cdef class ByteBuffer:
return
cdef char *c_data = data
- cdef gpr_slice data_slice
+ cdef grpc_slice data_slice
cdef size_t data_length = len(data)
with nogil:
- data_slice = gpr_slice_from_copied_buffer(c_data, data_length)
+ data_slice = grpc_slice_from_copied_buffer(c_data, data_length)
with nogil:
self.c_byte_buffer = grpc_raw_byte_buffer_create(
&data_slice, 1)
with nogil:
- gpr_slice_unref(data_slice)
+ grpc_slice_unref(data_slice)
def bytes(self):
cdef grpc_byte_buffer_reader reader
- cdef gpr_slice data_slice
+ cdef grpc_slice data_slice
cdef size_t data_slice_length
cdef void *data_slice_pointer
cdef bint reader_status
@@ -265,11 +267,11 @@ cdef class ByteBuffer:
result = bytearray()
with nogil:
while grpc_byte_buffer_reader_next(&reader, &data_slice):
- data_slice_pointer = gpr_slice_start_ptr(data_slice)
- data_slice_length = gpr_slice_length(data_slice)
+ data_slice_pointer = grpc_slice_start_ptr(data_slice)
+ data_slice_length = grpc_slice_length(data_slice)
with gil:
result += (<char *>data_slice_pointer)[:data_slice_length]
- gpr_slice_unref(data_slice)
+ grpc_slice_unref(data_slice)
with nogil:
grpc_byte_buffer_reader_destroy(&reader)
return bytes(result)
@@ -303,20 +305,49 @@ cdef class SslPemKeyCertPair:
self.c_pair.certificate_chain = self.certificate_chain
+
+cdef void* copy_ptr(void* ptr):
+ return ptr
+
+
+cdef void destroy_ptr(void* ptr):
+ pass
+
+
+cdef int compare_ptr(void* ptr1, void* ptr2):
+ if ptr1 < ptr2:
+ return -1
+ elif ptr1 > ptr2:
+ return 1
+ else:
+ return 0
+
+
cdef class ChannelArg:
def __cinit__(self, bytes key, value):
self.key = key
+ self.value = value
self.c_arg.key = self.key
if isinstance(value, int):
- self.value = value
self.c_arg.type = GRPC_ARG_INTEGER
self.c_arg.value.integer = self.value
elif isinstance(value, bytes):
- self.value = value
self.c_arg.type = GRPC_ARG_STRING
self.c_arg.value.string = self.value
+ elif hasattr(value, '__int__'):
+ # Pointer objects must override __int__() to return
+ # the underlying C address (Python ints are word size). The
+ # lifecycle of the pointer is fixed to the lifecycle of the
+ # python object wrapping it.
+ self.ptr_vtable.copy = &copy_ptr
+ self.ptr_vtable.destroy = &destroy_ptr
+ self.ptr_vtable.cmp = &compare_ptr
+ self.c_arg.type = GRPC_ARG_POINTER
+ self.c_arg.value.pointer.vtable = &self.ptr_vtable
+ self.c_arg.value.pointer.address = <void*>(<intptr_t>int(self.value))
else:
+ # TODO Add supported pointer types to this message
raise TypeError('Expected int or bytes, got {}'.format(type(value)))
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
index 23cee7bd6e..20fc1c5fce 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
@@ -35,7 +35,7 @@ import pkg_resources
cdef grpc_ssl_roots_override_result ssl_roots_override_callback(
char **pem_root_certs) with gil:
temporary_pem_root_certs = pkg_resources.resource_string(
- 'grpc._cython', '_credentials/roots.pem')
+ __name__.rstrip('.cygrpc'), '_credentials/roots.pem')
pem_root_certs[0] = <char *>gpr_malloc(len(temporary_pem_root_certs) + 1)
memcpy(
pem_root_certs[0], <char *>temporary_pem_root_certs,
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
index ca2b831114..18db38b686 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
@@ -34,12 +34,12 @@ import time
cdef class Server:
- def __cinit__(self, ChannelArgs arguments=None):
+ def __cinit__(self, ChannelArgs arguments):
grpc_init()
cdef grpc_channel_args *c_arguments = NULL
self.references = []
self.registered_completion_queues = []
- if arguments is not None:
+ if len(arguments) > 0:
c_arguments = &arguments.c_args
self.references.append(arguments)
with nogil:
diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pxd b/src/python/grpcio/grpc/_cython/cygrpc.pxd
index 9779534e38..26454634a1 100644
--- a/src/python/grpcio/grpc/_cython/cygrpc.pxd
+++ b/src/python/grpcio/grpc/_cython/cygrpc.pxd
@@ -27,12 +27,12 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-include "grpc/_cython/_cygrpc/grpc.pxi"
+include "_cygrpc/grpc.pxi"
-include "grpc/_cython/_cygrpc/call.pxd.pxi"
-include "grpc/_cython/_cygrpc/channel.pxd.pxi"
-include "grpc/_cython/_cygrpc/credentials.pxd.pxi"
-include "grpc/_cython/_cygrpc/completion_queue.pxd.pxi"
-include "grpc/_cython/_cygrpc/records.pxd.pxi"
-include "grpc/_cython/_cygrpc/security.pxd.pxi"
-include "grpc/_cython/_cygrpc/server.pxd.pxi"
+include "_cygrpc/call.pxd.pxi"
+include "_cygrpc/channel.pxd.pxi"
+include "_cygrpc/credentials.pxd.pxi"
+include "_cygrpc/completion_queue.pxd.pxi"
+include "_cygrpc/records.pxd.pxi"
+include "_cygrpc/security.pxd.pxi"
+include "_cygrpc/server.pxd.pxi"
diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx
index 08089994a9..e1bd046a1a 100644
--- a/src/python/grpcio/grpc/_cython/cygrpc.pyx
+++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx
@@ -35,14 +35,14 @@ import sys
# TODO(atash): figure out why the coverage tool gets confused about the Cython
# coverage plugin when the following files don't have a '.pxi' suffix.
-include "grpc/_cython/_cygrpc/grpc_string.pyx.pxi"
-include "grpc/_cython/_cygrpc/call.pyx.pxi"
-include "grpc/_cython/_cygrpc/channel.pyx.pxi"
-include "grpc/_cython/_cygrpc/credentials.pyx.pxi"
-include "grpc/_cython/_cygrpc/completion_queue.pyx.pxi"
-include "grpc/_cython/_cygrpc/records.pyx.pxi"
-include "grpc/_cython/_cygrpc/security.pyx.pxi"
-include "grpc/_cython/_cygrpc/server.pyx.pxi"
+include "_cygrpc/grpc_string.pyx.pxi"
+include "_cygrpc/call.pyx.pxi"
+include "_cygrpc/channel.pyx.pxi"
+include "_cygrpc/credentials.pyx.pxi"
+include "_cygrpc/completion_queue.pyx.pxi"
+include "_cygrpc/records.pyx.pxi"
+include "_cygrpc/security.pyx.pxi"
+include "_cygrpc/server.pyx.pxi"
#
# initialize gRPC
diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py
index f70cd2afa5..5223712dfa 100644
--- a/src/python/grpcio/grpc/_server.py
+++ b/src/python/grpcio/grpc/_server.py
@@ -727,12 +727,11 @@ def _start(state):
cleanup_server, target=_serve, args=(state,))
thread.start()
-
class Server(grpc.Server):
- def __init__(self, thread_pool, generic_handlers):
+ def __init__(self, thread_pool, generic_handlers, options):
completion_queue = cygrpc.CompletionQueue()
- server = cygrpc.Server()
+ server = cygrpc.Server(_common.channel_args(options))
server.register_completion_queue(completion_queue)
self._state = _ServerState(
completion_queue, server, generic_handlers, thread_pool)
diff --git a/src/python/grpcio/grpc/_utilities.py b/src/python/grpcio/grpc/_utilities.py
index 4850967fbc..a375896e6e 100644
--- a/src/python/grpcio/grpc/_utilities.py
+++ b/src/python/grpcio/grpc/_utilities.py
@@ -53,13 +53,17 @@ class RpcMethodHandler(
pass
-class DictionaryGenericHandler(grpc.GenericRpcHandler):
+class DictionaryGenericHandler(grpc.ServiceRpcHandler):
def __init__(self, service, method_handlers):
+ self._name = service
self._method_handlers = {
_common.fully_qualified_method(service, method): method_handler
for method, method_handler in six.iteritems(method_handlers)}
+ def service_name(self):
+ return self._name
+
def service(self, handler_call_details):
return self._method_handlers.get(handler_call_details.method)
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index b37e27c27e..d43f93b94f 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -50,9 +50,8 @@ CORE_SOURCE_FILES = [
'src/core/lib/support/log_linux.c',
'src/core/lib/support/log_posix.c',
'src/core/lib/support/log_windows.c',
+ 'src/core/lib/support/mpscq.c',
'src/core/lib/support/murmur_hash.c',
- 'src/core/lib/support/slice.c',
- 'src/core/lib/support/slice_buffer.c',
'src/core/lib/support/stack_lockfree.c',
'src/core/lib/support/string.c',
'src/core/lib/support/string_posix.c',
@@ -81,8 +80,11 @@ CORE_SOURCE_FILES = [
'src/core/lib/channel/channel_stack_builder.c',
'src/core/lib/channel/compress_filter.c',
'src/core/lib/channel/connected_channel.c',
+ 'src/core/lib/channel/deadline_filter.c',
+ 'src/core/lib/channel/handshaker.c',
'src/core/lib/channel/http_client_filter.c',
'src/core/lib/channel/http_server_filter.c',
+ 'src/core/lib/channel/message_size_filter.c',
'src/core/lib/compression/compression.c',
'src/core/lib/compression/message_compress.c',
'src/core/lib/debug/trace.c',
@@ -90,12 +92,13 @@ CORE_SOURCE_FILES = [
'src/core/lib/http/httpcli.c',
'src/core/lib/http/parser.c',
'src/core/lib/iomgr/closure.c',
+ 'src/core/lib/iomgr/combiner.c',
'src/core/lib/iomgr/endpoint.c',
'src/core/lib/iomgr/endpoint_pair_posix.c',
+ 'src/core/lib/iomgr/endpoint_pair_uv.c',
'src/core/lib/iomgr/endpoint_pair_windows.c',
'src/core/lib/iomgr/error.c',
'src/core/lib/iomgr/ev_epoll_linux.c',
- 'src/core/lib/iomgr/ev_poll_and_epoll_posix.c',
'src/core/lib/iomgr/ev_poll_posix.c',
'src/core/lib/iomgr/ev_posix.c',
'src/core/lib/iomgr/exec_ctx.c',
@@ -103,41 +106,58 @@ CORE_SOURCE_FILES = [
'src/core/lib/iomgr/iocp_windows.c',
'src/core/lib/iomgr/iomgr.c',
'src/core/lib/iomgr/iomgr_posix.c',
+ 'src/core/lib/iomgr/iomgr_uv.c',
'src/core/lib/iomgr/iomgr_windows.c',
'src/core/lib/iomgr/load_file.c',
'src/core/lib/iomgr/network_status_tracker.c',
'src/core/lib/iomgr/polling_entity.c',
+ 'src/core/lib/iomgr/pollset_set_uv.c',
'src/core/lib/iomgr/pollset_set_windows.c',
+ 'src/core/lib/iomgr/pollset_uv.c',
'src/core/lib/iomgr/pollset_windows.c',
'src/core/lib/iomgr/resolve_address_posix.c',
+ 'src/core/lib/iomgr/resolve_address_uv.c',
'src/core/lib/iomgr/resolve_address_windows.c',
+ 'src/core/lib/iomgr/resource_quota.c',
'src/core/lib/iomgr/sockaddr_utils.c',
+ 'src/core/lib/iomgr/socket_mutator.c',
'src/core/lib/iomgr/socket_utils_common_posix.c',
'src/core/lib/iomgr/socket_utils_linux.c',
'src/core/lib/iomgr/socket_utils_posix.c',
+ 'src/core/lib/iomgr/socket_utils_uv.c',
+ 'src/core/lib/iomgr/socket_utils_windows.c',
'src/core/lib/iomgr/socket_windows.c',
'src/core/lib/iomgr/tcp_client_posix.c',
+ 'src/core/lib/iomgr/tcp_client_uv.c',
'src/core/lib/iomgr/tcp_client_windows.c',
'src/core/lib/iomgr/tcp_posix.c',
'src/core/lib/iomgr/tcp_server_posix.c',
+ 'src/core/lib/iomgr/tcp_server_uv.c',
'src/core/lib/iomgr/tcp_server_windows.c',
+ 'src/core/lib/iomgr/tcp_uv.c',
'src/core/lib/iomgr/tcp_windows.c',
'src/core/lib/iomgr/time_averaged_stats.c',
- 'src/core/lib/iomgr/timer.c',
+ 'src/core/lib/iomgr/timer_generic.c',
'src/core/lib/iomgr/timer_heap.c',
+ 'src/core/lib/iomgr/timer_uv.c',
'src/core/lib/iomgr/udp_server.c',
'src/core/lib/iomgr/unix_sockets_posix.c',
'src/core/lib/iomgr/unix_sockets_posix_noop.c',
+ 'src/core/lib/iomgr/wakeup_fd_cv.c',
'src/core/lib/iomgr/wakeup_fd_eventfd.c',
'src/core/lib/iomgr/wakeup_fd_nospecial.c',
'src/core/lib/iomgr/wakeup_fd_pipe.c',
'src/core/lib/iomgr/wakeup_fd_posix.c',
- 'src/core/lib/iomgr/workqueue_posix.c',
+ 'src/core/lib/iomgr/workqueue_uv.c',
'src/core/lib/iomgr/workqueue_windows.c',
'src/core/lib/json/json.c',
'src/core/lib/json/json_reader.c',
'src/core/lib/json/json_string.c',
'src/core/lib/json/json_writer.c',
+ 'src/core/lib/slice/percent_encoding.c',
+ 'src/core/lib/slice/slice.c',
+ 'src/core/lib/slice/slice_buffer.c',
+ 'src/core/lib/slice/slice_string_helpers.c',
'src/core/lib/surface/alarm.c',
'src/core/lib/surface/api_trace.c',
'src/core/lib/surface/byte_buffer.c',
@@ -158,9 +178,13 @@ CORE_SOURCE_FILES = [
'src/core/lib/surface/version.c',
'src/core/lib/transport/byte_stream.c',
'src/core/lib/transport/connectivity_state.c',
+ 'src/core/lib/transport/mdstr_hash_table.c',
'src/core/lib/transport/metadata.c',
'src/core/lib/transport/metadata_batch.c',
+ 'src/core/lib/transport/pid_controller.c',
+ 'src/core/lib/transport/service_config.c',
'src/core/lib/transport/static_metadata.c',
+ 'src/core/lib/transport/timeout_encoding.c',
'src/core/lib/transport/transport.c',
'src/core/lib/transport/transport_op_string.c',
'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c',
@@ -183,7 +207,6 @@ CORE_SOURCE_FILES = [
'src/core/ext/transport/chttp2/transport/status_conversion.c',
'src/core/ext/transport/chttp2/transport/stream_lists.c',
'src/core/ext/transport/chttp2/transport/stream_map.c',
- 'src/core/ext/transport/chttp2/transport/timeout_encoding.c',
'src/core/ext/transport/chttp2/transport/varint.c',
'src/core/ext/transport/chttp2/transport/writing.c',
'src/core/ext/transport/chttp2/alpn/alpn.c',
@@ -193,8 +216,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/security/credentials/credentials.c',
'src/core/lib/security/credentials/credentials_metadata.c',
'src/core/lib/security/credentials/fake/fake_credentials.c',
- 'src/core/lib/security/credentials/google_default/credentials_posix.c',
- 'src/core/lib/security/credentials/google_default/credentials_windows.c',
+ 'src/core/lib/security/credentials/google_default/credentials_generic.c',
'src/core/lib/security/credentials/google_default/google_default_credentials.c',
'src/core/lib/security/credentials/iam/iam_credentials.c',
'src/core/lib/security/credentials/jwt/json_token.c',
@@ -204,9 +226,9 @@ CORE_SOURCE_FILES = [
'src/core/lib/security/credentials/plugin/plugin_credentials.c',
'src/core/lib/security/credentials/ssl/ssl_credentials.c',
'src/core/lib/security/transport/client_auth_filter.c',
- 'src/core/lib/security/transport/handshake.c',
'src/core/lib/security/transport/secure_endpoint.c',
'src/core/lib/security/transport/security_connector.c',
+ 'src/core/lib/security/transport/security_handshaker.c',
'src/core/lib/security/transport/server_auth_filter.c',
'src/core/lib/security/transport/tsi_error.c',
'src/core/lib/security/util/b64.c',
@@ -215,30 +237,32 @@ CORE_SOURCE_FILES = [
'src/core/lib/tsi/fake_transport_security.c',
'src/core/lib/tsi/ssl_transport_security.c',
'src/core/lib/tsi/transport_security.c',
+ 'src/core/ext/transport/chttp2/server/chttp2_server.c',
'src/core/ext/transport/chttp2/client/secure/secure_channel_create.c',
- 'src/core/ext/client_config/channel_connectivity.c',
- 'src/core/ext/client_config/client_channel.c',
- 'src/core/ext/client_config/client_channel_factory.c',
- 'src/core/ext/client_config/client_config.c',
- 'src/core/ext/client_config/client_config_plugin.c',
- 'src/core/ext/client_config/connector.c',
- 'src/core/ext/client_config/default_initial_connect_string.c',
- 'src/core/ext/client_config/initial_connect_string.c',
- 'src/core/ext/client_config/lb_policy.c',
- 'src/core/ext/client_config/lb_policy_factory.c',
- 'src/core/ext/client_config/lb_policy_registry.c',
- 'src/core/ext/client_config/parse_address.c',
- 'src/core/ext/client_config/resolver.c',
- 'src/core/ext/client_config/resolver_factory.c',
- 'src/core/ext/client_config/resolver_registry.c',
- 'src/core/ext/client_config/subchannel.c',
- 'src/core/ext/client_config/subchannel_call_holder.c',
- 'src/core/ext/client_config/subchannel_index.c',
- 'src/core/ext/client_config/uri_parser.c',
+ 'src/core/ext/client_channel/channel_connectivity.c',
+ 'src/core/ext/client_channel/client_channel.c',
+ 'src/core/ext/client_channel/client_channel_factory.c',
+ 'src/core/ext/client_channel/client_channel_plugin.c',
+ 'src/core/ext/client_channel/connector.c',
+ 'src/core/ext/client_channel/default_initial_connect_string.c',
+ 'src/core/ext/client_channel/http_connect_handshaker.c',
+ 'src/core/ext/client_channel/initial_connect_string.c',
+ 'src/core/ext/client_channel/lb_policy.c',
+ 'src/core/ext/client_channel/lb_policy_factory.c',
+ 'src/core/ext/client_channel/lb_policy_registry.c',
+ 'src/core/ext/client_channel/parse_address.c',
+ 'src/core/ext/client_channel/resolver.c',
+ 'src/core/ext/client_channel/resolver_factory.c',
+ 'src/core/ext/client_channel/resolver_registry.c',
+ 'src/core/ext/client_channel/subchannel.c',
+ 'src/core/ext/client_channel/subchannel_index.c',
+ 'src/core/ext/client_channel/uri_parser.c',
+ 'src/core/ext/transport/chttp2/client/chttp2_connector.c',
'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c',
'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c',
+ 'src/core/ext/lb_policy/grpclb/grpclb.c',
'src/core/ext/lb_policy/grpclb/load_balancer_api.c',
'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
'third_party/nanopb/pb_common.c',
@@ -250,8 +274,10 @@ CORE_SOURCE_FILES = [
'src/core/ext/resolver/sockaddr/sockaddr_resolver.c',
'src/core/ext/load_reporting/load_reporting.c',
'src/core/ext/load_reporting/load_reporting_filter.c',
+ 'src/core/ext/census/base_resources.c',
'src/core/ext/census/context.c',
'src/core/ext/census/gen/census.pb.c',
+ 'src/core/ext/census/gen/trace_context.pb.c',
'src/core/ext/census/grpc_context.c',
'src/core/ext/census/grpc_filter.c',
'src/core/ext/census/grpc_plugin.c',
@@ -259,6 +285,8 @@ CORE_SOURCE_FILES = [
'src/core/ext/census/mlog.c',
'src/core/ext/census/operation.c',
'src/core/ext/census/placeholders.c',
+ 'src/core/ext/census/resource.c',
+ 'src/core/ext/census/trace_context.c',
'src/core/ext/census/tracing.c',
'src/core/plugin_registry/grpc_plugin_registry.c',
'src/boringssl/err_data.c',
diff --git a/src/python/grpcio/grpc_version.py b/src/python/grpcio/grpc_version.py
index 1c0183ee47..ea38526a28 100644
--- a/src/python/grpcio/grpc_version.py
+++ b/src/python/grpcio/grpc_version.py
@@ -29,4 +29,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!!
-VERSION='1.0.4'
+VERSION='1.1.0.dev0'
diff --git a/src/python/grpcio/support.py b/src/python/grpcio/support.py
index 7730374df0..b226e690fd 100644
--- a/src/python/grpcio/support.py
+++ b/src/python/grpcio/support.py
@@ -45,7 +45,10 @@ int main(int argc, char **argv) { return 0; }
"""
C_PYTHON_DEV_ERROR_MESSAGE = """
Could not find <Python.h>. This could mean the following:
- * You're on Ubuntu and haven't `apt-get install`ed `python-dev`.
+ * You're on Ubuntu and haven't run `apt-get install python-dev`.
+ * You're on RHEL/Fedora and haven't run `yum install python-devel` or
+ `dnf install python-devel` (make sure you also have redhat-rpm-config
+ installed)
* You're on Mac OS X and the usual Python framework was somehow corrupted
(check your environment variables or try re-installing?)
* You're on Windows and your Python installation was somehow corrupted
@@ -97,9 +100,15 @@ def diagnose_compile_error(build_ext, error):
.format(source)
)
+def diagnose_attribute_error(build_ext, error):
+ if any('_needs_stub' in arg for arg in error.args):
+ raise commands.CommandError(
+ "We expect a missing `_needs_stub` attribute from older versions of "
+ "setuptools. Consider upgrading setuptools.")
_ERROR_DIAGNOSES = {
- errors.CompileError: diagnose_compile_error
+ errors.CompileError: diagnose_compile_error,
+ AttributeError: diagnose_attribute_error
}
def diagnose_build_ext_error(build_ext, error, formatted):
diff --git a/src/python/grpcio_health_checking/grpc_version.py b/src/python/grpcio_health_checking/grpc_version.py
index 60e94ffcfa..be0d0ced3c 100644
--- a/src/python/grpcio_health_checking/grpc_version.py
+++ b/src/python/grpcio_health_checking/grpc_version.py
@@ -29,4 +29,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!!
-VERSION='1.0.4'
+VERSION='1.1.0.dev0'
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_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..bfcbce8e04
--- /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..dee5491e0a
--- /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..cfc41f4fe7
--- /dev/null
+++ b/src/python/grpcio_reflection/setup.py
@@ -0,0 +1,72 @@
+# 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('.'),
+ install_requires=INSTALL_REQUIRES,
+ setup_requires=SETUP_REQUIRES,
+ cmdclass=COMMAND_CLASS
+)
diff --git a/src/python/grpcio_tests/grpc_version.py b/src/python/grpcio_tests/grpc_version.py
index bdc1c36b70..90f68a5741 100644
--- a/src/python/grpcio_tests/grpc_version.py
+++ b/src/python/grpcio_tests/grpc_version.py
@@ -29,4 +29,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!!
-VERSION='1.0.4'
+VERSION='1.1.0.dev0'
diff --git a/src/python/grpcio_tests/setup.py b/src/python/grpcio_tests/setup.py
index cd9194a7a8..375fbd6c77 100644
--- a/src/python/grpcio_tests/setup.py
+++ b/src/python/grpcio_tests/setup.py
@@ -80,8 +80,14 @@ PACKAGE_DATA = {
'credentials/server1.key',
'credentials/server1.pem',
],
- 'tests.protoc_plugin': [
- 'protoc_plugin_test.proto',
+ 'tests.protoc_plugin.protos.invocation_testing': [
+ 'same.proto',
+ ],
+ 'tests.protoc_plugin.protos.invocation_testing.split_messages': [
+ 'messages.proto',
+ ],
+ 'tests.protoc_plugin.protos.invocation_testing.split_services': [
+ 'services.proto',
],
'tests.unit': [
'credentials/ca.pem',
diff --git a/src/python/grpcio_tests/tests/_loader.py b/src/python/grpcio_tests/tests/_loader.py
index c2f097f6c6..621bedc7bb 100644
--- a/src/python/grpcio_tests/tests/_loader.py
+++ b/src/python/grpcio_tests/tests/_loader.py
@@ -84,11 +84,9 @@ class Loader(object):
along.
"""
for importer, module_name, is_package in (
- pkgutil.iter_modules(package_paths)):
+ pkgutil.walk_packages(package_paths)):
module = importer.find_module(module_name).load_module(module_name)
self.visit_module(module)
- if is_package:
- self.walk_packages(module.__path__)
def visit_module(self, module):
"""Visits the module, adding discovered tests to the test suite.
diff --git a/src/python/grpcio_tests/tests/http2/_negative_http2_client.py b/src/python/grpcio_tests/tests/http2/_negative_http2_client.py
new file mode 100644
index 0000000000..f8604683b3
--- /dev/null
+++ b/src/python/grpcio_tests/tests/http2/_negative_http2_client.py
@@ -0,0 +1,153 @@
+# 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.
+
+"""The Python client used to test negative http2 conditions."""
+
+import argparse
+
+import grpc
+from src.proto.grpc.testing import test_pb2
+from src.proto.grpc.testing import messages_pb2
+
+def _validate_payload_type_and_length(response, expected_type, expected_length):
+ if response.payload.type is not expected_type:
+ raise ValueError(
+ 'expected payload type %s, got %s' %
+ (expected_type, type(response.payload.type)))
+ elif len(response.payload.body) != expected_length:
+ raise ValueError(
+ 'expected payload body size %d, got %d' %
+ (expected_length, len(response.payload.body)))
+
+def _expect_status_code(call, expected_code):
+ if call.code() != expected_code:
+ raise ValueError(
+ 'expected code %s, got %s' % (expected_code, call.code()))
+
+def _expect_status_details(call, expected_details):
+ if call.details() != expected_details:
+ raise ValueError(
+ 'expected message %s, got %s' % (expected_details, call.details()))
+
+def _validate_status_code_and_details(call, expected_code, expected_details):
+ _expect_status_code(call, expected_code)
+ _expect_status_details(call, expected_details)
+
+# common requests
+_REQUEST_SIZE = 314159
+_RESPONSE_SIZE = 271828
+
+_SIMPLE_REQUEST = messages_pb2.SimpleRequest(
+ response_type=messages_pb2.COMPRESSABLE,
+ response_size=_RESPONSE_SIZE,
+ payload=messages_pb2.Payload(body=b'\x00' * _REQUEST_SIZE))
+
+def _goaway(stub):
+ first_response = stub.UnaryCall(_SIMPLE_REQUEST)
+ _validate_payload_type_and_length(first_response,
+ messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+ second_response = stub.UnaryCall(_SIMPLE_REQUEST)
+ _validate_payload_type_and_length(second_response,
+ messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _rst_after_header(stub):
+ resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+ _validate_status_code_and_details(resp_future, grpc.StatusCode.UNAVAILABLE, "")
+
+def _rst_during_data(stub):
+ resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+ _validate_status_code_and_details(resp_future, grpc.StatusCode.UNKNOWN, "")
+
+def _rst_after_data(stub):
+ resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+ _validate_payload_type_and_length(next(resp_future),
+ messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+ _validate_status_code_and_details(resp_future, grpc.StatusCode.UNKNOWN, "")
+
+def _ping(stub):
+ response = stub.UnaryCall(_SIMPLE_REQUEST)
+ _validate_payload_type_and_length(response,
+ messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _max_streams(stub):
+ # send one req to ensure server sets MAX_STREAMS
+ response = stub.UnaryCall(_SIMPLE_REQUEST)
+ _validate_payload_type_and_length(response,
+ messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+ # give the streams a workout
+ futures = []
+ for _ in range(15):
+ futures.append(stub.UnaryCall.future(_SIMPLE_REQUEST))
+ for future in futures:
+ _validate_payload_type_and_length(future.result(),
+ messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _run_test_case(test_case, stub):
+ if test_case == 'goaway':
+ _goaway(stub)
+ elif test_case == 'rst_after_header':
+ _rst_after_header(stub)
+ elif test_case == 'rst_during_data':
+ _rst_during_data(stub)
+ elif test_case == 'rst_after_data':
+ _rst_after_data(stub)
+ elif test_case =='ping':
+ _ping(stub)
+ elif test_case == 'max_streams':
+ _max_streams(stub)
+ else:
+ raise ValueError("Invalid test case: %s" % test_case)
+
+def _args():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--server_host', help='the host to which to connect', type=str,
+ default="127.0.0.1")
+ parser.add_argument(
+ '--server_port', help='the port to which to connect', type=int,
+ default="8080")
+ parser.add_argument(
+ '--test_case', help='the test case to execute', type=str,
+ default="goaway")
+ return parser.parse_args()
+
+def _stub(server_host, server_port):
+ target = '{}:{}'.format(server_host, server_port)
+ channel = grpc.insecure_channel(target)
+ return test_pb2.TestServiceStub(channel)
+
+def main():
+ args = _args()
+ stub = _stub(args.server_host, args.server_port)
+ _run_test_case(args.test_case, stub)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/python/grpcio_tests/tests/interop/_insecure_interop_test.py b/src/python/grpcio_tests/tests/interop/_insecure_intraop_test.py
index 936c895bd2..4fb22b4d9d 100644
--- a/src/python/grpcio_tests/tests/interop/_insecure_interop_test.py
+++ b/src/python/grpcio_tests/tests/interop/_insecure_intraop_test.py
@@ -35,13 +35,13 @@ import unittest
import grpc
from src.proto.grpc.testing import test_pb2
-from tests.interop import _interop_test_case
+from tests.interop import _intraop_test_case
from tests.interop import methods
from tests.interop import server
-class InsecureInteropTest(
- _interop_test_case.InteropTestCase,
+class InsecureIntraopTest(
+ _intraop_test_case.IntraopTestCase,
unittest.TestCase):
def setUp(self):
diff --git a/src/python/grpcio_tests/tests/interop/_interop_test_case.py b/src/python/grpcio_tests/tests/interop/_intraop_test_case.py
index ccea17a66d..fe1c173992 100644
--- a/src/python/grpcio_tests/tests/interop/_interop_test_case.py
+++ b/src/python/grpcio_tests/tests/interop/_intraop_test_case.py
@@ -32,7 +32,7 @@
from tests.interop import methods
-class InteropTestCase(object):
+class IntraopTestCase(object):
"""Unit test methods.
This class must be mixed in with unittest.TestCase and a class that defines
diff --git a/src/python/grpcio_tests/tests/interop/_secure_interop_test.py b/src/python/grpcio_tests/tests/interop/_secure_intraop_test.py
index eaca553e1b..3665c69726 100644
--- a/src/python/grpcio_tests/tests/interop/_secure_interop_test.py
+++ b/src/python/grpcio_tests/tests/interop/_secure_intraop_test.py
@@ -35,15 +35,15 @@ import unittest
import grpc
from src.proto.grpc.testing import test_pb2
-from tests.interop import _interop_test_case
+from tests.interop import _intraop_test_case
from tests.interop import methods
from tests.interop import resources
_SERVER_HOST_OVERRIDE = 'foo.test.google.fr'
-class SecureInteropTest(
- _interop_test_case.InteropTestCase,
+class SecureIntraopTest(
+ _intraop_test_case.IntraopTestCase,
unittest.TestCase):
def setUp(self):
diff --git a/src/python/grpcio_tests/tests/interop/client.py b/src/python/grpcio_tests/tests/interop/client.py
index 9d61d18975..afaa466254 100644
--- a/src/python/grpcio_tests/tests/interop/client.py
+++ b/src/python/grpcio_tests/tests/interop/client.py
@@ -43,11 +43,13 @@ from tests.interop import resources
def _args():
parser = argparse.ArgumentParser()
parser.add_argument(
- '--server_host', help='the host to which to connect', type=str)
+ '--server_host', help='the host to which to connect', type=str,
+ default="127.0.0.1")
parser.add_argument(
'--server_port', help='the port to which to connect', type=int)
parser.add_argument(
- '--test_case', help='the test case to execute', type=str)
+ '--test_case', help='the test case to execute', type=str,
+ default="large_unary")
parser.add_argument(
'--use_tls', help='require a secure connection', default=False,
type=resources.parse_bool)
@@ -55,7 +57,7 @@ def _args():
'--use_test_ca', help='replace platform root CAs with ca.pem',
default=False, type=resources.parse_bool)
parser.add_argument(
- '--server_host_override',
+ '--server_host_override', default="foo.test.google.fr",
help='the server host to which to claim to connect', type=str)
parser.add_argument('--oauth_scope', help='scope for OAuth tokens', type=str)
parser.add_argument(
@@ -106,7 +108,10 @@ def _stub(args):
(('grpc.ssl_target_name_override', args.server_host_override,),))
else:
channel = grpc.insecure_channel(target)
- return test_pb2.TestServiceStub(channel)
+ if args.test_case == "unimplemented_service":
+ return test_pb2.UnimplementedServiceStub(channel)
+ else:
+ return test_pb2.TestServiceStub(channel)
def _test_case_from_arg(test_case_arg):
diff --git a/src/python/grpcio_tests/tests/interop/methods.py b/src/python/grpcio_tests/tests/interop/methods.py
index 16afe4c6bf..9038ae5751 100644
--- a/src/python/grpcio_tests/tests/interop/methods.py
+++ b/src/python/grpcio_tests/tests/interop/methods.py
@@ -43,25 +43,43 @@ from src.proto.grpc.testing import empty_pb2
from src.proto.grpc.testing import messages_pb2
from src.proto.grpc.testing import test_pb2
+_INITIAL_METADATA_KEY = "x-grpc-test-echo-initial"
+_TRAILING_METADATA_KEY = "x-grpc-test-echo-trailing-bin"
+
+def _maybe_echo_metadata(servicer_context):
+ """Copies metadata from request to response if it is present."""
+ invocation_metadata = dict(servicer_context.invocation_metadata())
+ if _INITIAL_METADATA_KEY in invocation_metadata:
+ initial_metadatum = (
+ _INITIAL_METADATA_KEY, invocation_metadata[_INITIAL_METADATA_KEY])
+ servicer_context.send_initial_metadata((initial_metadatum,))
+ if _TRAILING_METADATA_KEY in invocation_metadata:
+ trailing_metadatum = (
+ _TRAILING_METADATA_KEY, invocation_metadata[_TRAILING_METADATA_KEY])
+ servicer_context.set_trailing_metadata((trailing_metadatum,))
+
+def _maybe_echo_status_and_message(request, servicer_context):
+ """Sets the response context code and details if the request asks for them"""
+ if request.HasField('response_status'):
+ servicer_context.set_code(request.response_status.code)
+ servicer_context.set_details(request.response_status.message)
class TestService(test_pb2.TestServiceServicer):
def EmptyCall(self, request, context):
+ _maybe_echo_metadata(context)
return empty_pb2.Empty()
def UnaryCall(self, request, context):
- if request.HasField('response_status'):
- context.set_code(request.response_status.code)
- context.set_details(request.response_status.message)
+ _maybe_echo_metadata(context)
+ _maybe_echo_status_and_message(request, context)
return messages_pb2.SimpleResponse(
payload=messages_pb2.Payload(
type=messages_pb2.COMPRESSABLE,
body=b'\x00' * request.response_size))
def StreamingOutputCall(self, request, context):
- if request.HasField('response_status'):
- context.set_code(request.response_status.code)
- context.set_details(request.response_status.message)
+ _maybe_echo_status_and_message(request, context)
for response_parameters in request.response_parameters:
yield messages_pb2.StreamingOutputCallResponse(
payload=messages_pb2.Payload(
@@ -77,10 +95,9 @@ class TestService(test_pb2.TestServiceServicer):
aggregated_payload_size=aggregate_size)
def FullDuplexCall(self, request_iterator, context):
+ _maybe_echo_metadata(context)
for request in request_iterator:
- if request.HasField('response_status'):
- context.set_code(request.response_status.code)
- context.set_details(request.response_status.message)
+ _maybe_echo_status_and_message(request, context)
for response_parameters in request.response_parameters:
yield messages_pb2.StreamingOutputCallResponse(
payload=messages_pb2.Payload(
@@ -93,23 +110,46 @@ class TestService(test_pb2.TestServiceServicer):
return self.FullDuplexCall(request_iterator, context)
+def _expect_status_code(call, expected_code):
+ if call.code() != expected_code:
+ raise ValueError(
+ 'expected code %s, got %s' % (expected_code, call.code()))
+
+
+def _expect_status_details(call, expected_details):
+ if call.details() != expected_details:
+ raise ValueError(
+ 'expected message %s, got %s' % (expected_details, call.details()))
+
+
+def _validate_status_code_and_details(call, expected_code, expected_details):
+ _expect_status_code(call, expected_code)
+ _expect_status_details(call, expected_details)
+
+
+def _validate_payload_type_and_length(response, expected_type, expected_length):
+ if response.payload.type is not expected_type:
+ raise ValueError(
+ 'expected payload type %s, got %s' %
+ (expected_type, type(response.payload.type)))
+ elif len(response.payload.body) != expected_length:
+ raise ValueError(
+ 'expected payload body size %d, got %d' %
+ (expected_length, len(response.payload.body)))
+
+
def _large_unary_common_behavior(
stub, fill_username, fill_oauth_scope, call_credentials):
+ size = 314159
request = messages_pb2.SimpleRequest(
- response_type=messages_pb2.COMPRESSABLE, response_size=314159,
+ response_type=messages_pb2.COMPRESSABLE, response_size=size,
payload=messages_pb2.Payload(body=b'\x00' * 271828),
fill_username=fill_username, fill_oauth_scope=fill_oauth_scope)
response_future = stub.UnaryCall.future(
request, credentials=call_credentials)
response = response_future.result()
- if response.payload.type is not messages_pb2.COMPRESSABLE:
- raise ValueError(
- 'response payload type is "%s"!' % type(response.payload.type))
- elif len(response.payload.body) != 314159:
- raise ValueError(
- 'response body of incorrect size %d!' % len(response.payload.body))
- else:
- return response
+ _validate_payload_type_and_length(response, messages_pb2.COMPRESSABLE, size)
+ return response
def _empty_unary(stub):
@@ -151,12 +191,9 @@ def _server_streaming(stub):
)
response_iterator = stub.StreamingOutputCall(request)
for index, response in enumerate(response_iterator):
- if response.payload.type != messages_pb2.COMPRESSABLE:
- raise ValueError(
- 'response body of invalid type %s!' % response.payload.type)
- elif len(response.payload.body) != sizes[index]:
- raise ValueError(
- 'response body of invalid size %d!' % len(response.payload.body))
+ _validate_payload_type_and_length(
+ response, messages_pb2.COMPRESSABLE, sizes[index])
+
class _Pipe(object):
@@ -213,12 +250,8 @@ def _ping_pong(stub):
payload=messages_pb2.Payload(body=b'\x00' * payload_size))
pipe.add(request)
response = next(response_iterator)
- if response.payload.type != messages_pb2.COMPRESSABLE:
- raise ValueError(
- 'response body of invalid type %s!' % response.payload.type)
- if len(response.payload.body) != response_size:
- raise ValueError(
- 'response body of invalid size %d!' % len(response.payload.body))
+ _validate_payload_type_and_length(
+ response, messages_pb2.COMPRESSABLE, response_size)
def _cancel_after_begin(stub):
@@ -289,36 +322,84 @@ def _empty_stream(stub):
def _status_code_and_message(stub):
- message = 'test status message'
+ details = 'test status message'
code = 2
status = grpc.StatusCode.UNKNOWN # code = 2
+
+ # Test with a UnaryCall
request = messages_pb2.SimpleRequest(
response_type=messages_pb2.COMPRESSABLE,
response_size=1,
payload=messages_pb2.Payload(body=b'\x00'),
- response_status=messages_pb2.EchoStatus(code=code, message=message)
+ response_status=messages_pb2.EchoStatus(code=code, message=details)
)
response_future = stub.UnaryCall.future(request)
- if response_future.code() != status:
- raise ValueError(
- 'expected code %s, got %s' % (status, response_future.code()))
- elif response_future.details() != message:
- raise ValueError(
- 'expected message %s, got %s' % (message, response_future.details()))
+ _validate_status_code_and_details(response_future, status, details)
- request = messages_pb2.StreamingOutputCallRequest(
+ # Test with a FullDuplexCall
+ with _Pipe() as pipe:
+ response_iterator = stub.FullDuplexCall(pipe)
+ request = messages_pb2.StreamingOutputCallRequest(
+ response_type=messages_pb2.COMPRESSABLE,
+ response_parameters=(
+ messages_pb2.ResponseParameters(size=1),),
+ payload=messages_pb2.Payload(body=b'\x00'),
+ response_status=messages_pb2.EchoStatus(code=code, message=details))
+ pipe.add(request) # sends the initial request.
+ # Dropping out of with block closes the pipe
+ _validate_status_code_and_details(response_iterator, status, details)
+
+
+def _unimplemented_method(test_service_stub):
+ response_future = (
+ test_service_stub.UnimplementedCall.future(empty_pb2.Empty()))
+ _expect_status_code(response_future, grpc.StatusCode.UNIMPLEMENTED)
+
+
+def _unimplemented_service(unimplemented_service_stub):
+ response_future = (
+ unimplemented_service_stub.UnimplementedCall.future(empty_pb2.Empty()))
+ _expect_status_code(response_future, grpc.StatusCode.UNIMPLEMENTED)
+
+
+def _custom_metadata(stub):
+ initial_metadata_value = "test_initial_metadata_value"
+ trailing_metadata_value = "\x0a\x0b\x0a\x0b\x0a\x0b"
+ metadata = (
+ (_INITIAL_METADATA_KEY, initial_metadata_value),
+ (_TRAILING_METADATA_KEY, trailing_metadata_value))
+
+ def _validate_metadata(response):
+ initial_metadata = dict(response.initial_metadata())
+ if initial_metadata[_INITIAL_METADATA_KEY] != initial_metadata_value:
+ raise ValueError(
+ 'expected initial metadata %s, got %s' % (
+ initial_metadata_value, initial_metadata[_INITIAL_METADATA_KEY]))
+ trailing_metadata = dict(response.trailing_metadata())
+ if trailing_metadata[_TRAILING_METADATA_KEY] != trailing_metadata_value:
+ raise ValueError(
+ 'expected trailing metadata %s, got %s' % (
+ trailing_metadata_value, initial_metadata[_TRAILING_METADATA_KEY]))
+
+ # Testing with UnaryCall
+ request = messages_pb2.SimpleRequest(
response_type=messages_pb2.COMPRESSABLE,
- response_parameters=(
- messages_pb2.ResponseParameters(size=1),),
- response_status=messages_pb2.EchoStatus(code=code, message=message))
- response_iterator = stub.StreamingOutputCall(request)
- if response_future.code() != status:
- raise ValueError(
- 'expected code %s, got %s' % (status, response_iterator.code()))
- elif response_future.details() != message:
- raise ValueError(
- 'expected message %s, got %s' % (message, response_iterator.details()))
+ response_size=1,
+ payload=messages_pb2.Payload(body=b'\x00'))
+ response_future = stub.UnaryCall.future(request, metadata=metadata)
+ _validate_metadata(response_future)
+ # Testing with FullDuplexCall
+ with _Pipe() as pipe:
+ response_iterator = stub.FullDuplexCall(pipe, metadata=metadata)
+ request = messages_pb2.StreamingOutputCallRequest(
+ response_type=messages_pb2.COMPRESSABLE,
+ response_parameters=(
+ messages_pb2.ResponseParameters(size=1),))
+ pipe.add(request) # Sends the request
+ next(response_iterator) # Causes server to send trailing metadata
+ # Dropping out of the with block closes the pipe
+ _validate_metadata(response_iterator)
def _compute_engine_creds(stub, args):
response = _large_unary_common_behavior(stub, True, True, None)
@@ -379,6 +460,9 @@ class TestCase(enum.Enum):
CANCEL_AFTER_FIRST_RESPONSE = 'cancel_after_first_response'
EMPTY_STREAM = 'empty_stream'
STATUS_CODE_AND_MESSAGE = 'status_code_and_message'
+ UNIMPLEMENTED_METHOD = 'unimplemented_method'
+ UNIMPLEMENTED_SERVICE = 'unimplemented_service'
+ CUSTOM_METADATA = "custom_metadata"
COMPUTE_ENGINE_CREDS = 'compute_engine_creds'
OAUTH2_AUTH_TOKEN = 'oauth2_auth_token'
JWT_TOKEN_CREDS = 'jwt_token_creds'
@@ -406,6 +490,12 @@ class TestCase(enum.Enum):
_empty_stream(stub)
elif self is TestCase.STATUS_CODE_AND_MESSAGE:
_status_code_and_message(stub)
+ elif self is TestCase.UNIMPLEMENTED_METHOD:
+ _unimplemented_method(stub)
+ elif self is TestCase.UNIMPLEMENTED_SERVICE:
+ _unimplemented_service(stub)
+ elif self is TestCase.CUSTOM_METADATA:
+ _custom_metadata(stub)
elif self is TestCase.COMPUTE_ENGINE_CREDS:
_compute_engine_creds(stub, args)
elif self is TestCase.OAUTH2_AUTH_TOKEN:
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..43d6c971b5
--- /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(iter(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.assertSequenceEqual(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(iter(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.assertSequenceEqual(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(iter(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.assertSequenceEqual(expected_responses, responses)
+
+ def testListServices(self):
+ requests = (
+ reflection_pb2.ServerReflectionRequest(
+ list_services='',
+ ),
+ )
+ responses = tuple(self._stub.ServerReflectionInfo(iter(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.assertSequenceEqual(expected_responses, responses)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/stress/client.py b/src/python/grpcio_tests/tests/stress/client.py
index 975f33b4c1..390ea13021 100644
--- a/src/python/grpcio_tests/tests/stress/client.py
+++ b/src/python/grpcio_tests/tests/stress/client.py
@@ -39,6 +39,7 @@ from src.proto.grpc.testing import metrics_pb2
from src.proto.grpc.testing import test_pb2
from tests.interop import methods
+from tests.interop import resources
from tests.qps import histogram
from tests.stress import metrics_server
from tests.stress import test_runner
@@ -71,6 +72,16 @@ def _args():
'--metrics_port',
help='the port to listen for metrics requests on',
default=8081, type=int)
+ parser.add_argument(
+ '--use_test_ca',
+ help='Whether to use our fake CA. Requires --use_tls=true',
+ default=False, type=bool)
+ parser.add_argument(
+ '--use_tls',
+ help='Whether to use TLS', default=False, type=bool)
+ parser.add_argument(
+ '--server_host_override', default="foo.test.google.fr",
+ help='the server host to which to claim to connect', type=str)
return parser.parse_args()
@@ -90,6 +101,19 @@ def _parse_weighted_test_cases(test_case_args):
weighted_test_cases[test_case] = int(weight)
return weighted_test_cases
+def _get_channel(target, args):
+ if args.use_tls:
+ if args.use_test_ca:
+ root_certificates = resources.test_root_certificates()
+ else:
+ root_certificates = None # will load default roots.
+ channel_credentials = grpc.ssl_channel_credentials(
+ root_certificates=root_certificates)
+ options = (('grpc.ssl_target_name_override', args.server_host_override,),)
+ return grpc.secure_channel(
+ target, channel_credentials, options=options)
+ else:
+ return grpc.insecure_channel(target)
def run_test(args):
test_cases = _parse_weighted_test_cases(args.test_cases)
@@ -108,7 +132,7 @@ def run_test(args):
for test_server_target in test_server_targets:
for _ in xrange(args.num_channels_per_server):
- channel = grpc.insecure_channel(test_server_target)
+ channel = _get_channel(test_server_target, args)
for _ in xrange(args.num_stubs_per_channel):
stub = test_pb2.TestServiceStub(channel)
runner = test_runner.TestRunner(stub, test_cases, hist,
diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json
index d47631cf75..70d965d3ca 100644
--- a/src/python/grpcio_tests/tests/tests.json
+++ b/src/python/grpcio_tests/tests/tests.json
@@ -1,49 +1,51 @@
[
- "_api_test.AllTest",
- "_api_test.ChannelConnectivityTest",
- "_api_test.ChannelTest",
- "_auth_test.AccessTokenCallCredentialsTest",
- "_auth_test.GoogleCallCredentialsTest",
- "_beta_features_test.BetaFeaturesTest",
- "_beta_features_test.ContextManagementAndLifecycleTest",
- "_cancel_many_calls_test.CancelManyCallsTest",
- "_channel_connectivity_test.ChannelConnectivityTest",
- "_channel_ready_future_test.ChannelReadyFutureTest",
- "_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.MultiCallableInvokerFutureInvocationAsynchronousEventServiceTest",
- "_health_servicer_test.HealthServicerTest",
- "_implementations_test.CallCredentialsTest",
- "_implementations_test.ChannelCredentialsTest",
- "_insecure_interop_test.InsecureInteropTest",
- "_invalid_metadata_test.InvalidMetadataTest",
- "_invocation_defects_test.InvocationDefectsTest",
- "_logging_pool_test.LoggingPoolTest",
- "_metadata_code_details_test.MetadataCodeDetailsTest",
- "_metadata_test.MetadataTest",
- "_not_found_test.NotFoundTest",
- "_python_plugin_test.PythonPluginTest",
- "_read_some_but_not_all_responses_test.ReadSomeButNotAllResponsesTest",
- "_rpc_test.RPCTest",
- "_sanity_test.Sanity",
- "_secure_interop_test.SecureInteropTest",
- "_split_definitions_test.SameCommonTest",
- "_split_definitions_test.SameSeparateTest",
- "_split_definitions_test.SplitCommonTest",
- "_split_definitions_test.SplitSeparateTest",
- "_thread_cleanup_test.CleanupThreadTest",
- "_utilities_test.ChannelConnectivityTest",
- "beta_python_plugin_test.PythonPluginTest",
- "cygrpc_test.InsecureServerInsecureClient",
- "cygrpc_test.SecureServerSecureClient",
- "cygrpc_test.TypeSmokeTest"
+ "health_check._health_servicer_test.HealthServicerTest",
+ "interop._insecure_intraop_test.InsecureIntraopTest",
+ "interop._secure_intraop_test.SecureIntraopTest",
+ "protoc_plugin._python_plugin_test.PythonPluginTest",
+ "protoc_plugin._split_definitions_test.SameCommonTest",
+ "protoc_plugin._split_definitions_test.SameSeparateTest",
+ "protoc_plugin._split_definitions_test.SplitCommonTest",
+ "protoc_plugin._split_definitions_test.SplitSeparateTest",
+ "protoc_plugin.beta_python_plugin_test.PythonPluginTest",
+ "reflection._reflection_servicer_test.ReflectionServicerTest",
+ "unit._api_test.AllTest",
+ "unit._api_test.ChannelConnectivityTest",
+ "unit._api_test.ChannelTest",
+ "unit._auth_test.AccessTokenCallCredentialsTest",
+ "unit._auth_test.GoogleCallCredentialsTest",
+ "unit._channel_args_test.ChannelArgsTest",
+ "unit._channel_connectivity_test.ChannelConnectivityTest",
+ "unit._channel_ready_future_test.ChannelReadyFutureTest",
+ "unit._compression_test.CompressionTest",
+ "unit._credentials_test.CredentialsTest",
+ "unit._cython._cancel_many_calls_test.CancelManyCallsTest",
+ "unit._cython._channel_test.ChannelTest",
+ "unit._cython._read_some_but_not_all_responses_test.ReadSomeButNotAllResponsesTest",
+ "unit._cython.cygrpc_test.InsecureServerInsecureClient",
+ "unit._cython.cygrpc_test.SecureServerSecureClient",
+ "unit._cython.cygrpc_test.TypeSmokeTest",
+ "unit._empty_message_test.EmptyMessageTest",
+ "unit._exit_test.ExitTest",
+ "unit._invalid_metadata_test.InvalidMetadataTest",
+ "unit._invocation_defects_test.InvocationDefectsTest",
+ "unit._metadata_code_details_test.MetadataCodeDetailsTest",
+ "unit._metadata_test.MetadataTest",
+ "unit._rpc_test.RPCTest",
+ "unit._sanity._sanity_test.Sanity",
+ "unit._thread_cleanup_test.CleanupThreadTest",
+ "unit.beta._beta_features_test.BetaFeaturesTest",
+ "unit.beta._beta_features_test.ContextManagementAndLifecycleTest",
+ "unit.beta._connectivity_channel_test.ConnectivityStatesTest",
+ "unit.beta._face_interface_test.DynamicInvokerBlockingInvocationInlineServiceTest",
+ "unit.beta._face_interface_test.DynamicInvokerFutureInvocationAsynchronousEventServiceTest",
+ "unit.beta._face_interface_test.GenericInvokerBlockingInvocationInlineServiceTest",
+ "unit.beta._face_interface_test.GenericInvokerFutureInvocationAsynchronousEventServiceTest",
+ "unit.beta._face_interface_test.MultiCallableInvokerBlockingInvocationInlineServiceTest",
+ "unit.beta._face_interface_test.MultiCallableInvokerFutureInvocationAsynchronousEventServiceTest",
+ "unit.beta._implementations_test.CallCredentialsTest",
+ "unit.beta._implementations_test.ChannelCredentialsTest",
+ "unit.beta._not_found_test.NotFoundTest",
+ "unit.beta._utilities_test.ChannelConnectivityTest",
+ "unit.framework.foundation._logging_pool_test.LoggingPoolTest"
]
diff --git a/src/python/grpcio_tests/tests/unit/_api_test.py b/src/python/grpcio_tests/tests/unit/_api_test.py
index 2fe89499f5..51dc425420 100644
--- a/src/python/grpcio_tests/tests/unit/_api_test.py
+++ b/src/python/grpcio_tests/tests/unit/_api_test.py
@@ -65,6 +65,7 @@ class AllTest(unittest.TestCase):
'RpcMethodHandler',
'HandlerCallDetails',
'GenericRpcHandler',
+ 'ServiceRpcHandler',
'Server',
'unary_unary_rpc_method_handler',
'unary_stream_rpc_method_handler',
diff --git a/src/python/grpcio_tests/tests/unit/_channel_args_test.py b/src/python/grpcio_tests/tests/unit/_channel_args_test.py
new file mode 100644
index 0000000000..b46497afd6
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_channel_args_test.py
@@ -0,0 +1,60 @@
+# 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 Channel Args on client/server side."""
+
+import unittest
+
+import grpc
+
+class TestPointerWrapper(object):
+
+ def __int__(self):
+ return 123456
+
+
+TEST_CHANNEL_ARGS = (
+ ('arg1', b'bytes_val'),
+ ('arg2', 'str_val'),
+ ('arg3', 1),
+ (b'arg4', 'str_val'),
+ ('arg6', TestPointerWrapper()),
+)
+
+
+class ChannelArgsTest(unittest.TestCase):
+
+ def test_client(self):
+ grpc.insecure_channel('localhost:8080', options=TEST_CHANNEL_ARGS)
+
+ def test_server(self):
+ grpc.server(None, options=TEST_CHANNEL_ARGS)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py b/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py
index 3c00f686ce..3d9dd17ff6 100644
--- a/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py
+++ b/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py
@@ -32,12 +32,10 @@
import threading
import time
import unittest
-from concurrent import futures
import grpc
-from grpc import _channel
-from grpc import _server
from tests.unit.framework.common import test_constants
+from tests.unit import _thread_pool
def _ready_in_connectivities(connectivities):
@@ -78,7 +76,7 @@ class ChannelConnectivityTest(unittest.TestCase):
def test_lonely_channel_connectivity(self):
callback = _Callback()
- channel = _channel.Channel('localhost:12345', None, None)
+ channel = grpc.insecure_channel('localhost:12345')
channel.subscribe(callback.update, try_to_connect=False)
first_connectivities = callback.block_until_connectivities_satisfy(bool)
channel.subscribe(callback.update, try_to_connect=True)
@@ -104,13 +102,14 @@ class ChannelConnectivityTest(unittest.TestCase):
grpc.ChannelConnectivity.READY, fifth_connectivities)
def test_immediately_connectable_channel_connectivity(self):
- server = _server.Server(futures.ThreadPoolExecutor(max_workers=0), ())
+ thread_pool = _thread_pool.RecordingThreadPool(max_workers=None)
+ server = grpc.server(thread_pool)
port = server.add_insecure_port('[::]:0')
server.start()
first_callback = _Callback()
second_callback = _Callback()
- channel = _channel.Channel('localhost:{}'.format(port), None, None)
+ channel = grpc.insecure_channel('localhost:{}'.format(port))
channel.subscribe(first_callback.update, try_to_connect=False)
first_connectivities = first_callback.block_until_connectivities_satisfy(
bool)
@@ -141,20 +140,23 @@ class ChannelConnectivityTest(unittest.TestCase):
fourth_connectivities)
self.assertNotIn(
grpc.ChannelConnectivity.SHUTDOWN, fourth_connectivities)
+ self.assertFalse(thread_pool.was_used())
def test_reachable_then_unreachable_channel_connectivity(self):
- server = _server.Server(futures.ThreadPoolExecutor(max_workers=0), ())
+ thread_pool = _thread_pool.RecordingThreadPool(max_workers=None)
+ server = grpc.server(thread_pool)
port = server.add_insecure_port('[::]:0')
server.start()
callback = _Callback()
- channel = _channel.Channel('localhost:{}'.format(port), None, None)
+ channel = grpc.insecure_channel('localhost:{}'.format(port))
channel.subscribe(callback.update, try_to_connect=True)
callback.block_until_connectivities_satisfy(_ready_in_connectivities)
# Now take down the server and confirm that channel readiness is repudiated.
server.stop(None)
callback.block_until_connectivities_satisfy(_last_connectivity_is_not_ready)
channel.unsubscribe(callback.update)
+ self.assertFalse(thread_pool.was_used())
if __name__ == '__main__':
diff --git a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
index 20ba13fc4e..46a964db8c 100644
--- a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
+++ b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
@@ -31,12 +31,10 @@
import threading
import unittest
-from concurrent import futures
import grpc
-from grpc import _channel
-from grpc import _server
from tests.unit.framework.common import test_constants
+from tests.unit import _thread_pool
class _Callback(object):
@@ -78,7 +76,8 @@ class ChannelReadyFutureTest(unittest.TestCase):
self.assertFalse(ready_future.running())
def test_immediately_connectable_channel_connectivity(self):
- server = _server.Server(futures.ThreadPoolExecutor(max_workers=0), ())
+ thread_pool = _thread_pool.RecordingThreadPool(max_workers=None)
+ server = grpc.server(thread_pool)
port = server.add_insecure_port('[::]:0')
server.start()
channel = grpc.insecure_channel('localhost:{}'.format(port))
@@ -97,6 +96,7 @@ class ChannelReadyFutureTest(unittest.TestCase):
self.assertFalse(ready_future.cancelled())
self.assertTrue(ready_future.done())
self.assertFalse(ready_future.running())
+ self.assertFalse(thread_pool.was_used())
if __name__ == '__main__':
diff --git a/src/python/grpcio_tests/tests/unit/_cython/_cancel_many_calls_test.py b/src/python/grpcio_tests/tests/unit/_cython/_cancel_many_calls_test.py
index cf212c5653..20115fb22c 100644
--- a/src/python/grpcio_tests/tests/unit/_cython/_cancel_many_calls_test.py
+++ b/src/python/grpcio_tests/tests/unit/_cython/_cancel_many_calls_test.py
@@ -157,11 +157,12 @@ class CancelManyCallsTest(unittest.TestCase):
server_thread_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
server_completion_queue = cygrpc.CompletionQueue()
- server = cygrpc.Server()
+ server = cygrpc.Server(cygrpc.ChannelArgs([]))
server.register_completion_queue(server_completion_queue)
port = server.add_http2_port(b'[::]:0')
server.start()
- channel = cygrpc.Channel('localhost:{}'.format(port).encode())
+ channel = cygrpc.Channel('localhost:{}'.format(port).encode(),
+ cygrpc.ChannelArgs([]))
state = _State()
diff --git a/src/python/grpcio_tests/tests/unit/_cython/_read_some_but_not_all_responses_test.py b/src/python/grpcio_tests/tests/unit/_cython/_read_some_but_not_all_responses_test.py
index 152d8edde3..2ae5285232 100644
--- a/src/python/grpcio_tests/tests/unit/_cython/_read_some_but_not_all_responses_test.py
+++ b/src/python/grpcio_tests/tests/unit/_cython/_read_some_but_not_all_responses_test.py
@@ -124,11 +124,12 @@ class ReadSomeButNotAllResponsesTest(unittest.TestCase):
def testReadSomeButNotAllResponses(self):
server_completion_queue = cygrpc.CompletionQueue()
- server = cygrpc.Server()
+ server = cygrpc.Server(cygrpc.ChannelArgs([]))
server.register_completion_queue(server_completion_queue)
port = server.add_http2_port(b'[::]:0')
server.start()
- channel = cygrpc.Channel('localhost:{}'.format(port).encode())
+ channel = cygrpc.Channel('localhost:{}'.format(port).encode(),
+ cygrpc.ChannelArgs([]))
server_shutdown_tag = 'server_shutdown_tag'
server_driver = _ServerDriver(server_completion_queue, server_shutdown_tag)
diff --git a/src/python/grpcio_tests/tests/unit/_cython/cygrpc_test.py b/src/python/grpcio_tests/tests/unit/_cython/cygrpc_test.py
index f9a8e2401b..8dedebfabe 100644
--- a/src/python/grpcio_tests/tests/unit/_cython/cygrpc_test.py
+++ b/src/python/grpcio_tests/tests/unit/_cython/cygrpc_test.py
@@ -30,6 +30,7 @@
import time
import threading
import unittest
+import platform
from grpc._cython import cygrpc
from tests.unit._cython import test_utilities
@@ -120,7 +121,7 @@ class TypeSmokeTest(unittest.TestCase):
del call_credentials
def testServerStartNoExplicitShutdown(self):
- server = cygrpc.Server()
+ server = cygrpc.Server(cygrpc.ChannelArgs([]))
completion_queue = cygrpc.CompletionQueue()
server.register_completion_queue(completion_queue)
port = server.add_http2_port(b'[::]:0')
@@ -130,7 +131,7 @@ class TypeSmokeTest(unittest.TestCase):
def testServerStartShutdown(self):
completion_queue = cygrpc.CompletionQueue()
- server = cygrpc.Server()
+ server = cygrpc.Server(cygrpc.ChannelArgs([]))
server.add_http2_port(b'[::]:0')
server.register_completion_queue(completion_queue)
server.start()
@@ -147,7 +148,7 @@ class ServerClientMixin(object):
def setUpMixin(self, server_credentials, client_credentials, host_override):
self.server_completion_queue = cygrpc.CompletionQueue()
- self.server = cygrpc.Server()
+ self.server = cygrpc.Server(cygrpc.ChannelArgs([]))
self.server.register_completion_queue(self.server_completion_queue)
if server_credentials:
self.port = self.server.add_http2_port(b'[::]:0', server_credentials)
@@ -163,7 +164,8 @@ class ServerClientMixin(object):
'localhost:{}'.format(self.port).encode(), client_channel_arguments,
client_credentials)
else:
- self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port).encode())
+ self.client_channel = cygrpc.Channel(
+ 'localhost:{}'.format(self.port).encode(), cygrpc.ChannelArgs([]))
if host_override:
self.host_argument = None # default host
self.expected_host = host_override
diff --git a/src/python/grpcio_tests/tests/unit/_exit_test.py b/src/python/grpcio_tests/tests/unit/_exit_test.py
index b0d6af73e5..5a4a32887c 100644
--- a/src/python/grpcio_tests/tests/unit/_exit_test.py
+++ b/src/python/grpcio_tests/tests/unit/_exit_test.py
@@ -84,6 +84,7 @@ def wait(process):
process.wait()
+@unittest.skip('https://github.com/grpc/grpc/issues/7311')
class ExitTest(unittest.TestCase):
def test_unstarted_server(self):
diff --git a/src/python/grpcio_tests/tests/unit/_rpc_test.py b/src/python/grpcio_tests/tests/unit/_rpc_test.py
index 59bf240d28..eb00156da5 100644
--- a/src/python/grpcio_tests/tests/unit/_rpc_test.py
+++ b/src/python/grpcio_tests/tests/unit/_rpc_test.py
@@ -191,6 +191,10 @@ class RPCTest(unittest.TestCase):
self._channel = grpc.insecure_channel('localhost:%d' % port)
+ def tearDown(self):
+ self._server.stop(None)
+ self._server_pool.shutdown(wait=True)
+
def testUnrecognizedMethod(self):
request = b'abc'
@@ -233,7 +237,11 @@ class RPCTest(unittest.TestCase):
('test', 'SuccessfulUnaryRequestFutureUnaryResponse'),))
response = response_future.result()
+ self.assertIsInstance(response_future, grpc.Future)
+ self.assertIsInstance(response_future, grpc.Call)
self.assertEqual(expected_response, response)
+ self.assertIsNone(response_future.exception())
+ self.assertIsNone(response_future.traceback())
def testSuccessfulUnaryRequestStreamResponse(self):
request = b'\x37\x58'
@@ -287,6 +295,8 @@ class RPCTest(unittest.TestCase):
response = response_future.result()
self.assertEqual(expected_response, response)
+ self.assertIsNone(response_future.exception())
+ self.assertIsNone(response_future.traceback())
def testSuccessfulStreamRequestStreamResponse(self):
requests = tuple(b'\x77\x58' for _ in range(test_constants.STREAM_LENGTH))
@@ -459,6 +469,10 @@ class RPCTest(unittest.TestCase):
self.assertTrue(response_future.cancelled())
with self.assertRaises(grpc.FutureCancelledError):
response_future.result()
+ with self.assertRaises(grpc.FutureCancelledError):
+ response_future.exception()
+ with self.assertRaises(grpc.FutureCancelledError):
+ response_future.traceback()
self.assertIs(grpc.StatusCode.CANCELLED, response_future.code())
def testCancelledUnaryRequestStreamResponse(self):
@@ -495,6 +509,10 @@ class RPCTest(unittest.TestCase):
self.assertTrue(response_future.cancelled())
with self.assertRaises(grpc.FutureCancelledError):
response_future.result()
+ with self.assertRaises(grpc.FutureCancelledError):
+ response_future.exception()
+ with self.assertRaises(grpc.FutureCancelledError):
+ response_future.traceback()
self.assertIsNotNone(response_future.initial_metadata())
self.assertIs(grpc.StatusCode.CANCELLED, response_future.code())
self.assertIsNotNone(response_future.details())
@@ -528,6 +546,7 @@ class RPCTest(unittest.TestCase):
request, timeout=test_constants.SHORT_TIMEOUT,
metadata=(('test', 'ExpiredUnaryRequestBlockingUnaryResponse'),))
+ self.assertIsInstance(exception_context.exception, grpc.Call)
self.assertIsNotNone(exception_context.exception.initial_metadata())
self.assertIs(
grpc.StatusCode.DEADLINE_EXCEEDED, exception_context.exception.code())
@@ -556,6 +575,7 @@ class RPCTest(unittest.TestCase):
self.assertIs(
grpc.StatusCode.DEADLINE_EXCEEDED, exception_context.exception.code())
self.assertIsInstance(response_future.exception(), grpc.RpcError)
+ self.assertIsNotNone(response_future.traceback())
self.assertIs(
grpc.StatusCode.DEADLINE_EXCEEDED, response_future.exception().code())
@@ -585,6 +605,8 @@ class RPCTest(unittest.TestCase):
request_iterator, timeout=test_constants.SHORT_TIMEOUT,
metadata=(('test', 'ExpiredStreamRequestBlockingUnaryResponse'),))
+ self.assertIsInstance(exception_context.exception, grpc.RpcError)
+ self.assertIsInstance(exception_context.exception, grpc.Call)
self.assertIsNotNone(exception_context.exception.initial_metadata())
self.assertIs(
grpc.StatusCode.DEADLINE_EXCEEDED, exception_context.exception.code())
@@ -601,6 +623,8 @@ class RPCTest(unittest.TestCase):
response_future = multi_callable.future(
request_iterator, timeout=test_constants.SHORT_TIMEOUT,
metadata=(('test', 'ExpiredStreamRequestFutureUnaryResponse'),))
+ with self.assertRaises(grpc.FutureTimeoutError):
+ response_future.result(timeout=test_constants.SHORT_TIMEOUT / 2.0)
response_future.add_done_callback(callback)
value_passed_to_callback = callback.value()
@@ -610,6 +634,7 @@ class RPCTest(unittest.TestCase):
self.assertIs(
grpc.StatusCode.DEADLINE_EXCEEDED, exception_context.exception.code())
self.assertIsInstance(response_future.exception(), grpc.RpcError)
+ self.assertIsNotNone(response_future.traceback())
self.assertIs(response_future, value_passed_to_callback)
self.assertIsNotNone(response_future.initial_metadata())
self.assertIs(grpc.StatusCode.DEADLINE_EXCEEDED, response_future.code())
@@ -656,11 +681,14 @@ class RPCTest(unittest.TestCase):
response_future.add_done_callback(callback)
value_passed_to_callback = callback.value()
+ self.assertIsInstance(response_future, grpc.Future)
+ self.assertIsInstance(response_future, grpc.Call)
with self.assertRaises(grpc.RpcError) as exception_context:
response_future.result()
self.assertIs(
grpc.StatusCode.UNKNOWN, exception_context.exception.code())
self.assertIsInstance(response_future.exception(), grpc.RpcError)
+ self.assertIsNotNone(response_future.traceback())
self.assertIs(grpc.StatusCode.UNKNOWN, response_future.exception().code())
self.assertIs(response_future, value_passed_to_callback)
@@ -709,6 +737,7 @@ class RPCTest(unittest.TestCase):
self.assertIs(
grpc.StatusCode.UNKNOWN, exception_context.exception.code())
self.assertIsInstance(response_future.exception(), grpc.RpcError)
+ self.assertIsNotNone(response_future.traceback())
self.assertIs(response_future, value_passed_to_callback)
def testFailedStreamRequestStreamResponse(self):
diff --git a/src/python/grpcio_tests/tests/unit/_thread_pool.py b/src/python/grpcio_tests/tests/unit/_thread_pool.py
new file mode 100644
index 0000000000..f13cc2f86f
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_thread_pool.py
@@ -0,0 +1,48 @@
+# 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 threading
+from concurrent import futures
+
+
+class RecordingThreadPool(futures.Executor):
+ """A thread pool that records if used."""
+ def __init__(self, max_workers):
+ self._tp_executor = futures.ThreadPoolExecutor(max_workers=max_workers)
+ self._lock = threading.Lock()
+ self._was_used = False
+
+ def submit(self, fn, *args, **kwargs):
+ with self._lock:
+ self._was_used = True
+ self._tp_executor.submit(fn, *args, **kwargs)
+
+ def was_used(self):
+ with self._lock:
+ return self._was_used