From 6615ef5eef129aa576ca5991e1a9e2bf56d95602 Mon Sep 17 00:00:00 2001 From: Mehrdad Afshari Date: Sun, 21 Jan 2018 16:14:24 -0800 Subject: [PATCH] Relax call details interface from interceptor A gRPC Python client interceptor is passed an instance of a class that implements the ClientCallDetails interface. The interceptor can choose to create its own object that implements the interface, and pass it back to the continuation invoked by the interceptor. To make it easy to add additional attributes to call details, without breaking user code that hardcode the attributes required by the interface, instead of interospecting the object passed to the interceptor at runtime, and to ease authorship of interceptors that want to keep some attributes intact and not care about them, we relax the requirements on the object that is expected to get passed by the interceptor and let the user omit some attributes. Omitted attributes will be replaced by the original value of the attribute given to the interceptor. --- src/python/grpcio/grpc/_interceptor.py | 91 +++++++++++++++++--------- 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/src/python/grpcio/grpc/_interceptor.py b/src/python/grpcio/grpc/_interceptor.py index 56a280624f1..d029472c687 100644 --- a/src/python/grpcio/grpc/_interceptor.py +++ b/src/python/grpcio/grpc/_interceptor.py @@ -51,6 +51,30 @@ class _ClientCallDetails( pass +def _unwrap_client_call_details(call_details, default_details): + try: + method = call_details.method + except AttributeError: + method = default_details.method + + try: + timeout = call_details.timeout + except AttributeError: + timeout = default_details.timeout + + try: + metadata = call_details.metadata + except AttributeError: + metadata = default_details.metadata + + try: + credentials = call_details.credentials + except AttributeError: + credentials = default_details.credentials + + return method, timeout, metadata, credentials + + class _LocalFailure(grpc.RpcError, grpc.Future, grpc.Call): def __init__(self, exception, traceback): @@ -126,15 +150,18 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable): def future(self, request, timeout=None, metadata=None, credentials=None): - def continuation(client_call_details, request): - return self._thunk(client_call_details.method).future( - request, - timeout=client_call_details.timeout, - metadata=client_call_details.metadata, - credentials=client_call_details.credentials) - client_call_details = _ClientCallDetails(self._method, timeout, metadata, credentials) + + def continuation(new_details, request): + new_method, new_timeout, new_metadata, new_credentials = ( + _unwrap_client_call_details(new_details, client_call_details)) + return self._thunk(new_method).future( + request, + timeout=new_timeout, + metadata=new_metadata, + credentials=new_credentials) + try: return self._interceptor.intercept_unary_unary( continuation, client_call_details, request) @@ -150,16 +177,18 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable): self._interceptor = interceptor def __call__(self, request, timeout=None, metadata=None, credentials=None): + client_call_details = _ClientCallDetails(self._method, timeout, + metadata, credentials) - def continuation(client_call_details, request): - return self._thunk(client_call_details.method)( + def continuation(new_details, request): + new_method, new_timeout, new_metadata, new_credentials = ( + _unwrap_client_call_details(new_details, client_call_details)) + return self._thunk(new_method)( request, - timeout=client_call_details.timeout, - metadata=client_call_details.metadata, - credentials=client_call_details.credentials) + timeout=new_timeout, + metadata=new_metadata, + credentials=new_credentials) - client_call_details = _ClientCallDetails(self._method, timeout, - metadata, credentials) try: return self._interceptor.intercept_unary_stream( continuation, client_call_details, request) @@ -203,17 +232,18 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable): timeout=None, metadata=None, credentials=None): - - def continuation(client_call_details, request_iterator): - return self._thunk(client_call_details.method).future( - request_iterator, - timeout=client_call_details.timeout, - metadata=client_call_details.metadata, - credentials=client_call_details.credentials) - client_call_details = _ClientCallDetails(self._method, timeout, metadata, credentials) + def continuation(new_details, request_iterator): + new_method, new_timeout, new_metadata, new_credentials = ( + _unwrap_client_call_details(new_details, client_call_details)) + return self._thunk(new_method).future( + request_iterator, + timeout=new_timeout, + metadata=new_metadata, + credentials=new_credentials) + try: return self._interceptor.intercept_stream_unary( continuation, client_call_details, request_iterator) @@ -233,17 +263,18 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable): timeout=None, metadata=None, credentials=None): - - def continuation(client_call_details, request_iterator): - return self._thunk(client_call_details.method)( - request_iterator, - timeout=client_call_details.timeout, - metadata=client_call_details.metadata, - credentials=client_call_details.credentials) - client_call_details = _ClientCallDetails(self._method, timeout, metadata, credentials) + def continuation(new_details, request_iterator): + new_method, new_timeout, new_metadata, new_credentials = ( + _unwrap_client_call_details(new_details, client_call_details)) + return self._thunk(new_method)( + request_iterator, + timeout=new_timeout, + metadata=new_metadata, + credentials=new_credentials) + try: return self._interceptor.intercept_stream_stream( continuation, client_call_details, request_iterator)