diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | python-grpc-interceptor.spec | 608 | ||||
-rw-r--r-- | sources | 1 |
3 files changed, 610 insertions, 0 deletions
@@ -0,0 +1 @@ +/grpc-interceptor-0.15.0.tar.gz diff --git a/python-grpc-interceptor.spec b/python-grpc-interceptor.spec new file mode 100644 index 0000000..18b2882 --- /dev/null +++ b/python-grpc-interceptor.spec @@ -0,0 +1,608 @@ +%global _empty_manifest_terminate_build 0 +Name: python-grpc-interceptor +Version: 0.15.0 +Release: 1 +Summary: Simplifies gRPC interceptors +License: MIT +URL: https://github.com/d5h-foss/grpc-interceptor +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/e2/24/6d43ab428c40aff3b90d59d6b4f9dbc7edd58d6b09c580a28f51245dd1e1/grpc-interceptor-0.15.0.tar.gz +BuildArch: noarch + +Requires: python3-grpcio +Requires: python3-protobuf + +%description +[](https://github.com/d5h-foss/grpc-interceptor/actions?workflow=Tests) +[](https://codecov.io/gh/d5h-foss/grpc-interceptor) +[](https://grpc-interceptor.readthedocs.io/) +[](https://pypi.org/project/grpc-interceptor/) + +# Summary + +Simplified Python gRPC interceptors. + +The Python `grpc` package provides service interceptors, but they're a bit hard to +use because of their flexibility. The `grpc` interceptors don't have direct access +to the request and response objects, or the service context. Access to these are often +desired, to be able to log data in the request or response, or set status codes on the +context. + +# Installation + +To just get the interceptors (and probably not write your own): + +```console +$ pip install grpc-interceptor +``` + +To also get the testing framework, which is good if you're writing your own interceptors: + +```console +$ pip install grpc-interceptor[testing] +``` + +# Usage + +## Server Interceptor + +To define your own interceptor (we can use `ExceptionToStatusInterceptor` as an example): + +```python +from grpc_interceptor import ServerInterceptor +from grpc_interceptor.exceptions import GrpcException + +class ExceptionToStatusInterceptor(ServerInterceptor): + def intercept( + self, + method: Callable, + request: Any, + context: grpc.ServicerContext, + method_name: str, + ) -> Any: + """Override this method to implement a custom interceptor. + You should call method(request, context) to invoke the + next handler (either the RPC method implementation, or the + next interceptor in the list). + Args: + method: The next interceptor, or method implementation. + request: The RPC request, as a protobuf message. + context: The ServicerContext pass by gRPC to the service. + method_name: A string of the form + "/protobuf.package.Service/Method" + Returns: + This should generally return the result of + method(request, context), which is typically the RPC + method response, as a protobuf message. The interceptor + is free to modify this in some way, however. + """ + try: + return method(request, context) + except GrpcException as e: + context.set_code(e.status_code) + context.set_details(e.details) + raise +``` + +Then inject your interceptor when you create the `grpc` server: + +```python +interceptors = [ExceptionToStatusInterceptor()] +server = grpc.server( + futures.ThreadPoolExecutor(max_workers=10), + interceptors=interceptors +) +``` + +To use `ExceptionToStatusInterceptor`: + +```python +from grpc_interceptor.exceptions import NotFound + +class MyService(my_pb2_grpc.MyServiceServicer): + def MyRpcMethod( + self, request: MyRequest, context: grpc.ServicerContext + ) -> MyResponse: + thing = lookup_thing() + if not thing: + raise NotFound("Sorry, your thing is missing") + ... +``` + +This results in the gRPC status status code being set to `NOT_FOUND`, +and the details `"Sorry, your thing is missing"`. This saves you the hassle of +catching exceptions in your service handler, or passing the context down into +helper functions so they can call `context.abort` or `context.set_code`. It allows +the more Pythonic approach of just raising an exception from anywhere in the code, +and having it be handled automatically. + +## Client Interceptor + +We will use an invocation metadata injecting interceptor as an example of defining +a client interceptor: + +```python +from grpc_interceptor import ClientCallDetails, ClientInterceptor + +class MetadataClientInterceptor(ClientInterceptor): + + def intercept( + self, + method: Callable, + request_or_iterator: Any, + call_details: grpc.ClientCallDetails, + ): + """Override this method to implement a custom interceptor. + + This method is called for all unary and streaming RPCs. The interceptor + implementation should call `method` using a `grpc.ClientCallDetails` and the + `request_or_iterator` object as parameters. The `request_or_iterator` + parameter may be type checked to determine if this is a singluar request + for unary RPCs or an iterator for client-streaming or client-server streaming + RPCs. + + Args: + method: A function that proceeds with the invocation by executing the next + interceptor in the chain or invoking the actual RPC on the underlying + channel. + request_or_iterator: RPC request message or iterator of request messages + for streaming requests. + call_details: Describes an RPC to be invoked. + + Returns: + The type of the return should match the type of the return value received + by calling `method`. This is an object that is both a + `Call <https://grpc.github.io/grpc/python/grpc.html#grpc.Call>`_ for the + RPC and a `Future <https://grpc.github.io/grpc/python/grpc.html#grpc.Future>`_. + + The actual result from the RPC can be got by calling `.result()` on the + value returned from `method`. + """ + new_details = ClientCallDetails( + call_details.method, + call_details.timeout, + [("authorization", "Bearer mysecrettoken")], + call_details.credentials, + call_details.wait_for_ready, + call_details.compression, + ) + + return method(request_or_iterator, new_details) +``` + +Now inject your interceptor when you create the ``grpc`` channel: + +```python +interceptors = [MetadataClientInterceptor()] +with grpc.insecure_channel("grpc-server:50051") as channel: + channel = grpc.intercept_channel(channel, *interceptors) + ... +``` + +Client interceptors can also be used to retry RPCs that fail due to specific errors, or +a host of other use cases. There are some basic approaches in the tests to get you +started. + +# Documentation + +The examples above showed usage for simple unary-unary RPC calls. For examples of +streaming and asyncio RPCs, read the +[complete documentation here](https://grpc-interceptor.readthedocs.io/). + +Note that there is no asyncio client interceptors at the moment, though contributions +are welcome. + + +%package -n python3-grpc-interceptor +Summary: Simplifies gRPC interceptors +Provides: python-grpc-interceptor +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-grpc-interceptor +[](https://github.com/d5h-foss/grpc-interceptor/actions?workflow=Tests) +[](https://codecov.io/gh/d5h-foss/grpc-interceptor) +[](https://grpc-interceptor.readthedocs.io/) +[](https://pypi.org/project/grpc-interceptor/) + +# Summary + +Simplified Python gRPC interceptors. + +The Python `grpc` package provides service interceptors, but they're a bit hard to +use because of their flexibility. The `grpc` interceptors don't have direct access +to the request and response objects, or the service context. Access to these are often +desired, to be able to log data in the request or response, or set status codes on the +context. + +# Installation + +To just get the interceptors (and probably not write your own): + +```console +$ pip install grpc-interceptor +``` + +To also get the testing framework, which is good if you're writing your own interceptors: + +```console +$ pip install grpc-interceptor[testing] +``` + +# Usage + +## Server Interceptor + +To define your own interceptor (we can use `ExceptionToStatusInterceptor` as an example): + +```python +from grpc_interceptor import ServerInterceptor +from grpc_interceptor.exceptions import GrpcException + +class ExceptionToStatusInterceptor(ServerInterceptor): + def intercept( + self, + method: Callable, + request: Any, + context: grpc.ServicerContext, + method_name: str, + ) -> Any: + """Override this method to implement a custom interceptor. + You should call method(request, context) to invoke the + next handler (either the RPC method implementation, or the + next interceptor in the list). + Args: + method: The next interceptor, or method implementation. + request: The RPC request, as a protobuf message. + context: The ServicerContext pass by gRPC to the service. + method_name: A string of the form + "/protobuf.package.Service/Method" + Returns: + This should generally return the result of + method(request, context), which is typically the RPC + method response, as a protobuf message. The interceptor + is free to modify this in some way, however. + """ + try: + return method(request, context) + except GrpcException as e: + context.set_code(e.status_code) + context.set_details(e.details) + raise +``` + +Then inject your interceptor when you create the `grpc` server: + +```python +interceptors = [ExceptionToStatusInterceptor()] +server = grpc.server( + futures.ThreadPoolExecutor(max_workers=10), + interceptors=interceptors +) +``` + +To use `ExceptionToStatusInterceptor`: + +```python +from grpc_interceptor.exceptions import NotFound + +class MyService(my_pb2_grpc.MyServiceServicer): + def MyRpcMethod( + self, request: MyRequest, context: grpc.ServicerContext + ) -> MyResponse: + thing = lookup_thing() + if not thing: + raise NotFound("Sorry, your thing is missing") + ... +``` + +This results in the gRPC status status code being set to `NOT_FOUND`, +and the details `"Sorry, your thing is missing"`. This saves you the hassle of +catching exceptions in your service handler, or passing the context down into +helper functions so they can call `context.abort` or `context.set_code`. It allows +the more Pythonic approach of just raising an exception from anywhere in the code, +and having it be handled automatically. + +## Client Interceptor + +We will use an invocation metadata injecting interceptor as an example of defining +a client interceptor: + +```python +from grpc_interceptor import ClientCallDetails, ClientInterceptor + +class MetadataClientInterceptor(ClientInterceptor): + + def intercept( + self, + method: Callable, + request_or_iterator: Any, + call_details: grpc.ClientCallDetails, + ): + """Override this method to implement a custom interceptor. + + This method is called for all unary and streaming RPCs. The interceptor + implementation should call `method` using a `grpc.ClientCallDetails` and the + `request_or_iterator` object as parameters. The `request_or_iterator` + parameter may be type checked to determine if this is a singluar request + for unary RPCs or an iterator for client-streaming or client-server streaming + RPCs. + + Args: + method: A function that proceeds with the invocation by executing the next + interceptor in the chain or invoking the actual RPC on the underlying + channel. + request_or_iterator: RPC request message or iterator of request messages + for streaming requests. + call_details: Describes an RPC to be invoked. + + Returns: + The type of the return should match the type of the return value received + by calling `method`. This is an object that is both a + `Call <https://grpc.github.io/grpc/python/grpc.html#grpc.Call>`_ for the + RPC and a `Future <https://grpc.github.io/grpc/python/grpc.html#grpc.Future>`_. + + The actual result from the RPC can be got by calling `.result()` on the + value returned from `method`. + """ + new_details = ClientCallDetails( + call_details.method, + call_details.timeout, + [("authorization", "Bearer mysecrettoken")], + call_details.credentials, + call_details.wait_for_ready, + call_details.compression, + ) + + return method(request_or_iterator, new_details) +``` + +Now inject your interceptor when you create the ``grpc`` channel: + +```python +interceptors = [MetadataClientInterceptor()] +with grpc.insecure_channel("grpc-server:50051") as channel: + channel = grpc.intercept_channel(channel, *interceptors) + ... +``` + +Client interceptors can also be used to retry RPCs that fail due to specific errors, or +a host of other use cases. There are some basic approaches in the tests to get you +started. + +# Documentation + +The examples above showed usage for simple unary-unary RPC calls. For examples of +streaming and asyncio RPCs, read the +[complete documentation here](https://grpc-interceptor.readthedocs.io/). + +Note that there is no asyncio client interceptors at the moment, though contributions +are welcome. + + +%package help +Summary: Development documents and examples for grpc-interceptor +Provides: python3-grpc-interceptor-doc +%description help +[](https://github.com/d5h-foss/grpc-interceptor/actions?workflow=Tests) +[](https://codecov.io/gh/d5h-foss/grpc-interceptor) +[](https://grpc-interceptor.readthedocs.io/) +[](https://pypi.org/project/grpc-interceptor/) + +# Summary + +Simplified Python gRPC interceptors. + +The Python `grpc` package provides service interceptors, but they're a bit hard to +use because of their flexibility. The `grpc` interceptors don't have direct access +to the request and response objects, or the service context. Access to these are often +desired, to be able to log data in the request or response, or set status codes on the +context. + +# Installation + +To just get the interceptors (and probably not write your own): + +```console +$ pip install grpc-interceptor +``` + +To also get the testing framework, which is good if you're writing your own interceptors: + +```console +$ pip install grpc-interceptor[testing] +``` + +# Usage + +## Server Interceptor + +To define your own interceptor (we can use `ExceptionToStatusInterceptor` as an example): + +```python +from grpc_interceptor import ServerInterceptor +from grpc_interceptor.exceptions import GrpcException + +class ExceptionToStatusInterceptor(ServerInterceptor): + def intercept( + self, + method: Callable, + request: Any, + context: grpc.ServicerContext, + method_name: str, + ) -> Any: + """Override this method to implement a custom interceptor. + You should call method(request, context) to invoke the + next handler (either the RPC method implementation, or the + next interceptor in the list). + Args: + method: The next interceptor, or method implementation. + request: The RPC request, as a protobuf message. + context: The ServicerContext pass by gRPC to the service. + method_name: A string of the form + "/protobuf.package.Service/Method" + Returns: + This should generally return the result of + method(request, context), which is typically the RPC + method response, as a protobuf message. The interceptor + is free to modify this in some way, however. + """ + try: + return method(request, context) + except GrpcException as e: + context.set_code(e.status_code) + context.set_details(e.details) + raise +``` + +Then inject your interceptor when you create the `grpc` server: + +```python +interceptors = [ExceptionToStatusInterceptor()] +server = grpc.server( + futures.ThreadPoolExecutor(max_workers=10), + interceptors=interceptors +) +``` + +To use `ExceptionToStatusInterceptor`: + +```python +from grpc_interceptor.exceptions import NotFound + +class MyService(my_pb2_grpc.MyServiceServicer): + def MyRpcMethod( + self, request: MyRequest, context: grpc.ServicerContext + ) -> MyResponse: + thing = lookup_thing() + if not thing: + raise NotFound("Sorry, your thing is missing") + ... +``` + +This results in the gRPC status status code being set to `NOT_FOUND`, +and the details `"Sorry, your thing is missing"`. This saves you the hassle of +catching exceptions in your service handler, or passing the context down into +helper functions so they can call `context.abort` or `context.set_code`. It allows +the more Pythonic approach of just raising an exception from anywhere in the code, +and having it be handled automatically. + +## Client Interceptor + +We will use an invocation metadata injecting interceptor as an example of defining +a client interceptor: + +```python +from grpc_interceptor import ClientCallDetails, ClientInterceptor + +class MetadataClientInterceptor(ClientInterceptor): + + def intercept( + self, + method: Callable, + request_or_iterator: Any, + call_details: grpc.ClientCallDetails, + ): + """Override this method to implement a custom interceptor. + + This method is called for all unary and streaming RPCs. The interceptor + implementation should call `method` using a `grpc.ClientCallDetails` and the + `request_or_iterator` object as parameters. The `request_or_iterator` + parameter may be type checked to determine if this is a singluar request + for unary RPCs or an iterator for client-streaming or client-server streaming + RPCs. + + Args: + method: A function that proceeds with the invocation by executing the next + interceptor in the chain or invoking the actual RPC on the underlying + channel. + request_or_iterator: RPC request message or iterator of request messages + for streaming requests. + call_details: Describes an RPC to be invoked. + + Returns: + The type of the return should match the type of the return value received + by calling `method`. This is an object that is both a + `Call <https://grpc.github.io/grpc/python/grpc.html#grpc.Call>`_ for the + RPC and a `Future <https://grpc.github.io/grpc/python/grpc.html#grpc.Future>`_. + + The actual result from the RPC can be got by calling `.result()` on the + value returned from `method`. + """ + new_details = ClientCallDetails( + call_details.method, + call_details.timeout, + [("authorization", "Bearer mysecrettoken")], + call_details.credentials, + call_details.wait_for_ready, + call_details.compression, + ) + + return method(request_or_iterator, new_details) +``` + +Now inject your interceptor when you create the ``grpc`` channel: + +```python +interceptors = [MetadataClientInterceptor()] +with grpc.insecure_channel("grpc-server:50051") as channel: + channel = grpc.intercept_channel(channel, *interceptors) + ... +``` + +Client interceptors can also be used to retry RPCs that fail due to specific errors, or +a host of other use cases. There are some basic approaches in the tests to get you +started. + +# Documentation + +The examples above showed usage for simple unary-unary RPC calls. For examples of +streaming and asyncio RPCs, read the +[complete documentation here](https://grpc-interceptor.readthedocs.io/). + +Note that there is no asyncio client interceptors at the moment, though contributions +are welcome. + + +%prep +%autosetup -n grpc-interceptor-0.15.0 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-grpc-interceptor -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Tue Apr 11 2023 Python_Bot <Python_Bot@openeuler.org> - 0.15.0-1 +- Package Spec generated @@ -0,0 +1 @@ +e37b74a4b7b3b441c576af94d3dd89e8 grpc-interceptor-0.15.0.tar.gz |