123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813 |
- # Copyright 2017 gRPC authors.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- """Implementation of gRPC Python interceptors."""
- import collections
- import sys
- import types
- from typing import Any, Callable, Optional, Sequence, Tuple, Union
- import grpc
- from ._typing import DeserializingFunction
- from ._typing import DoneCallbackType
- from ._typing import MetadataType
- from ._typing import RequestIterableType
- from ._typing import SerializingFunction
- class _ServicePipeline(object):
- interceptors: Tuple[grpc.ServerInterceptor]
- def __init__(self, interceptors: Sequence[grpc.ServerInterceptor]):
- self.interceptors = tuple(interceptors)
- def _continuation(self, thunk: Callable, index: int) -> Callable:
- return lambda context: self._intercept_at(thunk, index, context)
- def _intercept_at(
- self, thunk: Callable, index: int, context: grpc.HandlerCallDetails
- ) -> grpc.RpcMethodHandler:
- if index < len(self.interceptors):
- interceptor = self.interceptors[index]
- thunk = self._continuation(thunk, index + 1)
- return interceptor.intercept_service(thunk, context)
- else:
- return thunk(context)
- def execute(
- self, thunk: Callable, context: grpc.HandlerCallDetails
- ) -> grpc.RpcMethodHandler:
- return self._intercept_at(thunk, 0, context)
- def service_pipeline(
- interceptors: Optional[Sequence[grpc.ServerInterceptor]],
- ) -> Optional[_ServicePipeline]:
- return _ServicePipeline(interceptors) if interceptors else None
- class _ClientCallDetails(
- collections.namedtuple(
- "_ClientCallDetails",
- (
- "method",
- "timeout",
- "metadata",
- "credentials",
- "wait_for_ready",
- "compression",
- ),
- ),
- grpc.ClientCallDetails,
- ):
- pass
- def _unwrap_client_call_details(
- call_details: grpc.ClientCallDetails,
- default_details: grpc.ClientCallDetails,
- ) -> Tuple[
- str, float, MetadataType, grpc.CallCredentials, bool, grpc.Compression
- ]:
- try:
- method = call_details.method # pytype: disable=attribute-error
- except AttributeError:
- method = default_details.method # pytype: disable=attribute-error
- try:
- timeout = call_details.timeout # pytype: disable=attribute-error
- except AttributeError:
- timeout = default_details.timeout # pytype: disable=attribute-error
- try:
- metadata = call_details.metadata # pytype: disable=attribute-error
- except AttributeError:
- metadata = default_details.metadata # pytype: disable=attribute-error
- try:
- credentials = (
- call_details.credentials
- ) # pytype: disable=attribute-error
- except AttributeError:
- credentials = (
- default_details.credentials
- ) # pytype: disable=attribute-error
- try:
- wait_for_ready = (
- call_details.wait_for_ready
- ) # pytype: disable=attribute-error
- except AttributeError:
- wait_for_ready = (
- default_details.wait_for_ready
- ) # pytype: disable=attribute-error
- try:
- compression = (
- call_details.compression
- ) # pytype: disable=attribute-error
- except AttributeError:
- compression = (
- default_details.compression
- ) # pytype: disable=attribute-error
- return method, timeout, metadata, credentials, wait_for_ready, compression
- class _FailureOutcome(
- grpc.RpcError, grpc.Future, grpc.Call
- ): # pylint: disable=too-many-ancestors
- _exception: Exception
- _traceback: types.TracebackType
- def __init__(self, exception: Exception, traceback: types.TracebackType):
- super(_FailureOutcome, self).__init__()
- self._exception = exception
- self._traceback = traceback
- def initial_metadata(self) -> Optional[MetadataType]:
- return None
- def trailing_metadata(self) -> Optional[MetadataType]:
- return None
- def code(self) -> Optional[grpc.StatusCode]:
- return grpc.StatusCode.INTERNAL
- def details(self) -> Optional[str]:
- return "Exception raised while intercepting the RPC"
- def cancel(self) -> bool:
- return False
- def cancelled(self) -> bool:
- return False
- def is_active(self) -> bool:
- return False
- def time_remaining(self) -> Optional[float]:
- return None
- def running(self) -> bool:
- return False
- def done(self) -> bool:
- return True
- def result(self, ignored_timeout: Optional[float] = None):
- raise self._exception
- def exception(
- self, ignored_timeout: Optional[float] = None
- ) -> Optional[Exception]:
- return self._exception
- def traceback(
- self, ignored_timeout: Optional[float] = None
- ) -> Optional[types.TracebackType]:
- return self._traceback
- def add_callback(self, unused_callback) -> bool:
- return False
- def add_done_callback(self, fn: DoneCallbackType) -> None:
- fn(self)
- def __iter__(self):
- return self
- def __next__(self):
- raise self._exception
- def next(self):
- return self.__next__()
- class _UnaryOutcome(grpc.Call, grpc.Future):
- _response: Any
- _call: grpc.Call
- def __init__(self, response: Any, call: grpc.Call):
- self._response = response
- self._call = call
- def initial_metadata(self) -> Optional[MetadataType]:
- return self._call.initial_metadata()
- def trailing_metadata(self) -> Optional[MetadataType]:
- return self._call.trailing_metadata()
- def code(self) -> Optional[grpc.StatusCode]:
- return self._call.code()
- def details(self) -> Optional[str]:
- return self._call.details()
- def is_active(self) -> bool:
- return self._call.is_active()
- def time_remaining(self) -> Optional[float]:
- return self._call.time_remaining()
- def cancel(self) -> bool:
- return self._call.cancel()
- def add_callback(self, callback) -> bool:
- return self._call.add_callback(callback)
- def cancelled(self) -> bool:
- return False
- def running(self) -> bool:
- return False
- def done(self) -> bool:
- return True
- def result(self, ignored_timeout: Optional[float] = None):
- return self._response
- def exception(self, ignored_timeout: Optional[float] = None):
- return None
- def traceback(self, ignored_timeout: Optional[float] = None):
- return None
- def add_done_callback(self, fn: DoneCallbackType) -> None:
- fn(self)
- class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
- _thunk: Callable
- _method: str
- _interceptor: grpc.UnaryUnaryClientInterceptor
- def __init__(
- self,
- thunk: Callable,
- method: str,
- interceptor: grpc.UnaryUnaryClientInterceptor,
- ):
- self._thunk = thunk
- self._method = method
- self._interceptor = interceptor
- def __call__(
- self,
- request: Any,
- timeout: Optional[float] = None,
- metadata: Optional[MetadataType] = None,
- credentials: Optional[grpc.CallCredentials] = None,
- wait_for_ready: Optional[bool] = None,
- compression: Optional[grpc.Compression] = None,
- ) -> Any:
- response, ignored_call = self._with_call(
- request,
- timeout=timeout,
- metadata=metadata,
- credentials=credentials,
- wait_for_ready=wait_for_ready,
- compression=compression,
- )
- return response
- def _with_call(
- self,
- request: Any,
- timeout: Optional[float] = None,
- metadata: Optional[MetadataType] = None,
- credentials: Optional[grpc.CallCredentials] = None,
- wait_for_ready: Optional[bool] = None,
- compression: Optional[grpc.Compression] = None,
- ) -> Tuple[Any, grpc.Call]:
- client_call_details = _ClientCallDetails(
- self._method,
- timeout,
- metadata,
- credentials,
- wait_for_ready,
- compression,
- )
- def continuation(new_details, request):
- (
- new_method,
- new_timeout,
- new_metadata,
- new_credentials,
- new_wait_for_ready,
- new_compression,
- ) = _unwrap_client_call_details(new_details, client_call_details)
- try:
- response, call = self._thunk(new_method).with_call(
- request,
- timeout=new_timeout,
- metadata=new_metadata,
- credentials=new_credentials,
- wait_for_ready=new_wait_for_ready,
- compression=new_compression,
- )
- return _UnaryOutcome(response, call)
- except grpc.RpcError as rpc_error:
- return rpc_error
- except Exception as exception: # pylint:disable=broad-except
- return _FailureOutcome(exception, sys.exc_info()[2])
- call = self._interceptor.intercept_unary_unary(
- continuation, client_call_details, request
- )
- return call.result(), call
- def with_call(
- self,
- request: Any,
- timeout: Optional[float] = None,
- metadata: Optional[MetadataType] = None,
- credentials: Optional[grpc.CallCredentials] = None,
- wait_for_ready: Optional[bool] = None,
- compression: Optional[grpc.Compression] = None,
- ) -> Tuple[Any, grpc.Call]:
- return self._with_call(
- request,
- timeout=timeout,
- metadata=metadata,
- credentials=credentials,
- wait_for_ready=wait_for_ready,
- compression=compression,
- )
- def future(
- self,
- request: Any,
- timeout: Optional[float] = None,
- metadata: Optional[MetadataType] = None,
- credentials: Optional[grpc.CallCredentials] = None,
- wait_for_ready: Optional[bool] = None,
- compression: Optional[grpc.Compression] = None,
- ) -> Any:
- client_call_details = _ClientCallDetails(
- self._method,
- timeout,
- metadata,
- credentials,
- wait_for_ready,
- compression,
- )
- def continuation(new_details, request):
- (
- new_method,
- new_timeout,
- new_metadata,
- new_credentials,
- new_wait_for_ready,
- new_compression,
- ) = _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,
- wait_for_ready=new_wait_for_ready,
- compression=new_compression,
- )
- try:
- return self._interceptor.intercept_unary_unary(
- continuation, client_call_details, request
- )
- except Exception as exception: # pylint:disable=broad-except
- return _FailureOutcome(exception, sys.exc_info()[2])
- class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
- _thunk: Callable
- _method: str
- _interceptor: grpc.UnaryStreamClientInterceptor
- def __init__(
- self,
- thunk: Callable,
- method: str,
- interceptor: grpc.UnaryStreamClientInterceptor,
- ):
- self._thunk = thunk
- self._method = method
- self._interceptor = interceptor
- def __call__(
- self,
- request: Any,
- timeout: Optional[float] = None,
- metadata: Optional[MetadataType] = None,
- credentials: Optional[grpc.CallCredentials] = None,
- wait_for_ready: Optional[bool] = None,
- compression: Optional[grpc.Compression] = None,
- ):
- client_call_details = _ClientCallDetails(
- self._method,
- timeout,
- metadata,
- credentials,
- wait_for_ready,
- compression,
- )
- def continuation(new_details, request):
- (
- new_method,
- new_timeout,
- new_metadata,
- new_credentials,
- new_wait_for_ready,
- new_compression,
- ) = _unwrap_client_call_details(new_details, client_call_details)
- return self._thunk(new_method)(
- request,
- timeout=new_timeout,
- metadata=new_metadata,
- credentials=new_credentials,
- wait_for_ready=new_wait_for_ready,
- compression=new_compression,
- )
- try:
- return self._interceptor.intercept_unary_stream(
- continuation, client_call_details, request
- )
- except Exception as exception: # pylint:disable=broad-except
- return _FailureOutcome(exception, sys.exc_info()[2])
- class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
- _thunk: Callable
- _method: str
- _interceptor: grpc.StreamUnaryClientInterceptor
- def __init__(
- self,
- thunk: Callable,
- method: str,
- interceptor: grpc.StreamUnaryClientInterceptor,
- ):
- self._thunk = thunk
- self._method = method
- self._interceptor = interceptor
- def __call__(
- self,
- request_iterator: RequestIterableType,
- timeout: Optional[float] = None,
- metadata: Optional[MetadataType] = None,
- credentials: Optional[grpc.CallCredentials] = None,
- wait_for_ready: Optional[bool] = None,
- compression: Optional[grpc.Compression] = None,
- ) -> Any:
- response, ignored_call = self._with_call(
- request_iterator,
- timeout=timeout,
- metadata=metadata,
- credentials=credentials,
- wait_for_ready=wait_for_ready,
- compression=compression,
- )
- return response
- def _with_call(
- self,
- request_iterator: RequestIterableType,
- timeout: Optional[float] = None,
- metadata: Optional[MetadataType] = None,
- credentials: Optional[grpc.CallCredentials] = None,
- wait_for_ready: Optional[bool] = None,
- compression: Optional[grpc.Compression] = None,
- ) -> Tuple[Any, grpc.Call]:
- client_call_details = _ClientCallDetails(
- self._method,
- timeout,
- metadata,
- credentials,
- wait_for_ready,
- compression,
- )
- def continuation(new_details, request_iterator):
- (
- new_method,
- new_timeout,
- new_metadata,
- new_credentials,
- new_wait_for_ready,
- new_compression,
- ) = _unwrap_client_call_details(new_details, client_call_details)
- try:
- response, call = self._thunk(new_method).with_call(
- request_iterator,
- timeout=new_timeout,
- metadata=new_metadata,
- credentials=new_credentials,
- wait_for_ready=new_wait_for_ready,
- compression=new_compression,
- )
- return _UnaryOutcome(response, call)
- except grpc.RpcError as rpc_error:
- return rpc_error
- except Exception as exception: # pylint:disable=broad-except
- return _FailureOutcome(exception, sys.exc_info()[2])
- call = self._interceptor.intercept_stream_unary(
- continuation, client_call_details, request_iterator
- )
- return call.result(), call
- def with_call(
- self,
- request_iterator: RequestIterableType,
- timeout: Optional[float] = None,
- metadata: Optional[MetadataType] = None,
- credentials: Optional[grpc.CallCredentials] = None,
- wait_for_ready: Optional[bool] = None,
- compression: Optional[grpc.Compression] = None,
- ) -> Tuple[Any, grpc.Call]:
- return self._with_call(
- request_iterator,
- timeout=timeout,
- metadata=metadata,
- credentials=credentials,
- wait_for_ready=wait_for_ready,
- compression=compression,
- )
- def future(
- self,
- request_iterator: RequestIterableType,
- timeout: Optional[float] = None,
- metadata: Optional[MetadataType] = None,
- credentials: Optional[grpc.CallCredentials] = None,
- wait_for_ready: Optional[bool] = None,
- compression: Optional[grpc.Compression] = None,
- ) -> Any:
- client_call_details = _ClientCallDetails(
- self._method,
- timeout,
- metadata,
- credentials,
- wait_for_ready,
- compression,
- )
- def continuation(new_details, request_iterator):
- (
- new_method,
- new_timeout,
- new_metadata,
- new_credentials,
- new_wait_for_ready,
- new_compression,
- ) = _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,
- wait_for_ready=new_wait_for_ready,
- compression=new_compression,
- )
- try:
- return self._interceptor.intercept_stream_unary(
- continuation, client_call_details, request_iterator
- )
- except Exception as exception: # pylint:disable=broad-except
- return _FailureOutcome(exception, sys.exc_info()[2])
- class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
- _thunk: Callable
- _method: str
- _interceptor: grpc.StreamStreamClientInterceptor
- def __init__(
- self,
- thunk: Callable,
- method: str,
- interceptor: grpc.StreamStreamClientInterceptor,
- ):
- self._thunk = thunk
- self._method = method
- self._interceptor = interceptor
- def __call__(
- self,
- request_iterator: RequestIterableType,
- timeout: Optional[float] = None,
- metadata: Optional[MetadataType] = None,
- credentials: Optional[grpc.CallCredentials] = None,
- wait_for_ready: Optional[bool] = None,
- compression: Optional[grpc.Compression] = None,
- ):
- client_call_details = _ClientCallDetails(
- self._method,
- timeout,
- metadata,
- credentials,
- wait_for_ready,
- compression,
- )
- def continuation(new_details, request_iterator):
- (
- new_method,
- new_timeout,
- new_metadata,
- new_credentials,
- new_wait_for_ready,
- new_compression,
- ) = _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,
- wait_for_ready=new_wait_for_ready,
- compression=new_compression,
- )
- try:
- return self._interceptor.intercept_stream_stream(
- continuation, client_call_details, request_iterator
- )
- except Exception as exception: # pylint:disable=broad-except
- return _FailureOutcome(exception, sys.exc_info()[2])
- class _Channel(grpc.Channel):
- _channel: grpc.Channel
- _interceptor: Union[
- grpc.UnaryUnaryClientInterceptor,
- grpc.UnaryStreamClientInterceptor,
- grpc.StreamStreamClientInterceptor,
- grpc.StreamUnaryClientInterceptor,
- ]
- def __init__(
- self,
- channel: grpc.Channel,
- interceptor: Union[
- grpc.UnaryUnaryClientInterceptor,
- grpc.UnaryStreamClientInterceptor,
- grpc.StreamStreamClientInterceptor,
- grpc.StreamUnaryClientInterceptor,
- ],
- ):
- self._channel = channel
- self._interceptor = interceptor
- def subscribe(
- self, callback: Callable, try_to_connect: Optional[bool] = False
- ):
- self._channel.subscribe(callback, try_to_connect=try_to_connect)
- def unsubscribe(self, callback: Callable):
- self._channel.unsubscribe(callback)
- # pylint: disable=arguments-differ
- def unary_unary(
- self,
- method: str,
- request_serializer: Optional[SerializingFunction] = None,
- response_deserializer: Optional[DeserializingFunction] = None,
- _registered_method: Optional[bool] = False,
- ) -> grpc.UnaryUnaryMultiCallable:
- # pytype: disable=wrong-arg-count
- thunk = lambda m: self._channel.unary_unary(
- m,
- request_serializer,
- response_deserializer,
- _registered_method,
- )
- # pytype: enable=wrong-arg-count
- if isinstance(self._interceptor, grpc.UnaryUnaryClientInterceptor):
- return _UnaryUnaryMultiCallable(thunk, method, self._interceptor)
- else:
- return thunk(method)
- # pylint: disable=arguments-differ
- def unary_stream(
- self,
- method: str,
- request_serializer: Optional[SerializingFunction] = None,
- response_deserializer: Optional[DeserializingFunction] = None,
- _registered_method: Optional[bool] = False,
- ) -> grpc.UnaryStreamMultiCallable:
- # pytype: disable=wrong-arg-count
- thunk = lambda m: self._channel.unary_stream(
- m,
- request_serializer,
- response_deserializer,
- _registered_method,
- )
- # pytype: enable=wrong-arg-count
- if isinstance(self._interceptor, grpc.UnaryStreamClientInterceptor):
- return _UnaryStreamMultiCallable(thunk, method, self._interceptor)
- else:
- return thunk(method)
- # pylint: disable=arguments-differ
- def stream_unary(
- self,
- method: str,
- request_serializer: Optional[SerializingFunction] = None,
- response_deserializer: Optional[DeserializingFunction] = None,
- _registered_method: Optional[bool] = False,
- ) -> grpc.StreamUnaryMultiCallable:
- # pytype: disable=wrong-arg-count
- thunk = lambda m: self._channel.stream_unary(
- m,
- request_serializer,
- response_deserializer,
- _registered_method,
- )
- # pytype: enable=wrong-arg-count
- if isinstance(self._interceptor, grpc.StreamUnaryClientInterceptor):
- return _StreamUnaryMultiCallable(thunk, method, self._interceptor)
- else:
- return thunk(method)
- # pylint: disable=arguments-differ
- def stream_stream(
- self,
- method: str,
- request_serializer: Optional[SerializingFunction] = None,
- response_deserializer: Optional[DeserializingFunction] = None,
- _registered_method: Optional[bool] = False,
- ) -> grpc.StreamStreamMultiCallable:
- # pytype: disable=wrong-arg-count
- thunk = lambda m: self._channel.stream_stream(
- m,
- request_serializer,
- response_deserializer,
- _registered_method,
- )
- # pytype: enable=wrong-arg-count
- if isinstance(self._interceptor, grpc.StreamStreamClientInterceptor):
- return _StreamStreamMultiCallable(thunk, method, self._interceptor)
- else:
- return thunk(method)
- def _close(self):
- self._channel.close()
- def __enter__(self):
- return self
- def __exit__(self, exc_type, exc_val, exc_tb):
- self._close()
- return False
- def close(self):
- self._channel.close()
- def intercept_channel(
- channel: grpc.Channel,
- *interceptors: Optional[
- Sequence[
- Union[
- grpc.UnaryUnaryClientInterceptor,
- grpc.UnaryStreamClientInterceptor,
- grpc.StreamStreamClientInterceptor,
- grpc.StreamUnaryClientInterceptor,
- ]
- ]
- ],
- ) -> grpc.Channel:
- for interceptor in reversed(list(interceptors)):
- if (
- not isinstance(interceptor, grpc.UnaryUnaryClientInterceptor)
- and not isinstance(interceptor, grpc.UnaryStreamClientInterceptor)
- and not isinstance(interceptor, grpc.StreamUnaryClientInterceptor)
- and not isinstance(interceptor, grpc.StreamStreamClientInterceptor)
- ):
- raise TypeError(
- "interceptor must be "
- "grpc.UnaryUnaryClientInterceptor or "
- "grpc.UnaryStreamClientInterceptor or "
- "grpc.StreamUnaryClientInterceptor or "
- "grpc.StreamStreamClientInterceptor or "
- )
- channel = _Channel(channel, interceptor)
- return channel
|