diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi index ad20c94de99..9560fad1372 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi @@ -173,10 +173,14 @@ cdef extern from "grpc/grpc.h": GRPC_ARG_INTEGER GRPC_ARG_POINTER - ctypedef struct grpc_arg_value_pointer: - void *address "p" + ctypedef struct grpc_arg_pointer_vtable: void *(*copy)(void *) void (*destroy)(void *) + int (*cmp)(void *, void *) + + ctypedef struct grpc_arg_value_pointer: + void *address "p" + grpc_arg_pointer_vtable *vtable union grpc_arg_value: char *string diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi index 96c5b02bc2e..00ec91b131e 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi @@ -84,6 +84,7 @@ cdef class SslPemKeyCertPair: cdef class ChannelArg: cdef grpc_arg c_arg + cdef grpc_arg_pointer_vtable ptr_vtable cdef readonly object key, value diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi index 5a11a08f541..8a4eef4d2e8 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi @@ -27,6 +27,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from libc.stdint cimport intptr_t class ConnectivityState: idle = GRPC_CHANNEL_IDLE @@ -304,20 +305,49 @@ cdef class SslPemKeyCertPair: self.c_pair.certificate_chain = self.certificate_chain + +cdef void* copy_ptr(void* ptr): + return ptr + + +cdef void destroy_ptr(void* ptr): + pass + + +cdef int compare_ptr(void* ptr1, void* ptr2): + if ptr1 < ptr2: + return -1 + elif ptr1 > ptr2: + return 1 + else: + return 0 + + cdef class ChannelArg: def __cinit__(self, bytes key, value): self.key = key + self.value = value self.c_arg.key = self.key if isinstance(value, int): - self.value = value self.c_arg.type = GRPC_ARG_INTEGER self.c_arg.value.integer = self.value elif isinstance(value, bytes): - self.value = value self.c_arg.type = GRPC_ARG_STRING self.c_arg.value.string = self.value + elif hasattr(value, '__int__'): + # Pointer objects must override __int__() to return + # the underlying C address (Python ints are word size). The + # lifecycle of the pointer is fixed to the lifecycle of the + # python object wrapping it. + self.ptr_vtable.copy = ©_ptr + self.ptr_vtable.destroy = &destroy_ptr + self.ptr_vtable.cmp = &compare_ptr + self.c_arg.type = GRPC_ARG_POINTER + self.c_arg.value.pointer.vtable = &self.ptr_vtable + self.c_arg.value.pointer.address = (int(self.value)) else: + # TODO Add supported pointer types to this message raise TypeError('Expected int or bytes, got {}'.format(type(value))) diff --git a/src/python/grpcio_tests/tests/unit/_channel_args_test.py b/src/python/grpcio_tests/tests/unit/_channel_args_test.py index 6a636d79935..b46497afd60 100644 --- a/src/python/grpcio_tests/tests/unit/_channel_args_test.py +++ b/src/python/grpcio_tests/tests/unit/_channel_args_test.py @@ -33,11 +33,18 @@ import unittest import grpc +class TestPointerWrapper(object): + + def __int__(self): + return 123456 + + TEST_CHANNEL_ARGS = ( ('arg1', b'bytes_val'), ('arg2', 'str_val'), ('arg3', 1), (b'arg4', 'str_val'), + ('arg6', TestPointerWrapper()), )