mirror of https://github.com/grpc/grpc.git
Merge pull request #2243 from soltanmm/cycy
Cython refactor of Python C wrapping layerpull/2362/head
commit
f260abdd98
34 changed files with 2997 additions and 49 deletions
@ -1,3 +1,4 @@ |
||||
enum34==1.0.4 |
||||
futures==2.2.0 |
||||
protobuf==3.0.0a3 |
||||
cython>=0.22 |
||||
|
@ -1,3 +1,4 @@ |
||||
MANIFEST |
||||
grpcio.egg-info/ |
||||
build/ |
||||
dist/ |
||||
|
@ -0,0 +1,5 @@ |
||||
*.a |
||||
*.so |
||||
*.dll |
||||
*.pyc |
||||
*.pyd |
@ -0,0 +1,7 @@ |
||||
*.h |
||||
*.c |
||||
*.a |
||||
*.so |
||||
*.dll |
||||
*.pyc |
||||
*.pyd |
@ -0,0 +1,52 @@ |
||||
GRPC Python Cython layer |
||||
======================== |
||||
|
||||
Package for the GRPC Python Cython layer. |
||||
|
||||
What is Cython? |
||||
--------------- |
||||
|
||||
Cython is both a superset of the Python language with extensions for dealing |
||||
with C types and a tool that transpiles this superset into C code. It provides |
||||
convenient means of statically typing expressions and of converting Python |
||||
strings to pointers (among other niceties), thus dramatically smoothing the |
||||
Python/C interop by allowing fluid use of APIs in both from the same source. |
||||
See the wonderful `Cython website`_. |
||||
|
||||
Why Cython? |
||||
----------- |
||||
|
||||
- **Python 2 and 3 support** |
||||
Cython generated C code has precompiler macros to target both Python 2 and |
||||
Python 3 C APIs, even while acting as a superset of just the Python 2 |
||||
language (e.g. using ``basestring``). |
||||
- **Significantly less semantic noise** |
||||
A lot of CPython code is just glue, especially human-error-prone |
||||
``Py_INCREF``-ing and ``Py_DECREF``-ing around error handlers and such. |
||||
Cython takes care of that automagically. |
||||
- **Possible PyPy support** |
||||
One of the major developments in Cython over the past few years was the |
||||
addition of support for PyPy. We might soon be able to provide such support |
||||
ourselves through our use of Cython. |
||||
- **Less Python glue code** |
||||
There existed several adapter layers in and around the original CPython code |
||||
to smooth the surface exposed to Python due to how much trouble it was to |
||||
make such a smooth surface via the CPython API alone. Cython makes writing |
||||
such a surface incredibly easy, so these adapter layers may be removed. |
||||
|
||||
Implications for Users |
||||
---------------------- |
||||
|
||||
Nothing additional will be required for users. PyPI packages will contain |
||||
Cython generated C code and thus not necessitate a Cython installation. |
||||
|
||||
Implications for GRPC Developers |
||||
-------------------------------- |
||||
|
||||
A typical edit-compile-debug cycle now requires Cython. We install Cython in |
||||
the ``virtualenv`` generated for the Python tests in this repository, so |
||||
initial test runs may take an extra 2+ minutes to complete. Subsequent test |
||||
runs won't reinstall ``Cython`` (unless required versions change and the |
||||
``virtualenv`` doesn't have installed versions that satisfy the change). |
||||
|
||||
.. _`Cython website`: http://cython.org/ |
@ -0,0 +1,28 @@ |
||||
# Copyright 2015, 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. |
@ -0,0 +1,28 @@ |
||||
# Copyright 2015, 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. |
@ -0,0 +1,37 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
from grpc._cython._cygrpc cimport grpc |
||||
|
||||
|
||||
cdef class Call: |
||||
|
||||
cdef grpc.grpc_call *c_call |
||||
cdef list references |
||||
|
@ -0,0 +1,82 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
cimport cpython |
||||
|
||||
from grpc._cython._cygrpc cimport records |
||||
|
||||
|
||||
cdef class Call: |
||||
|
||||
def __cinit__(self): |
||||
# Create an *empty* call |
||||
self.c_call = NULL |
||||
self.references = [] |
||||
|
||||
def start_batch(self, operations, tag): |
||||
if not self.is_valid: |
||||
raise ValueError("invalid call object cannot be used from Python") |
||||
cdef records.Operations cy_operations = records.Operations(operations) |
||||
cdef records.OperationTag operation_tag = records.OperationTag(tag) |
||||
operation_tag.operation_call = self |
||||
operation_tag.batch_operations = cy_operations |
||||
cpython.Py_INCREF(operation_tag) |
||||
return grpc.grpc_call_start_batch( |
||||
self.c_call, cy_operations.c_ops, cy_operations.c_nops, |
||||
<cpython.PyObject *>operation_tag) |
||||
|
||||
def cancel(self, |
||||
grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE, |
||||
details=None): |
||||
if not self.is_valid: |
||||
raise ValueError("invalid call object cannot be used from Python") |
||||
if (details is None) != (error_code == grpc.GRPC_STATUS__DO_NOT_USE): |
||||
raise ValueError("if error_code is specified, so must details " |
||||
"(and vice-versa)") |
||||
if isinstance(details, bytes): |
||||
pass |
||||
elif isinstance(details, basestring): |
||||
details = details.encode() |
||||
else: |
||||
raise TypeError("expected details to be str or bytes") |
||||
if error_code != grpc.GRPC_STATUS__DO_NOT_USE: |
||||
self.references.append(details) |
||||
return grpc.grpc_call_cancel_with_status(self.c_call, error_code, details) |
||||
else: |
||||
return grpc.grpc_call_cancel(self.c_call) |
||||
|
||||
def __dealloc__(self): |
||||
if self.c_call != NULL: |
||||
grpc.grpc_call_destroy(self.c_call) |
||||
|
||||
# The object *should* always be valid from Python. Used for debugging. |
||||
@property |
||||
def is_valid(self): |
||||
return self.c_call != NULL |
||||
|
@ -0,0 +1,36 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
from grpc._cython._cygrpc cimport grpc |
||||
|
||||
|
||||
cdef class Channel: |
||||
|
||||
cdef grpc.grpc_channel *c_channel |
||||
cdef list references |
@ -0,0 +1,84 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
from grpc._cython._cygrpc cimport call |
||||
from grpc._cython._cygrpc cimport completion_queue |
||||
from grpc._cython._cygrpc cimport credentials |
||||
from grpc._cython._cygrpc cimport records |
||||
|
||||
|
||||
cdef class Channel: |
||||
|
||||
def __cinit__(self, target, records.ChannelArgs arguments=None, |
||||
credentials.ClientCredentials client_credentials=None): |
||||
cdef grpc.grpc_channel_args *c_arguments = NULL |
||||
self.c_channel = NULL |
||||
self.references = [] |
||||
if arguments is not None: |
||||
c_arguments = &arguments.c_args |
||||
if isinstance(target, bytes): |
||||
pass |
||||
elif isinstance(target, basestring): |
||||
target = target.encode() |
||||
else: |
||||
raise TypeError("expected target to be str or bytes") |
||||
if client_credentials is None: |
||||
self.c_channel = grpc.grpc_channel_create(target, c_arguments) |
||||
else: |
||||
self.c_channel = grpc.grpc_secure_channel_create( |
||||
client_credentials.c_credentials, target, c_arguments) |
||||
self.references.append(client_credentials) |
||||
self.references.append(target) |
||||
self.references.append(arguments) |
||||
|
||||
def create_call(self, completion_queue.CompletionQueue queue not None, |
||||
method, host, records.Timespec deadline not None): |
||||
if queue.is_shutting_down: |
||||
raise ValueError("queue must not be shutting down or shutdown") |
||||
if isinstance(method, bytes): |
||||
pass |
||||
elif isinstance(method, basestring): |
||||
method = method.encode() |
||||
else: |
||||
raise TypeError("expected method to be str or bytes") |
||||
if isinstance(host, bytes): |
||||
pass |
||||
elif isinstance(host, basestring): |
||||
host = host.encode() |
||||
else: |
||||
raise TypeError("expected host to be str or bytes") |
||||
cdef call.Call operation_call = call.Call() |
||||
operation_call.references = [self, method, host, queue] |
||||
operation_call.c_call = grpc.grpc_channel_create_call( |
||||
self.c_channel, queue.c_completion_queue, method, host, deadline.c_time) |
||||
return operation_call |
||||
|
||||
def __dealloc__(self): |
||||
if self.c_channel != NULL: |
||||
grpc.grpc_channel_destroy(self.c_channel) |
@ -0,0 +1,39 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
from grpc._cython._cygrpc cimport grpc |
||||
|
||||
|
||||
cdef class CompletionQueue: |
||||
|
||||
cdef grpc.grpc_completion_queue *c_completion_queue |
||||
cdef object poll_condition |
||||
cdef bint is_polling |
||||
cdef bint is_shutting_down |
||||
cdef bint is_shutdown |
@ -0,0 +1,117 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
cimport cpython |
||||
|
||||
from grpc._cython._cygrpc cimport call |
||||
from grpc._cython._cygrpc cimport records |
||||
|
||||
import threading |
||||
import time |
||||
|
||||
|
||||
cdef class CompletionQueue: |
||||
|
||||
def __cinit__(self): |
||||
self.c_completion_queue = grpc.grpc_completion_queue_create() |
||||
self.is_shutting_down = False |
||||
self.is_shutdown = False |
||||
self.poll_condition = threading.Condition() |
||||
self.is_polling = False |
||||
|
||||
def poll(self, records.Timespec deadline=None): |
||||
# We name this 'poll' to avoid problems with CPython's expectations for |
||||
# 'special' methods (like next and __next__). |
||||
cdef grpc.gpr_timespec c_deadline = grpc.gpr_inf_future |
||||
cdef records.OperationTag tag = None |
||||
cdef object user_tag = None |
||||
cdef call.Call operation_call = None |
||||
cdef records.CallDetails request_call_details = None |
||||
cdef records.Metadata request_metadata = None |
||||
cdef records.Operations batch_operations = None |
||||
if deadline is not None: |
||||
c_deadline = deadline.c_time |
||||
cdef grpc.grpc_event event |
||||
|
||||
# Poll within a critical section |
||||
with self.poll_condition: |
||||
while self.is_polling: |
||||
self.poll_condition.wait(float(deadline) - time.time()) |
||||
self.is_polling = True |
||||
with nogil: |
||||
event = grpc.grpc_completion_queue_next( |
||||
self.c_completion_queue, c_deadline) |
||||
with self.poll_condition: |
||||
self.is_polling = False |
||||
self.poll_condition.notify() |
||||
|
||||
if event.type == grpc.GRPC_QUEUE_TIMEOUT: |
||||
return records.Event(event.type, False, None, None, None, None, None) |
||||
elif event.type == grpc.GRPC_QUEUE_SHUTDOWN: |
||||
self.is_shutdown = True |
||||
return records.Event(event.type, True, None, None, None, None, None) |
||||
else: |
||||
if event.tag != NULL: |
||||
tag = <records.OperationTag>event.tag |
||||
# We receive event tags only after they've been inc-ref'd elsewhere in |
||||
# the code. |
||||
cpython.Py_DECREF(tag) |
||||
if tag.shutting_down_server is not None: |
||||
tag.shutting_down_server.notify_shutdown_complete() |
||||
user_tag = tag.user_tag |
||||
operation_call = tag.operation_call |
||||
request_call_details = tag.request_call_details |
||||
request_metadata = tag.request_metadata |
||||
batch_operations = tag.batch_operations |
||||
if tag.is_new_request: |
||||
# Stuff in the tag not explicitly handled by us needs to live through |
||||
# the life of the call |
||||
operation_call.references.extend(tag.references) |
||||
return records.Event( |
||||
event.type, event.success, user_tag, operation_call, |
||||
request_call_details, request_metadata, batch_operations) |
||||
|
||||
def shutdown(self): |
||||
grpc.grpc_completion_queue_shutdown(self.c_completion_queue) |
||||
self.is_shutting_down = True |
||||
|
||||
def clear(self): |
||||
if not self.is_shutting_down: |
||||
raise ValueError('queue must be shutting down to be cleared') |
||||
while self.poll().type != grpc.GRPC_QUEUE_SHUTDOWN: |
||||
pass |
||||
|
||||
def __dealloc__(self): |
||||
if self.c_completion_queue != NULL: |
||||
# Ensure shutdown, pump the queue |
||||
if not self.is_shutting_down: |
||||
self.shutdown() |
||||
while not self.is_shutdown: |
||||
self.poll() |
||||
grpc.grpc_completion_queue_destroy(self.c_completion_queue) |
@ -0,0 +1,45 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
from grpc._cython._cygrpc cimport grpc |
||||
|
||||
|
||||
cdef class ClientCredentials: |
||||
|
||||
cdef grpc.grpc_credentials *c_credentials |
||||
cdef grpc.grpc_ssl_pem_key_cert_pair c_ssl_pem_key_cert_pair |
||||
cdef list references |
||||
|
||||
|
||||
cdef class ServerCredentials: |
||||
|
||||
cdef grpc.grpc_server_credentials *c_credentials |
||||
cdef grpc.grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs |
||||
cdef size_t c_ssl_pem_key_cert_pairs_count |
||||
cdef list references |
@ -0,0 +1,217 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
from grpc._cython._cygrpc cimport records |
||||
|
||||
|
||||
cdef class ClientCredentials: |
||||
|
||||
def __cinit__(self): |
||||
self.c_credentials = NULL |
||||
self.c_ssl_pem_key_cert_pair.private_key = NULL |
||||
self.c_ssl_pem_key_cert_pair.certificate_chain = NULL |
||||
self.references = [] |
||||
|
||||
# The object *can* be invalid in Python if we fail to make the credentials |
||||
# (and the core thus returns NULL credentials). Used primarily for debugging. |
||||
@property |
||||
def is_valid(self): |
||||
return self.c_credentials != NULL |
||||
|
||||
def __dealloc__(self): |
||||
if self.c_credentials != NULL: |
||||
grpc.grpc_credentials_release(self.c_credentials) |
||||
|
||||
|
||||
cdef class ServerCredentials: |
||||
|
||||
def __cinit__(self): |
||||
self.c_credentials = NULL |
||||
|
||||
def __dealloc__(self): |
||||
if self.c_credentials != NULL: |
||||
grpc.grpc_server_credentials_release(self.c_credentials) |
||||
|
||||
|
||||
def client_credentials_google_default(): |
||||
cdef ClientCredentials credentials = ClientCredentials(); |
||||
credentials.c_credentials = grpc.grpc_google_default_credentials_create() |
||||
return credentials |
||||
|
||||
def client_credentials_ssl(pem_root_certificates, |
||||
records.SslPemKeyCertPair ssl_pem_key_cert_pair): |
||||
if pem_root_certificates is None: |
||||
pass |
||||
elif isinstance(pem_root_certificates, bytes): |
||||
pass |
||||
elif isinstance(pem_root_certificates, basestring): |
||||
pem_root_certificates = pem_root_certificates.encode() |
||||
else: |
||||
raise TypeError("expected str or bytes for pem_root_certificates") |
||||
cdef ClientCredentials credentials = ClientCredentials() |
||||
cdef const char *c_pem_root_certificates = NULL |
||||
if pem_root_certificates is not None: |
||||
c_pem_root_certificates = pem_root_certificates |
||||
credentials.references.append(pem_root_certificates) |
||||
if ssl_pem_key_cert_pair is not None: |
||||
credentials.c_credentials = grpc.grpc_ssl_credentials_create( |
||||
c_pem_root_certificates, &ssl_pem_key_cert_pair.c_pair |
||||
) |
||||
credentials.references.append(ssl_pem_key_cert_pair) |
||||
else: |
||||
credentials.c_credentials = grpc.grpc_ssl_credentials_create( |
||||
c_pem_root_certificates, NULL |
||||
) |
||||
|
||||
def client_credentials_composite_credentials( |
||||
ClientCredentials credentials_1 not None, |
||||
ClientCredentials credentials_2 not None): |
||||
if not credentials_1.is_valid or not credentials_2.is_valid: |
||||
raise ValueError("passed credentials must both be valid") |
||||
cdef ClientCredentials credentials = ClientCredentials() |
||||
credentials.c_credentials = grpc.grpc_composite_credentials_create( |
||||
credentials_1.c_credentials, credentials_2.c_credentials) |
||||
credentials.references.append(credentials_1) |
||||
credentials.references.append(credentials_2) |
||||
return credentials |
||||
|
||||
def client_credentials_compute_engine(): |
||||
cdef ClientCredentials credentials = ClientCredentials() |
||||
credentials.c_credentials = grpc.grpc_compute_engine_credentials_create() |
||||
return credentials |
||||
|
||||
def client_credentials_service_account( |
||||
json_key, scope, records.Timespec token_lifetime not None): |
||||
if isinstance(json_key, bytes): |
||||
pass |
||||
elif isinstance(json_key, basestring): |
||||
json_key = json_key.encode() |
||||
else: |
||||
raise TypeError("expected json_key to be str or bytes") |
||||
if isinstance(scope, bytes): |
||||
pass |
||||
elif isinstance(scope, basestring): |
||||
scope = scope.encode() |
||||
else: |
||||
raise TypeError("expected scope to be str or bytes") |
||||
cdef ClientCredentials credentials = ClientCredentials() |
||||
credentials.c_credentials = grpc.grpc_service_account_credentials_create( |
||||
json_key, scope, token_lifetime.c_time) |
||||
credentials.references.extend([json_key, scope]) |
||||
return credentials |
||||
|
||||
def client_credentials_jwt(json_key, records.Timespec token_lifetime not None): |
||||
if isinstance(json_key, bytes): |
||||
pass |
||||
elif isinstance(json_key, basestring): |
||||
json_key = json_key.encode() |
||||
else: |
||||
raise TypeError("expected json_key to be str or bytes") |
||||
cdef ClientCredentials credentials = ClientCredentials() |
||||
credentials.c_credentials = grpc.grpc_jwt_credentials_create( |
||||
json_key, token_lifetime.c_time) |
||||
credentials.references.append(json_key) |
||||
return credentials |
||||
|
||||
def client_credentials_refresh_token(json_refresh_token): |
||||
if isinstance(json_refresh_token, bytes): |
||||
pass |
||||
elif isinstance(json_refresh_token, basestring): |
||||
json_refresh_token = json_refresh_token.encode() |
||||
else: |
||||
raise TypeError("expected json_refresh_token to be str or bytes") |
||||
cdef ClientCredentials credentials = ClientCredentials() |
||||
credentials.c_credentials = grpc.grpc_refresh_token_credentials_create( |
||||
json_refresh_token) |
||||
credentials.references.append(json_refresh_token) |
||||
return credentials |
||||
|
||||
def client_credentials_fake_transport_security(): |
||||
cdef ClientCredentials credentials = ClientCredentials() |
||||
credentials.c_credentials = ( |
||||
grpc.grpc_fake_transport_security_credentials_create()) |
||||
return credentials |
||||
|
||||
def client_credentials_iam(authorization_token, authority_selector): |
||||
if isinstance(authorization_token, bytes): |
||||
pass |
||||
elif isinstance(authorization_token, basestring): |
||||
authorization_token = authorization_token.encode() |
||||
else: |
||||
raise TypeError("expected authorization_token to be str or bytes") |
||||
if isinstance(authority_selector, bytes): |
||||
pass |
||||
elif isinstance(authority_selector, basestring): |
||||
authority_selector = authority_selector.encode() |
||||
else: |
||||
raise TypeError("expected authority_selector to be str or bytes") |
||||
cdef ClientCredentials credentials = ClientCredentials() |
||||
credentials.c_credentials = grpc.grpc_iam_credentials_create( |
||||
authorization_token, authority_selector) |
||||
credentials.references.append(authorization_token) |
||||
credentials.references.append(authority_selector) |
||||
return credentials |
||||
|
||||
def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs): |
||||
if pem_root_certs is None: |
||||
pass |
||||
elif isinstance(pem_root_certs, bytes): |
||||
pass |
||||
elif isinstance(pem_root_certs, basestring): |
||||
pem_root_certs = pem_root_certs.encode() |
||||
else: |
||||
raise TypeError("expected pem_root_certs to be str or bytes") |
||||
pem_key_cert_pairs = list(pem_key_cert_pairs) |
||||
for pair in pem_key_cert_pairs: |
||||
if not isinstance(pair, records.SslPemKeyCertPair): |
||||
raise TypeError("expected pem_key_cert_pairs to be sequence of " |
||||
"records.SslPemKeyCertPair") |
||||
cdef ServerCredentials credentials = ServerCredentials() |
||||
credentials.references.append(pem_key_cert_pairs) |
||||
credentials.references.append(pem_root_certs) |
||||
credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs) |
||||
credentials.c_ssl_pem_key_cert_pairs = ( |
||||
<grpc.grpc_ssl_pem_key_cert_pair *>grpc.gpr_malloc( |
||||
sizeof(grpc.grpc_ssl_pem_key_cert_pair) * |
||||
credentials.c_ssl_pem_key_cert_pairs_count |
||||
)) |
||||
for i in range(credentials.c_ssl_pem_key_cert_pairs_count): |
||||
credentials.c_ssl_pem_key_cert_pairs[i] = ( |
||||
(<records.SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair) |
||||
credentials.c_credentials = grpc.grpc_ssl_server_credentials_create( |
||||
pem_root_certs, credentials.c_ssl_pem_key_cert_pairs, |
||||
credentials.c_ssl_pem_key_cert_pairs_count |
||||
) |
||||
return credentials |
||||
|
||||
def server_credentials_fake_transport_security(): |
||||
cdef ServerCredentials credentials = ServerCredentials() |
||||
credentials.c_credentials = ( |
||||
grpc.grpc_fake_transport_security_server_credentials_create()) |
||||
return credentials |
@ -0,0 +1,344 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
cimport libc.time |
||||
|
||||
|
||||
cdef extern from "grpc/support/alloc.h": |
||||
void *gpr_malloc(size_t size) |
||||
void gpr_free(void *ptr) |
||||
void *gpr_realloc(void *p, size_t size) |
||||
|
||||
cdef extern from "grpc/support/slice.h": |
||||
ctypedef struct gpr_slice: |
||||
# don't worry about writing out the members of gpr_slice; we never access |
||||
# them directly. |
||||
pass |
||||
|
||||
gpr_slice gpr_slice_ref(gpr_slice s) |
||||
void gpr_slice_unref(gpr_slice s) |
||||
gpr_slice gpr_slice_new(void *p, size_t len, void (*destroy)(void *)) |
||||
gpr_slice gpr_slice_new_with_len( |
||||
void *p, size_t len, void (*destroy)(void *, size_t)) |
||||
gpr_slice gpr_slice_malloc(size_t length) |
||||
gpr_slice gpr_slice_from_copied_string(const char *source) |
||||
gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len) |
||||
|
||||
# Declare functions for function-like macros (because Cython)... |
||||
void *gpr_slice_start_ptr "GPR_SLICE_START_PTR" (gpr_slice s) |
||||
size_t gpr_slice_length "GPR_SLICE_LENGTH" (gpr_slice s) |
||||
|
||||
|
||||
cdef extern from "grpc/support/port_platform.h": |
||||
# As long as the header file gets this type right, we don't need to get this |
||||
# type exactly; just close enough that the operations will be supported in the |
||||
# underlying C layers. |
||||
ctypedef unsigned int gpr_uint32 |
||||
|
||||
|
||||
cdef extern from "grpc/support/time.h": |
||||
|
||||
ctypedef struct gpr_timespec: |
||||
libc.time.time_t seconds "tv_sec" |
||||
int nanoseconds "tv_nsec" |
||||
|
||||
cdef gpr_timespec gpr_time_0 |
||||
cdef gpr_timespec gpr_inf_future |
||||
cdef gpr_timespec gpr_inf_past |
||||
|
||||
gpr_timespec gpr_now() |
||||
|
||||
|
||||
cdef extern from "grpc/status.h": |
||||
ctypedef enum grpc_status_code: |
||||
GRPC_STATUS_OK |
||||
GRPC_STATUS_CANCELLED |
||||
GRPC_STATUS_UNKNOWN |
||||
GRPC_STATUS_INVALID_ARGUMENT |
||||
GRPC_STATUS_DEADLINE_EXCEEDED |
||||
GRPC_STATUS_NOT_FOUND |
||||
GRPC_STATUS_ALREADY_EXISTS |
||||
GRPC_STATUS_PERMISSION_DENIED |
||||
GRPC_STATUS_UNAUTHENTICATED |
||||
GRPC_STATUS_RESOURCE_EXHAUSTED |
||||
GRPC_STATUS_FAILED_PRECONDITION |
||||
GRPC_STATUS_ABORTED |
||||
GRPC_STATUS_OUT_OF_RANGE |
||||
GRPC_STATUS_UNIMPLEMENTED |
||||
GRPC_STATUS_INTERNAL |
||||
GRPC_STATUS_UNAVAILABLE |
||||
GRPC_STATUS_DATA_LOSS |
||||
GRPC_STATUS__DO_NOT_USE |
||||
|
||||
|
||||
cdef extern from "grpc/byte_buffer_reader.h": |
||||
struct grpc_byte_buffer_reader: |
||||
# We don't care about the internals |
||||
pass |
||||
|
||||
|
||||
cdef extern from "grpc/byte_buffer.h": |
||||
ctypedef struct grpc_byte_buffer: |
||||
# We don't care about the internals. |
||||
pass |
||||
|
||||
grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices, |
||||
size_t nslices) |
||||
size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) |
||||
void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer) |
||||
|
||||
void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader, |
||||
grpc_byte_buffer *buffer) |
||||
int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader, |
||||
gpr_slice *slice) |
||||
void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader) |
||||
|
||||
|
||||
cdef extern from "grpc/grpc.h": |
||||
|
||||
ctypedef struct grpc_completion_queue: |
||||
# We don't care about the internals (and in fact don't know them) |
||||
pass |
||||
|
||||
ctypedef struct grpc_channel: |
||||
# We don't care about the internals (and in fact don't know them) |
||||
pass |
||||
|
||||
ctypedef struct grpc_server: |
||||
# We don't care about the internals (and in fact don't know them) |
||||
pass |
||||
|
||||
ctypedef struct grpc_call: |
||||
# We don't care about the internals (and in fact don't know them) |
||||
pass |
||||
|
||||
ctypedef enum grpc_arg_type: |
||||
grpc_arg_string "GRPC_ARG_STRING" |
||||
grpc_arg_integer "GRPC_ARG_INTEGER" |
||||
grpc_arg_pointer "GRPC_ARG_POINTER" |
||||
|
||||
ctypedef struct grpc_arg_value_pointer: |
||||
void *address "p" |
||||
void *(*copy)(void *) |
||||
void (*destroy)(void *) |
||||
|
||||
union grpc_arg_value: |
||||
char *string |
||||
int integer |
||||
grpc_arg_value_pointer pointer |
||||
|
||||
ctypedef struct grpc_arg: |
||||
grpc_arg_type type |
||||
char *key |
||||
grpc_arg_value value |
||||
|
||||
ctypedef struct grpc_channel_args: |
||||
size_t arguments_length "num_args" |
||||
grpc_arg *arguments "args" |
||||
|
||||
ctypedef enum grpc_call_error: |
||||
GRPC_CALL_OK |
||||
GRPC_CALL_ERROR |
||||
GRPC_CALL_ERROR_NOT_ON_SERVER |
||||
GRPC_CALL_ERROR_NOT_ON_CLIENT |
||||
GRPC_CALL_ERROR_ALREADY_ACCEPTED |
||||
GRPC_CALL_ERROR_ALREADY_INVOKED |
||||
GRPC_CALL_ERROR_NOT_INVOKED |
||||
GRPC_CALL_ERROR_ALREADY_FINISHED |
||||
GRPC_CALL_ERROR_TOO_MANY_OPERATIONS |
||||
GRPC_CALL_ERROR_INVALID_FLAGS |
||||
GRPC_CALL_ERROR_INVALID_METADATA |
||||
|
||||
ctypedef struct grpc_metadata: |
||||
const char *key |
||||
const char *value |
||||
size_t value_length |
||||
# ignore the 'internal_data.obfuscated' fields. |
||||
|
||||
ctypedef enum grpc_completion_type: |
||||
GRPC_QUEUE_SHUTDOWN |
||||
GRPC_QUEUE_TIMEOUT |
||||
GRPC_OP_COMPLETE |
||||
|
||||
ctypedef struct grpc_event: |
||||
grpc_completion_type type |
||||
int success |
||||
void *tag |
||||
|
||||
ctypedef struct grpc_metadata_array: |
||||
size_t count |
||||
size_t capacity |
||||
grpc_metadata *metadata |
||||
|
||||
void grpc_metadata_array_init(grpc_metadata_array *array) |
||||
void grpc_metadata_array_destroy(grpc_metadata_array *array) |
||||
|
||||
ctypedef struct grpc_call_details: |
||||
char *method |
||||
size_t method_capacity |
||||
char *host |
||||
size_t host_capacity |
||||
gpr_timespec deadline |
||||
|
||||
void grpc_call_details_init(grpc_call_details *details) |
||||
void grpc_call_details_destroy(grpc_call_details *details) |
||||
|
||||
ctypedef enum grpc_op_type: |
||||
GRPC_OP_SEND_INITIAL_METADATA |
||||
GRPC_OP_SEND_MESSAGE |
||||
GRPC_OP_SEND_CLOSE_FROM_CLIENT |
||||
GRPC_OP_SEND_STATUS_FROM_SERVER |
||||
GRPC_OP_RECV_INITIAL_METADATA |
||||
GRPC_OP_RECV_MESSAGE |
||||
GRPC_OP_RECV_STATUS_ON_CLIENT |
||||
GRPC_OP_RECV_CLOSE_ON_SERVER |
||||
|
||||
ctypedef struct grpc_op_data_send_initial_metadata: |
||||
size_t count |
||||
grpc_metadata *metadata |
||||
|
||||
ctypedef struct grpc_op_data_send_status_from_server: |
||||
size_t trailing_metadata_count |
||||
grpc_metadata *trailing_metadata |
||||
grpc_status_code status |
||||
const char *status_details |
||||
|
||||
ctypedef struct grpc_op_data_recv_status_on_client: |
||||
grpc_metadata_array *trailing_metadata |
||||
grpc_status_code *status |
||||
char **status_details |
||||
size_t *status_details_capacity |
||||
|
||||
ctypedef struct grpc_op_data_recv_close_on_server: |
||||
int *cancelled |
||||
|
||||
union grpc_op_data: |
||||
grpc_op_data_send_initial_metadata send_initial_metadata |
||||
grpc_byte_buffer *send_message |
||||
grpc_op_data_send_status_from_server send_status_from_server |
||||
grpc_metadata_array *receive_initial_metadata "recv_initial_metadata" |
||||
grpc_byte_buffer **receive_message "recv_message" |
||||
grpc_op_data_recv_status_on_client receive_status_on_client "recv_status_on_client" |
||||
grpc_op_data_recv_close_on_server receive_close_on_server "recv_close_on_server" |
||||
|
||||
ctypedef struct grpc_op: |
||||
grpc_op_type type "op" |
||||
gpr_uint32 flags |
||||
grpc_op_data data |
||||
|
||||
void grpc_init() |
||||
void grpc_shutdown() |
||||
|
||||
grpc_completion_queue *grpc_completion_queue_create() |
||||
grpc_event grpc_completion_queue_next(grpc_completion_queue *cq, |
||||
gpr_timespec deadline) nogil |
||||
void grpc_completion_queue_shutdown(grpc_completion_queue *cq) |
||||
void grpc_completion_queue_destroy(grpc_completion_queue *cq) |
||||
|
||||
grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, |
||||
size_t nops, void *tag) |
||||
grpc_call_error grpc_call_cancel(grpc_call *call) |
||||
grpc_call_error grpc_call_cancel_with_status(grpc_call *call, |
||||
grpc_status_code status, |
||||
const char *description) |
||||
void grpc_call_destroy(grpc_call *call) |
||||
|
||||
|
||||
grpc_channel *grpc_channel_create(const char *target, |
||||
const grpc_channel_args *args) |
||||
grpc_call *grpc_channel_create_call(grpc_channel *channel, |
||||
grpc_completion_queue *completion_queue, |
||||
const char *method, const char *host, |
||||
gpr_timespec deadline) |
||||
void grpc_channel_destroy(grpc_channel *channel) |
||||
|
||||
grpc_server *grpc_server_create(const grpc_channel_args *args) |
||||
grpc_call_error grpc_server_request_call( |
||||
grpc_server *server, grpc_call **call, grpc_call_details *details, |
||||
grpc_metadata_array *request_metadata, grpc_completion_queue |
||||
*cq_bound_to_call, grpc_completion_queue *cq_for_notification, void |
||||
*tag_new) |
||||
void grpc_server_register_completion_queue(grpc_server *server, |
||||
grpc_completion_queue *cq) |
||||
int grpc_server_add_http2_port(grpc_server *server, const char *addr) |
||||
void grpc_server_start(grpc_server *server) |
||||
void grpc_server_shutdown_and_notify( |
||||
grpc_server *server, grpc_completion_queue *cq, void *tag) |
||||
void grpc_server_cancel_all_calls(grpc_server *server) |
||||
void grpc_server_destroy(grpc_server *server) |
||||
|
||||
|
||||
cdef extern from "grpc/grpc_security.h": |
||||
|
||||
ctypedef struct grpc_ssl_pem_key_cert_pair: |
||||
const char *private_key |
||||
const char *certificate_chain "cert_chain" |
||||
|
||||
ctypedef struct grpc_credentials: |
||||
# We don't care about the internals (and in fact don't know them) |
||||
pass |
||||
|
||||
grpc_credentials *grpc_google_default_credentials_create() |
||||
grpc_credentials *grpc_ssl_credentials_create( |
||||
const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair) |
||||
|
||||
grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1, |
||||
grpc_credentials *creds2) |
||||
grpc_credentials *grpc_compute_engine_credentials_create() |
||||
grpc_credentials *grpc_service_account_credentials_create( |
||||
const char *json_key, const char *scope, gpr_timespec token_lifetime) |
||||
grpc_credentials *grpc_jwt_credentials_create(const char *json_key, |
||||
gpr_timespec token_lifetime) |
||||
grpc_credentials *grpc_refresh_token_credentials_create( |
||||
const char *json_refresh_token) |
||||
grpc_credentials *grpc_fake_transport_security_credentials_create() |
||||
grpc_credentials *grpc_iam_credentials_create(const char *authorization_token, |
||||
const char *authority_selector) |
||||
void grpc_credentials_release(grpc_credentials *creds) |
||||
|
||||
grpc_channel *grpc_secure_channel_create( |
||||
grpc_credentials *creds, const char *target, |
||||
const grpc_channel_args *args) |
||||
|
||||
ctypedef struct grpc_server_credentials: |
||||
# We don't care about the internals (and in fact don't know them) |
||||
pass |
||||
|
||||
grpc_server_credentials *grpc_ssl_server_credentials_create( |
||||
const char *pem_root_certs, |
||||
grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, |
||||
size_t num_key_cert_pairs); |
||||
grpc_server_credentials *grpc_fake_transport_security_server_credentials_create() |
||||
void grpc_server_credentials_release(grpc_server_credentials *creds) |
||||
|
||||
int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, |
||||
grpc_server_credentials *creds) |
||||
|
||||
grpc_call_error grpc_call_set_credentials(grpc_call *call, |
||||
grpc_credentials *creds) |
@ -0,0 +1,129 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
from grpc._cython._cygrpc cimport grpc |
||||
from grpc._cython._cygrpc cimport call |
||||
from grpc._cython._cygrpc cimport server |
||||
|
||||
|
||||
cdef class Timespec: |
||||
|
||||
cdef grpc.gpr_timespec c_time |
||||
|
||||
|
||||
cdef class CallDetails: |
||||
|
||||
cdef grpc.grpc_call_details c_details |
||||
|
||||
|
||||
cdef class OperationTag: |
||||
|
||||
cdef object user_tag |
||||
cdef list references |
||||
# This allows CompletionQueue to notify the Python Server object that the |
||||
# underlying GRPC core server has shutdown |
||||
cdef server.Server shutting_down_server |
||||
cdef call.Call operation_call |
||||
cdef CallDetails request_call_details |
||||
cdef Metadata request_metadata |
||||
cdef Operations batch_operations |
||||
cdef bint is_new_request |
||||
|
||||
|
||||
cdef class Event: |
||||
|
||||
cdef readonly grpc.grpc_completion_type type |
||||
cdef readonly bint success |
||||
cdef readonly object tag |
||||
|
||||
# For operations with calls |
||||
cdef readonly call.Call operation_call |
||||
|
||||
# For Server.request_call |
||||
cdef readonly CallDetails request_call_details |
||||
cdef readonly Metadata request_metadata |
||||
|
||||
# For Call.start_batch |
||||
cdef readonly Operations batch_operations |
||||
|
||||
|
||||
cdef class ByteBuffer: |
||||
|
||||
cdef grpc.grpc_byte_buffer *c_byte_buffer |
||||
|
||||
|
||||
cdef class SslPemKeyCertPair: |
||||
|
||||
cdef grpc.grpc_ssl_pem_key_cert_pair c_pair |
||||
cdef readonly object private_key, certificate_chain |
||||
|
||||
|
||||
cdef class ChannelArg: |
||||
|
||||
cdef grpc.grpc_arg c_arg |
||||
cdef readonly object key, value |
||||
|
||||
|
||||
cdef class ChannelArgs: |
||||
|
||||
cdef grpc.grpc_channel_args c_args |
||||
cdef list args |
||||
|
||||
|
||||
cdef class Metadatum: |
||||
|
||||
cdef grpc.grpc_metadata c_metadata |
||||
cdef object _key, _value |
||||
|
||||
|
||||
cdef class Metadata: |
||||
|
||||
cdef grpc.grpc_metadata_array c_metadata_array |
||||
cdef object metadata |
||||
|
||||
|
||||
cdef class Operation: |
||||
|
||||
cdef grpc.grpc_op c_op |
||||
cdef ByteBuffer _received_message |
||||
cdef Metadata _received_metadata |
||||
cdef grpc.grpc_status_code _received_status_code |
||||
cdef char *_received_status_details |
||||
cdef size_t _received_status_details_capacity |
||||
cdef int _received_cancelled |
||||
cdef readonly bint is_valid |
||||
cdef object references |
||||
|
||||
|
||||
cdef class Operations: |
||||
|
||||
cdef grpc.grpc_op *c_ops |
||||
cdef size_t c_nops |
||||
cdef list operations |
||||
|
@ -0,0 +1,575 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
from grpc._cython._cygrpc cimport grpc |
||||
from grpc._cython._cygrpc cimport call |
||||
from grpc._cython._cygrpc cimport server |
||||
|
||||
|
||||
class StatusCode: |
||||
ok = grpc.GRPC_STATUS_OK |
||||
cancelled = grpc.GRPC_STATUS_CANCELLED |
||||
unknown = grpc.GRPC_STATUS_UNKNOWN |
||||
invalid_argument = grpc.GRPC_STATUS_INVALID_ARGUMENT |
||||
deadline_exceeded = grpc.GRPC_STATUS_DEADLINE_EXCEEDED |
||||
not_found = grpc.GRPC_STATUS_NOT_FOUND |
||||
already_exists = grpc.GRPC_STATUS_ALREADY_EXISTS |
||||
permission_denied = grpc.GRPC_STATUS_PERMISSION_DENIED |
||||
unauthenticated = grpc.GRPC_STATUS_UNAUTHENTICATED |
||||
resource_exhausted = grpc.GRPC_STATUS_RESOURCE_EXHAUSTED |
||||
failed_precondition = grpc.GRPC_STATUS_FAILED_PRECONDITION |
||||
aborted = grpc.GRPC_STATUS_ABORTED |
||||
out_of_range = grpc.GRPC_STATUS_OUT_OF_RANGE |
||||
unimplemented = grpc.GRPC_STATUS_UNIMPLEMENTED |
||||
internal = grpc.GRPC_STATUS_INTERNAL |
||||
unavailable = grpc.GRPC_STATUS_UNAVAILABLE |
||||
data_loss = grpc.GRPC_STATUS_DATA_LOSS |
||||
|
||||
|
||||
class CallError: |
||||
ok = grpc.GRPC_CALL_OK |
||||
error = grpc.GRPC_CALL_ERROR |
||||
not_on_server = grpc.GRPC_CALL_ERROR_NOT_ON_SERVER |
||||
not_on_client = grpc.GRPC_CALL_ERROR_NOT_ON_CLIENT |
||||
already_accepted = grpc.GRPC_CALL_ERROR_ALREADY_ACCEPTED |
||||
already_invoked = grpc.GRPC_CALL_ERROR_ALREADY_INVOKED |
||||
not_invoked = grpc.GRPC_CALL_ERROR_NOT_INVOKED |
||||
already_finished = grpc.GRPC_CALL_ERROR_ALREADY_FINISHED |
||||
too_many_operations = grpc.GRPC_CALL_ERROR_TOO_MANY_OPERATIONS |
||||
invalid_flags = grpc.GRPC_CALL_ERROR_INVALID_FLAGS |
||||
invalid_metadata = grpc.GRPC_CALL_ERROR_INVALID_METADATA |
||||
|
||||
|
||||
class CompletionType: |
||||
queue_shutdown = grpc.GRPC_QUEUE_SHUTDOWN |
||||
queue_timeout = grpc.GRPC_QUEUE_TIMEOUT |
||||
operation_complete = grpc.GRPC_OP_COMPLETE |
||||
|
||||
|
||||
class OperationType: |
||||
send_initial_metadata = grpc.GRPC_OP_SEND_INITIAL_METADATA |
||||
send_message = grpc.GRPC_OP_SEND_MESSAGE |
||||
send_close_from_client = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT |
||||
send_status_from_server = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER |
||||
receive_initial_metadata = grpc.GRPC_OP_RECV_INITIAL_METADATA |
||||
receive_message = grpc.GRPC_OP_RECV_MESSAGE |
||||
receive_status_on_client = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT |
||||
receive_close_on_server = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER |
||||
|
||||
|
||||
cdef class Timespec: |
||||
|
||||
def __cinit__(self, time): |
||||
if time is None: |
||||
self.c_time = grpc.gpr_now() |
||||
elif isinstance(time, float): |
||||
if time == float("+inf"): |
||||
self.c_time = grpc.gpr_inf_future |
||||
elif time == float("-inf"): |
||||
self.c_time = grpc.gpr_inf_past |
||||
else: |
||||
self.c_time.seconds = time |
||||
self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9 |
||||
else: |
||||
raise TypeError("expected time to be float") |
||||
|
||||
@property |
||||
def seconds(self): |
||||
return self.c_time.seconds |
||||
|
||||
@property |
||||
def nanoseconds(self): |
||||
return self.c_time.nanoseconds |
||||
|
||||
def __float__(self): |
||||
return <double>self.c_time.seconds + <double>self.c_time.nanoseconds / 1e9 |
||||
|
||||
infinite_future = Timespec(float("+inf")) |
||||
infinite_past = Timespec(float("-inf")) |
||||
|
||||
|
||||
cdef class CallDetails: |
||||
|
||||
def __cinit__(self): |
||||
grpc.grpc_call_details_init(&self.c_details) |
||||
|
||||
def __dealloc__(self): |
||||
grpc.grpc_call_details_destroy(&self.c_details) |
||||
|
||||
@property |
||||
def method(self): |
||||
if self.c_details.method != NULL: |
||||
return <bytes>self.c_details.method |
||||
else: |
||||
return None |
||||
|
||||
@property |
||||
def host(self): |
||||
if self.c_details.host != NULL: |
||||
return <bytes>self.c_details.host |
||||
else: |
||||
return None |
||||
|
||||
@property |
||||
def deadline(self): |
||||
timespec = Timespec(float("-inf")) |
||||
timespec.c_time = self.c_details.deadline |
||||
return timespec |
||||
|
||||
|
||||
cdef class OperationTag: |
||||
|
||||
def __cinit__(self, user_tag): |
||||
self.user_tag = user_tag |
||||
self.references = [] |
||||
|
||||
|
||||
cdef class Event: |
||||
|
||||
def __cinit__(self, grpc.grpc_completion_type type, bint success, |
||||
object tag, call.Call operation_call, |
||||
CallDetails request_call_details, |
||||
Metadata request_metadata, |
||||
Operations batch_operations): |
||||
self.type = type |
||||
self.success = success |
||||
self.tag = tag |
||||
self.operation_call = operation_call |
||||
self.request_call_details = request_call_details |
||||
self.request_metadata = request_metadata |
||||
self.batch_operations = batch_operations |
||||
|
||||
|
||||
cdef class ByteBuffer: |
||||
|
||||
def __cinit__(self, data): |
||||
if data is None: |
||||
self.c_byte_buffer = NULL |
||||
return |
||||
if isinstance(data, bytes): |
||||
pass |
||||
elif isinstance(data, basestring): |
||||
data = data.encode() |
||||
else: |
||||
raise TypeError("expected value to be of type str or bytes") |
||||
|
||||
cdef char *c_data = data |
||||
data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data)) |
||||
self.c_byte_buffer = grpc.grpc_raw_byte_buffer_create( |
||||
&data_slice, 1) |
||||
grpc.gpr_slice_unref(data_slice) |
||||
|
||||
def bytes(self): |
||||
cdef grpc.grpc_byte_buffer_reader reader |
||||
cdef grpc.gpr_slice data_slice |
||||
cdef size_t data_slice_length |
||||
cdef void *data_slice_pointer |
||||
if self.c_byte_buffer != NULL: |
||||
grpc.grpc_byte_buffer_reader_init(&reader, self.c_byte_buffer) |
||||
result = b"" |
||||
while grpc.grpc_byte_buffer_reader_next(&reader, &data_slice): |
||||
data_slice_pointer = grpc.gpr_slice_start_ptr(data_slice) |
||||
data_slice_length = grpc.gpr_slice_length(data_slice) |
||||
result += (<char *>data_slice_pointer)[:data_slice_length] |
||||
grpc.grpc_byte_buffer_reader_destroy(&reader) |
||||
return result |
||||
else: |
||||
return None |
||||
|
||||
def __len__(self): |
||||
if self.c_byte_buffer != NULL: |
||||
return grpc.grpc_byte_buffer_length(self.c_byte_buffer) |
||||
else: |
||||
return 0 |
||||
|
||||
def __str__(self): |
||||
return self.bytes() |
||||
|
||||
def __dealloc__(self): |
||||
if self.c_byte_buffer != NULL: |
||||
grpc.grpc_byte_buffer_destroy(self.c_byte_buffer) |
||||
|
||||
|
||||
cdef class SslPemKeyCertPair: |
||||
|
||||
def __cinit__(self, private_key, certificate_chain): |
||||
if isinstance(private_key, bytes): |
||||
self.private_key = private_key |
||||
elif isinstance(private_key, basestring): |
||||
self.private_key = private_key.encode() |
||||
else: |
||||
raise TypeError("expected private_key to be of type str or bytes") |
||||
if isinstance(certificate_chain, bytes): |
||||
self.certificate_chain = certificate_chain |
||||
elif isinstance(certificate_chain, basestring): |
||||
self.certificate_chain = certificate_chain.encode() |
||||
else: |
||||
raise TypeError("expected certificate_chain to be of type str or bytes " |
||||
"or int") |
||||
self.c_pair.private_key = self.private_key |
||||
self.c_pair.certificate_chain = self.certificate_chain |
||||
|
||||
|
||||
cdef class ChannelArg: |
||||
|
||||
def __cinit__(self, key, value): |
||||
if isinstance(key, bytes): |
||||
self.key = key |
||||
elif isinstance(key, basestring): |
||||
self.key = key.encode() |
||||
else: |
||||
raise TypeError("expected key to be of type str or bytes") |
||||
if isinstance(value, bytes): |
||||
self.value = value |
||||
self.c_arg.type = grpc.GRPC_ARG_STRING |
||||
self.c_arg.value.string = self.value |
||||
elif isinstance(value, basestring): |
||||
self.value = value.encode() |
||||
self.c_arg.type = grpc.GRPC_ARG_STRING |
||||
self.c_arg.value.string = self.value |
||||
elif isinstance(value, int): |
||||
self.value = int(value) |
||||
self.c_arg.type = grpc.GRPC_ARG_INTEGER |
||||
self.c_arg.value.integer = self.value |
||||
else: |
||||
raise TypeError("expected value to be of type str or bytes or int") |
||||
self.c_arg.key = self.key |
||||
|
||||
|
||||
cdef class ChannelArgs: |
||||
|
||||
def __cinit__(self, args): |
||||
self.args = list(args) |
||||
for arg in self.args: |
||||
if not isinstance(arg, ChannelArg): |
||||
raise TypeError("expected list of ChannelArg") |
||||
self.c_args.arguments_length = len(self.args) |
||||
self.c_args.arguments = <grpc.grpc_arg *>grpc.gpr_malloc( |
||||
self.c_args.arguments_length*sizeof(grpc.grpc_arg) |
||||
) |
||||
for i in range(self.c_args.arguments_length): |
||||
self.c_args.arguments[i] = (<ChannelArg>self.args[i]).c_arg |
||||
|
||||
def __dealloc__(self): |
||||
grpc.gpr_free(self.c_args.arguments) |
||||
|
||||
def __len__(self): |
||||
# self.args is never stale; it's only updated from this file |
||||
return len(self.args) |
||||
|
||||
def __getitem__(self, size_t i): |
||||
# self.args is never stale; it's only updated from this file |
||||
return self.args[i] |
||||
|
||||
|
||||
cdef class Metadatum: |
||||
|
||||
def __cinit__(self, key, value): |
||||
if isinstance(key, bytes): |
||||
self._key = key |
||||
elif isinstance(key, basestring): |
||||
self._key = key.encode() |
||||
else: |
||||
raise TypeError("expected key to be of type str or bytes") |
||||
if isinstance(value, bytes): |
||||
self._value = value |
||||
elif isinstance(value, basestring): |
||||
self._value = value.encode() |
||||
else: |
||||
raise TypeError("expected value to be of type str or bytes") |
||||
self.c_metadata.key = self._key |
||||
self.c_metadata.value = self._value |
||||
self.c_metadata.value_length = len(self._value) |
||||
|
||||
@property |
||||
def key(self): |
||||
return <bytes>self.c_metadata.key |
||||
|
||||
@property |
||||
def value(self): |
||||
return <bytes>self.c_metadata.value[:self.c_metadata.value_length] |
||||
|
||||
def __len__(self): |
||||
return 2 |
||||
|
||||
def __getitem__(self, size_t i): |
||||
if i == 0: |
||||
return self.key |
||||
elif i == 1: |
||||
return self.value |
||||
else: |
||||
raise IndexError("index must be 0 (key) or 1 (value)") |
||||
|
||||
def __iter__(self): |
||||
return iter((self.key, self.value)) |
||||
|
||||
|
||||
cdef class _MetadataIterator: |
||||
|
||||
cdef size_t i |
||||
cdef Metadata metadata |
||||
|
||||
def __cinit__(self, Metadata metadata not None): |
||||
self.i = 0 |
||||
self.metadata = metadata |
||||
|
||||
def __next__(self): |
||||
if self.i < len(self.metadata): |
||||
result = self.metadata[self.i] |
||||
self.i = self.i + 1 |
||||
return result |
||||
else: |
||||
raise StopIteration() |
||||
|
||||
|
||||
cdef class Metadata: |
||||
|
||||
def __cinit__(self, metadata): |
||||
self.metadata = list(metadata) |
||||
for metadatum in metadata: |
||||
if not isinstance(metadatum, Metadatum): |
||||
raise TypeError("expected list of Metadatum") |
||||
grpc.grpc_metadata_array_init(&self.c_metadata_array) |
||||
self.c_metadata_array.count = len(self.metadata) |
||||
self.c_metadata_array.capacity = len(self.metadata) |
||||
self.c_metadata_array.metadata = <grpc.grpc_metadata *>grpc.gpr_malloc( |
||||
self.c_metadata_array.count*sizeof(grpc.grpc_metadata) |
||||
) |
||||
for i in range(self.c_metadata_array.count): |
||||
self.c_metadata_array.metadata[i] = ( |
||||
(<Metadatum>self.metadata[i]).c_metadata) |
||||
|
||||
def __dealloc__(self): |
||||
# this frees the allocated memory for the grpc_metadata_array (although |
||||
# it'd be nice if that were documented somewhere...) TODO(atash): document |
||||
# this in the C core |
||||
grpc.grpc_metadata_array_destroy(&self.c_metadata_array) |
||||
|
||||
def __len__(self): |
||||
return self.c_metadata_array.count |
||||
|
||||
def __getitem__(self, size_t i): |
||||
return Metadatum( |
||||
key=<bytes>self.c_metadata_array.metadata[i].key, |
||||
value=<bytes>self.c_metadata_array.metadata[i].value[ |
||||
:self.c_metadata_array.metadata[i].value_length]) |
||||
|
||||
def __iter__(self): |
||||
return _MetadataIterator(self) |
||||
|
||||
|
||||
cdef class Operation: |
||||
|
||||
def __cinit__(self): |
||||
self.references = [] |
||||
self._received_status_details = NULL |
||||
self._received_status_details_capacity = 0 |
||||
self.is_valid = False |
||||
|
||||
@property |
||||
def type(self): |
||||
return self.c_op.type |
||||
|
||||
@property |
||||
def received_message(self): |
||||
if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE: |
||||
raise TypeError("self must be an operation receiving a message") |
||||
return self._received_message |
||||
|
||||
@property |
||||
def received_metadata(self): |
||||
if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and |
||||
self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT): |
||||
raise TypeError("self must be an operation receiving metadata") |
||||
return self._received_metadata |
||||
|
||||
@property |
||||
def received_status_code(self): |
||||
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: |
||||
raise TypeError("self must be an operation receiving a status code") |
||||
return self._received_status_code |
||||
|
||||
@property |
||||
def received_status_details(self): |
||||
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: |
||||
raise TypeError("self must be an operation receiving status details") |
||||
if self._received_status_details: |
||||
return self._received_status_details |
||||
else: |
||||
return None |
||||
|
||||
@property |
||||
def received_cancelled(self): |
||||
if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER: |
||||
raise TypeError("self must be an operation receiving cancellation " |
||||
"information") |
||||
return False if self._received_cancelled == 0 else True |
||||
|
||||
def __dealloc__(self): |
||||
# We *almost* don't need to do anything; most of the objects are handled by |
||||
# Python. The remaining one(s) are primitive fields filled in by GRPC core. |
||||
# This means that we need to clean up after receive_status_on_client. |
||||
if self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: |
||||
grpc.gpr_free(self._received_status_details) |
||||
|
||||
def operation_send_initial_metadata(Metadata metadata): |
||||
cdef Operation op = Operation() |
||||
op.c_op.type = grpc.GRPC_OP_SEND_INITIAL_METADATA |
||||
op.c_op.data.send_initial_metadata.count = metadata.c_metadata_array.count |
||||
op.c_op.data.send_initial_metadata.metadata = ( |
||||
metadata.c_metadata_array.metadata) |
||||
op.references.append(metadata) |
||||
op.is_valid = True |
||||
return op |
||||
|
||||
def operation_send_message(data): |
||||
cdef Operation op = Operation() |
||||
op.c_op.type = grpc.GRPC_OP_SEND_MESSAGE |
||||
byte_buffer = ByteBuffer(data) |
||||
op.c_op.data.send_message = byte_buffer.c_byte_buffer |
||||
op.references.append(byte_buffer) |
||||
op.is_valid = True |
||||
return op |
||||
|
||||
def operation_send_close_from_client(): |
||||
cdef Operation op = Operation() |
||||
op.c_op.type = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT |
||||
op.is_valid = True |
||||
return op |
||||
|
||||
def operation_send_status_from_server( |
||||
Metadata metadata, grpc.grpc_status_code code, details): |
||||
if isinstance(details, bytes): |
||||
pass |
||||
elif isinstance(details, basestring): |
||||
details = details.encode() |
||||
else: |
||||
raise TypeError("expected a str or bytes object for details") |
||||
cdef Operation op = Operation() |
||||
op.c_op.type = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER |
||||
op.c_op.data.send_status_from_server.trailing_metadata_count = ( |
||||
metadata.c_metadata_array.count) |
||||
op.c_op.data.send_status_from_server.trailing_metadata = ( |
||||
metadata.c_metadata_array.metadata) |
||||
op.c_op.data.send_status_from_server.status = code |
||||
op.c_op.data.send_status_from_server.status_details = details |
||||
op.references.append(metadata) |
||||
op.references.append(details) |
||||
op.is_valid = True |
||||
return op |
||||
|
||||
def operation_receive_initial_metadata(): |
||||
cdef Operation op = Operation() |
||||
op.c_op.type = grpc.GRPC_OP_RECV_INITIAL_METADATA |
||||
op._received_metadata = Metadata([]) |
||||
op.c_op.data.receive_initial_metadata = ( |
||||
&op._received_metadata.c_metadata_array) |
||||
op.is_valid = True |
||||
return op |
||||
|
||||
def operation_receive_message(): |
||||
cdef Operation op = Operation() |
||||
op.c_op.type = grpc.GRPC_OP_RECV_MESSAGE |
||||
op._received_message = ByteBuffer(None) |
||||
# n.b. the c_op.data.receive_message field needs to be deleted by us, |
||||
# anyway, so we just let that be handled by the ByteBuffer() we allocated |
||||
# the line before. |
||||
op.c_op.data.receive_message = &op._received_message.c_byte_buffer |
||||
op.is_valid = True |
||||
return op |
||||
|
||||
def operation_receive_status_on_client(): |
||||
cdef Operation op = Operation() |
||||
op.c_op.type = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT |
||||
op._received_metadata = Metadata([]) |
||||
op.c_op.data.receive_status_on_client.trailing_metadata = ( |
||||
&op._received_metadata.c_metadata_array) |
||||
op.c_op.data.receive_status_on_client.status = ( |
||||
&op._received_status_code) |
||||
op.c_op.data.receive_status_on_client.status_details = ( |
||||
&op._received_status_details) |
||||
op.c_op.data.receive_status_on_client.status_details_capacity = ( |
||||
&op._received_status_details_capacity) |
||||
op.is_valid = True |
||||
return op |
||||
|
||||
def operation_receive_close_on_server(): |
||||
cdef Operation op = Operation() |
||||
op.c_op.type = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER |
||||
op.c_op.data.receive_close_on_server.cancelled = &op._received_cancelled |
||||
op.is_valid = True |
||||
return op |
||||
|
||||
|
||||
cdef class _OperationsIterator: |
||||
|
||||
cdef size_t i |
||||
cdef Operations operations |
||||
|
||||
def __cinit__(self, Operations operations not None): |
||||
self.i = 0 |
||||
self.operations = operations |
||||
|
||||
def __next__(self): |
||||
if self.i < len(self.operations): |
||||
result = self.operations[self.i] |
||||
self.i = self.i + 1 |
||||
return result |
||||
else: |
||||
raise StopIteration() |
||||
|
||||
|
||||
cdef class Operations: |
||||
|
||||
def __cinit__(self, operations): |
||||
self.operations = list(operations) # normalize iterable |
||||
self.c_ops = NULL |
||||
self.c_nops = 0 |
||||
for operation in self.operations: |
||||
if not isinstance(operation, Operation): |
||||
raise TypeError("expected operations to be iterable of Operation") |
||||
self.c_nops = len(self.operations) |
||||
self.c_ops = <grpc.grpc_op *>grpc.gpr_malloc( |
||||
sizeof(grpc.grpc_op)*self.c_nops) |
||||
for i in range(self.c_nops): |
||||
self.c_ops[i] = (<Operation>(self.operations[i])).c_op |
||||
|
||||
def __len__(self): |
||||
return self.c_nops |
||||
|
||||
def __getitem__(self, size_t i): |
||||
# self.operations is never stale; it's only updated from this file |
||||
return self.operations[i] |
||||
|
||||
def __dealloc__(self): |
||||
grpc.gpr_free(self.c_ops) |
||||
|
||||
def __iter__(self): |
||||
return _OperationsIterator(self) |
||||
|
@ -0,0 +1,45 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
from grpc._cython._cygrpc cimport grpc |
||||
from grpc._cython._cygrpc cimport completion_queue |
||||
|
||||
|
||||
cdef class Server: |
||||
|
||||
cdef grpc.grpc_server *c_server |
||||
cdef bint is_started # start has been called |
||||
cdef bint is_shutting_down # shutdown has been called |
||||
cdef bint is_shutdown # notification of complete shutdown received |
||||
# used at dealloc when user forgets to shutdown |
||||
cdef completion_queue.CompletionQueue backup_shutdown_queue |
||||
cdef list references |
||||
cdef list registered_completion_queues |
||||
|
||||
cdef notify_shutdown_complete(self) |
@ -0,0 +1,158 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
cimport cpython |
||||
|
||||
from grpc._cython._cygrpc cimport call |
||||
from grpc._cython._cygrpc cimport completion_queue |
||||
from grpc._cython._cygrpc cimport credentials |
||||
from grpc._cython._cygrpc cimport records |
||||
|
||||
import time |
||||
|
||||
|
||||
cdef class Server: |
||||
|
||||
def __cinit__(self, records.ChannelArgs arguments=None): |
||||
cdef grpc.grpc_channel_args *c_arguments = NULL |
||||
self.references = [] |
||||
self.registered_completion_queues = [] |
||||
if arguments is not None: |
||||
c_arguments = &arguments.c_args |
||||
self.references.append(arguments) |
||||
self.c_server = grpc.grpc_server_create(c_arguments) |
||||
self.is_started = False |
||||
self.is_shutting_down = False |
||||
self.is_shutdown = False |
||||
|
||||
def request_call( |
||||
self, completion_queue.CompletionQueue call_queue not None, |
||||
completion_queue.CompletionQueue server_queue not None, tag): |
||||
if not self.is_started or self.is_shutting_down: |
||||
raise ValueError("server must be started and not shutting down") |
||||
if server_queue not in self.registered_completion_queues: |
||||
raise ValueError("server_queue must be a registered completion queue") |
||||
cdef records.OperationTag operation_tag = records.OperationTag(tag) |
||||
operation_tag.operation_call = call.Call() |
||||
operation_tag.request_call_details = records.CallDetails() |
||||
operation_tag.request_metadata = records.Metadata([]) |
||||
operation_tag.references.extend([self, call_queue, server_queue]) |
||||
operation_tag.is_new_request = True |
||||
operation_tag.batch_operations = records.Operations([]) |
||||
cpython.Py_INCREF(operation_tag) |
||||
return grpc.grpc_server_request_call( |
||||
self.c_server, &operation_tag.operation_call.c_call, |
||||
&operation_tag.request_call_details.c_details, |
||||
&operation_tag.request_metadata.c_metadata_array, |
||||
call_queue.c_completion_queue, server_queue.c_completion_queue, |
||||
<cpython.PyObject *>operation_tag) |
||||
|
||||
def register_completion_queue( |
||||
self, completion_queue.CompletionQueue queue not None): |
||||
if self.is_started: |
||||
raise ValueError("cannot register completion queues after start") |
||||
grpc.grpc_server_register_completion_queue( |
||||
self.c_server, queue.c_completion_queue) |
||||
self.registered_completion_queues.append(queue) |
||||
|
||||
def start(self): |
||||
if self.is_started: |
||||
raise ValueError("the server has already started") |
||||
self.backup_shutdown_queue = completion_queue.CompletionQueue() |
||||
self.register_completion_queue(self.backup_shutdown_queue) |
||||
self.is_started = True |
||||
grpc.grpc_server_start(self.c_server) |
||||
|
||||
def add_http2_port(self, address, |
||||
credentials.ServerCredentials server_credentials=None): |
||||
if isinstance(address, bytes): |
||||
pass |
||||
elif isinstance(address, basestring): |
||||
address = address.encode() |
||||
else: |
||||
raise TypeError("expected address to be a str or bytes") |
||||
self.references.append(address) |
||||
if server_credentials is not None: |
||||
self.references.append(server_credentials) |
||||
return grpc.grpc_server_add_secure_http2_port( |
||||
self.c_server, address, server_credentials.c_credentials) |
||||
else: |
||||
return grpc.grpc_server_add_http2_port(self.c_server, address) |
||||
|
||||
def shutdown(self, completion_queue.CompletionQueue queue not None, tag): |
||||
cdef records.OperationTag operation_tag |
||||
if queue.is_shutting_down: |
||||
raise ValueError("queue must be live") |
||||
elif not self.is_started: |
||||
raise ValueError("the server hasn't started yet") |
||||
elif self.is_shutting_down: |
||||
return |
||||
elif queue not in self.registered_completion_queues: |
||||
raise ValueError("expected registered completion queue") |
||||
else: |
||||
self.is_shutting_down = True |
||||
operation_tag = records.OperationTag(tag) |
||||
operation_tag.shutting_down_server = self |
||||
operation_tag.references.extend([self, queue]) |
||||
cpython.Py_INCREF(operation_tag) |
||||
grpc.grpc_server_shutdown_and_notify( |
||||
self.c_server, queue.c_completion_queue, |
||||
<cpython.PyObject *>operation_tag) |
||||
|
||||
cdef notify_shutdown_complete(self): |
||||
# called only by a completion queue on receiving our shutdown operation tag |
||||
self.is_shutdown = True |
||||
|
||||
def cancel_all_calls(self): |
||||
if not self.is_shutting_down: |
||||
raise ValueError("the server must be shutting down to cancel all calls") |
||||
elif self.is_shutdown: |
||||
return |
||||
else: |
||||
grpc.grpc_server_cancel_all_calls(self.c_server) |
||||
|
||||
def __dealloc__(self): |
||||
if self.c_server != NULL: |
||||
if not self.is_started: |
||||
pass |
||||
elif self.is_shutdown: |
||||
pass |
||||
elif not self.is_shutting_down: |
||||
# the user didn't call shutdown - use our backup queue |
||||
self.shutdown(self.backup_shutdown_queue, None) |
||||
# and now we wait |
||||
while not self.is_shutdown: |
||||
self.backup_shutdown_queue.poll() |
||||
else: |
||||
# We're in the process of shutting down, but have not shutdown; can't do |
||||
# much but repeatedly release the GIL and wait |
||||
while not self.is_shutdown: |
||||
time.sleep(0) |
||||
grpc.grpc_server_destroy(self.c_server) |
||||
|
@ -0,0 +1,114 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
|
||||
# Adapter from grpc._cython.types to the surface expected by |
||||
# grpc._adapter._intermediary_low. |
||||
# |
||||
# TODO(atash): Once this is plugged into grpc._adapter._intermediary_low, remove |
||||
# both grpc._adapter._intermediary_low and this file. The fore and rear links in |
||||
# grpc._adapter should be able to use grpc._cython.types directly. |
||||
|
||||
from grpc._adapter import _types as type_interfaces |
||||
from grpc._cython import cygrpc |
||||
|
||||
|
||||
class ClientCredentials(object): |
||||
def __init__(self): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def google_default(): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def ssl(): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def composite(): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def compute_engine(): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def service_account(): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def jwt(): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def refresh_token(): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def fake_transport_security(): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def iam(): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class ServerCredentials(object): |
||||
def __init__(self): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def ssl(): |
||||
raise NotImplementedError() |
||||
|
||||
@staticmethod |
||||
def fake_transport_security(): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class CompletionQueue(type_interfaces.CompletionQueue): |
||||
def __init__(self): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class Call(type_interfaces.Call): |
||||
def __init__(self): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class Channel(type_interfaces.Channel): |
||||
def __init__(self): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class Server(type_interfaces.Server): |
||||
def __init__(self): |
||||
raise NotImplementedError() |
||||
|
@ -0,0 +1,187 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
# Fork of grpc._adapter._low_test; the grpc._cython.types adapter in |
||||
# grpc._cython.low should transparently support the semantics expected of |
||||
# grpc._adapter._low. |
||||
|
||||
import time |
||||
import unittest |
||||
|
||||
from grpc._adapter import _types |
||||
from grpc._cython import adapter_low as _low |
||||
|
||||
|
||||
class InsecureServerInsecureClient(unittest.TestCase): |
||||
|
||||
def setUp(self): |
||||
self.server_completion_queue = _low.CompletionQueue() |
||||
self.server = _low.Server(self.server_completion_queue, []) |
||||
self.port = self.server.add_http2_port('[::]:0') |
||||
self.client_completion_queue = _low.CompletionQueue() |
||||
self.client_channel = _low.Channel('localhost:%d'%self.port, []) |
||||
|
||||
self.server.start() |
||||
|
||||
def tearDown(self): |
||||
self.server.shutdown() |
||||
del self.client_channel |
||||
|
||||
self.client_completion_queue.shutdown() |
||||
while (self.client_completion_queue.next().type != |
||||
_types.EventType.QUEUE_SHUTDOWN): |
||||
pass |
||||
self.server_completion_queue.shutdown() |
||||
while (self.server_completion_queue.next().type != |
||||
_types.EventType.QUEUE_SHUTDOWN): |
||||
pass |
||||
|
||||
del self.client_completion_queue |
||||
del self.server_completion_queue |
||||
del self.server |
||||
|
||||
@unittest.skip('TODO(atash): implement grpc._cython.adapter_low') |
||||
def testEcho(self): |
||||
DEADLINE = time.time()+5 |
||||
DEADLINE_TOLERANCE = 0.25 |
||||
CLIENT_METADATA_ASCII_KEY = 'key' |
||||
CLIENT_METADATA_ASCII_VALUE = 'val' |
||||
CLIENT_METADATA_BIN_KEY = 'key-bin' |
||||
CLIENT_METADATA_BIN_VALUE = b'\0'*1000 |
||||
SERVER_INITIAL_METADATA_KEY = 'init_me_me_me' |
||||
SERVER_INITIAL_METADATA_VALUE = 'whodawha?' |
||||
SERVER_TRAILING_METADATA_KEY = 'California_is_in_a_drought' |
||||
SERVER_TRAILING_METADATA_VALUE = 'zomg it is' |
||||
SERVER_STATUS_CODE = _types.StatusCode.OK |
||||
SERVER_STATUS_DETAILS = 'our work is never over' |
||||
REQUEST = 'in death a member of project mayhem has a name' |
||||
RESPONSE = 'his name is robert paulson' |
||||
METHOD = 'twinkies' |
||||
HOST = 'hostess' |
||||
server_request_tag = object() |
||||
request_call_result = self.server.request_call(self.server_completion_queue, |
||||
server_request_tag) |
||||
|
||||
self.assertEqual(_types.CallError.OK, request_call_result) |
||||
|
||||
client_call_tag = object() |
||||
client_call = self.client_channel.create_call(self.client_completion_queue, |
||||
METHOD, HOST, DEADLINE) |
||||
client_initial_metadata = [ |
||||
(CLIENT_METADATA_ASCII_KEY, CLIENT_METADATA_ASCII_VALUE), |
||||
(CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)] |
||||
client_start_batch_result = client_call.start_batch([ |
||||
_types.OpArgs.send_initial_metadata(client_initial_metadata), |
||||
_types.OpArgs.send_message(REQUEST), |
||||
_types.OpArgs.send_close_from_client(), |
||||
_types.OpArgs.recv_initial_metadata(), |
||||
_types.OpArgs.recv_message(), |
||||
_types.OpArgs.recv_status_on_client() |
||||
], client_call_tag) |
||||
self.assertEqual(_types.CallError.OK, client_start_batch_result) |
||||
|
||||
request_event = self.server_completion_queue.next(DEADLINE) |
||||
self.assertEqual(_types.EventType.OP_COMPLETE, request_event.type) |
||||
self.assertIsInstance(request_event.call, _low.Call) |
||||
self.assertIs(server_request_tag, request_event.tag) |
||||
self.assertEqual(1, len(request_event.results)) |
||||
self.assertEqual(dict(client_initial_metadata), |
||||
dict(request_event.results[0].initial_metadata)) |
||||
self.assertEqual(METHOD, request_event.call_details.method) |
||||
self.assertEqual(HOST, request_event.call_details.host) |
||||
self.assertLess(abs(DEADLINE - request_event.call_details.deadline), |
||||
DEADLINE_TOLERANCE) |
||||
|
||||
server_call_tag = object() |
||||
server_call = request_event.call |
||||
server_initial_metadata = [ |
||||
(SERVER_INITIAL_METADATA_KEY, SERVER_INITIAL_METADATA_VALUE)] |
||||
server_trailing_metadata = [ |
||||
(SERVER_TRAILING_METADATA_KEY, SERVER_TRAILING_METADATA_VALUE)] |
||||
server_start_batch_result = server_call.start_batch([ |
||||
_types.OpArgs.send_initial_metadata(server_initial_metadata), |
||||
_types.OpArgs.recv_message(), |
||||
_types.OpArgs.send_message(RESPONSE), |
||||
_types.OpArgs.recv_close_on_server(), |
||||
_types.OpArgs.send_status_from_server( |
||||
server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS) |
||||
], server_call_tag) |
||||
self.assertEqual(_types.CallError.OK, server_start_batch_result) |
||||
|
||||
client_event = self.client_completion_queue.next(DEADLINE) |
||||
server_event = self.server_completion_queue.next(DEADLINE) |
||||
|
||||
self.assertEqual(6, len(client_event.results)) |
||||
found_client_op_types = set() |
||||
for client_result in client_event.results: |
||||
# we expect each op type to be unique |
||||
self.assertNotIn(client_result.type, found_client_op_types) |
||||
found_client_op_types.add(client_result.type) |
||||
if client_result.type == _types.OpType.RECV_INITIAL_METADATA: |
||||
self.assertEqual(dict(server_initial_metadata), |
||||
dict(client_result.initial_metadata)) |
||||
elif client_result.type == _types.OpType.RECV_MESSAGE: |
||||
self.assertEqual(RESPONSE, client_result.message) |
||||
elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT: |
||||
self.assertEqual(dict(server_trailing_metadata), |
||||
dict(client_result.trailing_metadata)) |
||||
self.assertEqual(SERVER_STATUS_DETAILS, client_result.status.details) |
||||
self.assertEqual(SERVER_STATUS_CODE, client_result.status.code) |
||||
self.assertEqual(set([ |
||||
_types.OpType.SEND_INITIAL_METADATA, |
||||
_types.OpType.SEND_MESSAGE, |
||||
_types.OpType.SEND_CLOSE_FROM_CLIENT, |
||||
_types.OpType.RECV_INITIAL_METADATA, |
||||
_types.OpType.RECV_MESSAGE, |
||||
_types.OpType.RECV_STATUS_ON_CLIENT |
||||
]), found_client_op_types) |
||||
|
||||
self.assertEqual(5, len(server_event.results)) |
||||
found_server_op_types = set() |
||||
for server_result in server_event.results: |
||||
self.assertNotIn(client_result.type, found_server_op_types) |
||||
found_server_op_types.add(server_result.type) |
||||
if server_result.type == _types.OpType.RECV_MESSAGE: |
||||
self.assertEqual(REQUEST, server_result.message) |
||||
elif server_result.type == _types.OpType.RECV_CLOSE_ON_SERVER: |
||||
self.assertFalse(server_result.cancelled) |
||||
self.assertEqual(set([ |
||||
_types.OpType.SEND_INITIAL_METADATA, |
||||
_types.OpType.RECV_MESSAGE, |
||||
_types.OpType.SEND_MESSAGE, |
||||
_types.OpType.RECV_CLOSE_ON_SERVER, |
||||
_types.OpType.SEND_STATUS_FROM_SERVER |
||||
]), found_server_op_types) |
||||
|
||||
del client_call |
||||
del server_call |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main(verbosity=2) |
@ -0,0 +1,111 @@ |
||||
# Copyright 2015, 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. |
||||
|
||||
cimport cpython |
||||
|
||||
from grpc._cython._cygrpc cimport grpc |
||||
from grpc._cython._cygrpc cimport call |
||||
from grpc._cython._cygrpc cimport channel |
||||
from grpc._cython._cygrpc cimport credentials |
||||
from grpc._cython._cygrpc cimport completion_queue |
||||
from grpc._cython._cygrpc cimport records |
||||
from grpc._cython._cygrpc cimport server |
||||
|
||||
from grpc._cython._cygrpc import call |
||||
from grpc._cython._cygrpc import channel |
||||
from grpc._cython._cygrpc import credentials |
||||
from grpc._cython._cygrpc import completion_queue |
||||
from grpc._cython._cygrpc import records |
||||
from grpc._cython._cygrpc import server |
||||
|
||||
StatusCode = records.StatusCode |
||||
CallError = records.CallError |
||||
CompletionType = records.CompletionType |
||||
OperationType = records.OperationType |
||||
Timespec = records.Timespec |
||||
CallDetails = records.CallDetails |
||||
Event = records.Event |
||||
ByteBuffer = records.ByteBuffer |
||||
SslPemKeyCertPair = records.SslPemKeyCertPair |
||||
ChannelArg = records.ChannelArg |
||||
ChannelArgs = records.ChannelArgs |
||||
Metadatum = records.Metadatum |
||||
Metadata = records.Metadata |
||||
Operation = records.Operation |
||||
|
||||
operation_send_initial_metadata = records.operation_send_initial_metadata |
||||
operation_send_message = records.operation_send_message |
||||
operation_send_close_from_client = records.operation_send_close_from_client |
||||
operation_send_status_from_server = records.operation_send_status_from_server |
||||
operation_receive_initial_metadata = records.operation_receive_initial_metadata |
||||
operation_receive_message = records.operation_receive_message |
||||
operation_receive_status_on_client = records.operation_receive_status_on_client |
||||
operation_receive_close_on_server = records.operation_receive_close_on_server |
||||
|
||||
Operations = records.Operations |
||||
|
||||
ClientCredentials = credentials.ClientCredentials |
||||
ServerCredentials = credentials.ServerCredentials |
||||
|
||||
client_credentials_google_default = ( |
||||
credentials.client_credentials_google_default) |
||||
client_credentials_ssl = credentials.client_credentials_ssl |
||||
client_credentials_composite_credentials = ( |
||||
credentials.client_credentials_composite_credentials) |
||||
client_credentials_compute_engine = ( |
||||
credentials.client_credentials_compute_engine) |
||||
client_credentials_jwt = credentials.client_credentials_jwt |
||||
client_credentials_refresh_token = credentials.client_credentials_refresh_token |
||||
client_credentials_fake_transport_security = ( |
||||
credentials.client_credentials_fake_transport_security) |
||||
client_credentials_iam = credentials.client_credentials_iam |
||||
server_credentials_ssl = credentials.server_credentials_ssl |
||||
server_credentials_fake_transport_security = ( |
||||
credentials.server_credentials_fake_transport_security) |
||||
|
||||
CompletionQueue = completion_queue.CompletionQueue |
||||
Channel = channel.Channel |
||||
Server = server.Server |
||||
Call = call.Call |
||||
|
||||
|
||||
# |
||||
# Global state |
||||
# |
||||
|
||||
cdef class _ModuleState: |
||||
|
||||
def __cinit__(self): |
||||
grpc.grpc_init() |
||||
|
||||
def __dealloc__(self): |
||||
grpc.grpc_shutdown() |
||||
|
||||
_module_state = _ModuleState() |
||||
|
@ -0,0 +1,276 @@ |
||||
# Copyright 2015, 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 time |
||||
import unittest |
||||
|
||||
from grpc._cython import cygrpc |
||||
from grpc._cython import test_utilities |
||||
|
||||
|
||||
class TypeSmokeTest(unittest.TestCase): |
||||
|
||||
def testStringsInUtilitiesUpDown(self): |
||||
self.assertEqual(0, cygrpc.StatusCode.ok) |
||||
metadatum = cygrpc.Metadatum('a', 'b') |
||||
self.assertEqual('a'.encode(), metadatum.key) |
||||
self.assertEqual('b'.encode(), metadatum.value) |
||||
metadata = cygrpc.Metadata([metadatum]) |
||||
self.assertEqual(1, len(metadata)) |
||||
self.assertEqual(metadatum.key, metadata[0].key) |
||||
|
||||
def testMetadataIteration(self): |
||||
metadata = cygrpc.Metadata([ |
||||
cygrpc.Metadatum('a', 'b'), cygrpc.Metadatum('c', 'd')]) |
||||
iterator = iter(metadata) |
||||
metadatum = next(iterator) |
||||
self.assertIsInstance(metadatum, cygrpc.Metadatum) |
||||
self.assertEqual(metadatum.key, 'a'.encode()) |
||||
self.assertEqual(metadatum.value, 'b'.encode()) |
||||
metadatum = next(iterator) |
||||
self.assertIsInstance(metadatum, cygrpc.Metadatum) |
||||
self.assertEqual(metadatum.key, 'c'.encode()) |
||||
self.assertEqual(metadatum.value, 'd'.encode()) |
||||
with self.assertRaises(StopIteration): |
||||
next(iterator) |
||||
|
||||
def testOperationsIteration(self): |
||||
operations = cygrpc.Operations([ |
||||
cygrpc.operation_send_message('asdf')]) |
||||
iterator = iter(operations) |
||||
operation = next(iterator) |
||||
self.assertIsInstance(operation, cygrpc.Operation) |
||||
# `Operation`s are write-only structures; can't directly debug anything out |
||||
# of them. Just check that we stop iterating. |
||||
with self.assertRaises(StopIteration): |
||||
next(iterator) |
||||
|
||||
def testTimespec(self): |
||||
now = time.time() |
||||
timespec = cygrpc.Timespec(now) |
||||
self.assertAlmostEqual(now, float(timespec), places=8) |
||||
|
||||
def testClientCredentialsUpDown(self): |
||||
credentials = cygrpc.client_credentials_fake_transport_security() |
||||
del credentials |
||||
|
||||
def testServerCredentialsUpDown(self): |
||||
credentials = cygrpc.server_credentials_fake_transport_security() |
||||
del credentials |
||||
|
||||
def testCompletionQueueUpDown(self): |
||||
completion_queue = cygrpc.CompletionQueue() |
||||
del completion_queue |
||||
|
||||
def testServerUpDown(self): |
||||
server = cygrpc.Server(cygrpc.ChannelArgs([])) |
||||
del server |
||||
|
||||
def testChannelUpDown(self): |
||||
channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([])) |
||||
del channel |
||||
|
||||
def testSecureChannelUpDown(self): |
||||
channel = cygrpc.Channel( |
||||
'[::]:0', cygrpc.ChannelArgs([]), |
||||
cygrpc.client_credentials_fake_transport_security()) |
||||
del channel |
||||
|
||||
@unittest.skip('TODO(atash): undo skip after #2229 is merged') |
||||
def testServerStartNoExplicitShutdown(self): |
||||
server = cygrpc.Server() |
||||
completion_queue = cygrpc.CompletionQueue() |
||||
server.register_completion_queue(completion_queue) |
||||
port = server.add_http2_port('[::]:0') |
||||
self.assertIsInstance(port, int) |
||||
server.start() |
||||
del server |
||||
|
||||
@unittest.skip('TODO(atash): undo skip after #2229 is merged') |
||||
def testServerStartShutdown(self): |
||||
completion_queue = cygrpc.CompletionQueue() |
||||
server = cygrpc.Server() |
||||
server.add_http2_port('[::]:0') |
||||
server.register_completion_queue(completion_queue) |
||||
server.start() |
||||
shutdown_tag = object() |
||||
server.shutdown(completion_queue, shutdown_tag) |
||||
event = completion_queue.poll() |
||||
self.assertEqual(cygrpc.CompletionType.operation_complete, event.type) |
||||
self.assertIs(shutdown_tag, event.tag) |
||||
del server |
||||
del completion_queue |
||||
|
||||
|
||||
class InsecureServerInsecureClient(unittest.TestCase): |
||||
|
||||
def setUp(self): |
||||
self.server_completion_queue = cygrpc.CompletionQueue() |
||||
self.server = cygrpc.Server() |
||||
self.server.register_completion_queue(self.server_completion_queue) |
||||
self.port = self.server.add_http2_port('[::]:0') |
||||
self.server.start() |
||||
self.client_completion_queue = cygrpc.CompletionQueue() |
||||
self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port)) |
||||
|
||||
def tearDown(self): |
||||
del self.server |
||||
del self.client_completion_queue |
||||
del self.server_completion_queue |
||||
|
||||
def testEcho(self): |
||||
DEADLINE = time.time()+5 |
||||
DEADLINE_TOLERANCE = 0.25 |
||||
CLIENT_METADATA_ASCII_KEY = b'key' |
||||
CLIENT_METADATA_ASCII_VALUE = b'val' |
||||
CLIENT_METADATA_BIN_KEY = b'key-bin' |
||||
CLIENT_METADATA_BIN_VALUE = b'\0'*1000 |
||||
SERVER_INITIAL_METADATA_KEY = b'init_me_me_me' |
||||
SERVER_INITIAL_METADATA_VALUE = b'whodawha?' |
||||
SERVER_TRAILING_METADATA_KEY = b'California_is_in_a_drought' |
||||
SERVER_TRAILING_METADATA_VALUE = b'zomg it is' |
||||
SERVER_STATUS_CODE = cygrpc.StatusCode.ok |
||||
SERVER_STATUS_DETAILS = b'our work is never over' |
||||
REQUEST = b'in death a member of project mayhem has a name' |
||||
RESPONSE = b'his name is robert paulson' |
||||
METHOD = b'twinkies' |
||||
HOST = b'hostess' |
||||
|
||||
cygrpc_deadline = cygrpc.Timespec(DEADLINE) |
||||
|
||||
server_request_tag = object() |
||||
request_call_result = self.server.request_call( |
||||
self.server_completion_queue, self.server_completion_queue, |
||||
server_request_tag) |
||||
|
||||
self.assertEqual(cygrpc.CallError.ok, request_call_result) |
||||
|
||||
client_call_tag = object() |
||||
client_call = self.client_channel.create_call(self.client_completion_queue, |
||||
METHOD, HOST, cygrpc_deadline) |
||||
client_initial_metadata = cygrpc.Metadata([ |
||||
cygrpc.Metadatum(CLIENT_METADATA_ASCII_KEY, |
||||
CLIENT_METADATA_ASCII_VALUE), |
||||
cygrpc.Metadatum(CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)]) |
||||
client_start_batch_result = client_call.start_batch(cygrpc.Operations([ |
||||
cygrpc.operation_send_initial_metadata(client_initial_metadata), |
||||
cygrpc.operation_send_message(REQUEST), |
||||
cygrpc.operation_send_close_from_client(), |
||||
cygrpc.operation_receive_initial_metadata(), |
||||
cygrpc.operation_receive_message(), |
||||
cygrpc.operation_receive_status_on_client() |
||||
]), client_call_tag) |
||||
self.assertEqual(cygrpc.CallError.ok, client_start_batch_result) |
||||
client_event_future = test_utilities.CompletionQueuePollFuture( |
||||
self.client_completion_queue, cygrpc_deadline) |
||||
|
||||
request_event = self.server_completion_queue.poll(cygrpc_deadline) |
||||
self.assertEqual(cygrpc.CompletionType.operation_complete, |
||||
request_event.type) |
||||
self.assertIsInstance(request_event.operation_call, cygrpc.Call) |
||||
self.assertIs(server_request_tag, request_event.tag) |
||||
self.assertEqual(0, len(request_event.batch_operations)) |
||||
self.assertEqual(dict(client_initial_metadata), |
||||
dict(request_event.request_metadata)) |
||||
self.assertEqual(METHOD, request_event.request_call_details.method) |
||||
self.assertEqual(HOST, request_event.request_call_details.host) |
||||
self.assertLess( |
||||
abs(DEADLINE - float(request_event.request_call_details.deadline)), |
||||
DEADLINE_TOLERANCE) |
||||
|
||||
server_call_tag = object() |
||||
server_call = request_event.operation_call |
||||
server_initial_metadata = cygrpc.Metadata([ |
||||
cygrpc.Metadatum(SERVER_INITIAL_METADATA_KEY, |
||||
SERVER_INITIAL_METADATA_VALUE)]) |
||||
server_trailing_metadata = cygrpc.Metadata([ |
||||
cygrpc.Metadatum(SERVER_TRAILING_METADATA_KEY, |
||||
SERVER_TRAILING_METADATA_VALUE)]) |
||||
server_start_batch_result = server_call.start_batch([ |
||||
cygrpc.operation_send_initial_metadata(server_initial_metadata), |
||||
cygrpc.operation_receive_message(), |
||||
cygrpc.operation_send_message(RESPONSE), |
||||
cygrpc.operation_receive_close_on_server(), |
||||
cygrpc.operation_send_status_from_server( |
||||
server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS) |
||||
], server_call_tag) |
||||
self.assertEqual(cygrpc.CallError.ok, server_start_batch_result) |
||||
|
||||
client_event = client_event_future.result() |
||||
server_event = self.server_completion_queue.poll(cygrpc_deadline) |
||||
|
||||
self.assertEqual(6, len(client_event.batch_operations)) |
||||
found_client_op_types = set() |
||||
for client_result in client_event.batch_operations: |
||||
# we expect each op type to be unique |
||||
self.assertNotIn(client_result.type, found_client_op_types) |
||||
found_client_op_types.add(client_result.type) |
||||
if client_result.type == cygrpc.OperationType.receive_initial_metadata: |
||||
self.assertEqual(dict(server_initial_metadata), |
||||
dict(client_result.received_metadata)) |
||||
elif client_result.type == cygrpc.OperationType.receive_message: |
||||
self.assertEqual(RESPONSE, client_result.received_message.bytes()) |
||||
elif client_result.type == cygrpc.OperationType.receive_status_on_client: |
||||
self.assertEqual(dict(server_trailing_metadata), |
||||
dict(client_result.received_metadata)) |
||||
self.assertEqual(SERVER_STATUS_DETAILS, |
||||
client_result.received_status_details) |
||||
self.assertEqual(SERVER_STATUS_CODE, client_result.received_status_code) |
||||
self.assertEqual(set([ |
||||
cygrpc.OperationType.send_initial_metadata, |
||||
cygrpc.OperationType.send_message, |
||||
cygrpc.OperationType.send_close_from_client, |
||||
cygrpc.OperationType.receive_initial_metadata, |
||||
cygrpc.OperationType.receive_message, |
||||
cygrpc.OperationType.receive_status_on_client |
||||
]), found_client_op_types) |
||||
|
||||
self.assertEqual(5, len(server_event.batch_operations)) |
||||
found_server_op_types = set() |
||||
for server_result in server_event.batch_operations: |
||||
self.assertNotIn(client_result.type, found_server_op_types) |
||||
found_server_op_types.add(server_result.type) |
||||
if server_result.type == cygrpc.OperationType.receive_message: |
||||
self.assertEqual(REQUEST, server_result.received_message.bytes()) |
||||
elif server_result.type == cygrpc.OperationType.receive_close_on_server: |
||||
self.assertFalse(server_result.received_cancelled) |
||||
self.assertEqual(set([ |
||||
cygrpc.OperationType.send_initial_metadata, |
||||
cygrpc.OperationType.receive_message, |
||||
cygrpc.OperationType.send_message, |
||||
cygrpc.OperationType.receive_close_on_server, |
||||
cygrpc.OperationType.send_status_from_server |
||||
]), found_server_op_types) |
||||
|
||||
del client_call |
||||
del server_call |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main(verbosity=2) |
@ -0,0 +1,46 @@ |
||||
# Copyright 2015, 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 grpc._cython._cygrpc import completion_queue |
||||
|
||||
|
||||
class CompletionQueuePollFuture: |
||||
|
||||
def __init__(self, completion_queue, deadline): |
||||
def poller_function(): |
||||
self._event_result = completion_queue.poll(deadline) |
||||
self._event_result = None |
||||
self._thread = threading.Thread(target=poller_function) |
||||
self._thread.start() |
||||
|
||||
def result(self): |
||||
self._thread.join() |
||||
return self._event_result |
@ -1,56 +1,123 @@ |
||||
[ |
||||
{ |
||||
"module": "grpc._adapter._c_test" |
||||
"module": "grpc._cython.cygrpc_test", |
||||
"pythonVersions": [ |
||||
"2.7", |
||||
"3.4" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc._adapter._low_test" |
||||
"module": "grpc._cython.adapter_low_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc._adapter._intermediary_low_test" |
||||
"module": "grpc._adapter._c_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc._adapter._links_test" |
||||
"module": "grpc._adapter._low_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc._adapter._lonely_rear_link_test" |
||||
"module": "grpc._adapter._intermediary_low_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc._adapter._blocking_invocation_inline_service_test" |
||||
"module": "grpc._adapter._links_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc._adapter._event_invocation_synchronous_event_service_test" |
||||
"module": "grpc._adapter._lonely_rear_link_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc._adapter._future_invocation_asynchronous_event_service_test" |
||||
"module": "grpc._adapter._blocking_invocation_inline_service_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc.early_adopter.implementations_test" |
||||
"module": "grpc._adapter._event_invocation_synchronous_event_service_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc.framework.base.implementations_test" |
||||
"module": "grpc._adapter._future_invocation_asynchronous_event_service_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc.framework.face.blocking_invocation_inline_service_test" |
||||
"module": "grpc.early_adopter.implementations_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc.framework.face.event_invocation_synchronous_event_service_test" |
||||
"module": "grpc.framework.base.implementations_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc.framework.face.future_invocation_asynchronous_event_service_test" |
||||
"module": "grpc.framework.face.blocking_invocation_inline_service_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc.framework.foundation._later_test" |
||||
"module": "grpc.framework.face.event_invocation_synchronous_event_service_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "grpc.framework.foundation._logging_pool_test" |
||||
"module": "grpc.framework.face.future_invocation_asynchronous_event_service_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "interop._insecure_interop_test" |
||||
"module": "grpc.framework.foundation._later_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "interop._secure_interop_test" |
||||
"module": "grpc.framework.foundation._logging_pool_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"file": "test/compiler/python_plugin_test.py" |
||||
"module": "interop._insecure_interop_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"module": "interop._secure_interop_test", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
}, |
||||
{ |
||||
"file": "test/compiler/python_plugin_test.py", |
||||
"pythonVersions": [ |
||||
"2.7" |
||||
] |
||||
} |
||||
] |
||||
|
Loading…
Reference in new issue