diff options
Diffstat (limited to 'python-django-grpc.spec')
| -rw-r--r-- | python-django-grpc.spec | 613 |
1 files changed, 613 insertions, 0 deletions
diff --git a/python-django-grpc.spec b/python-django-grpc.spec new file mode 100644 index 0000000..751641c --- /dev/null +++ b/python-django-grpc.spec @@ -0,0 +1,613 @@ +%global _empty_manifest_terminate_build 0 +Name: python-django-grpc +Version: 1.0.19 +Release: 1 +Summary: Easy Django based gRPC service +License: MIT +URL: https://github.com/gluk-w/django-grpc +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/e4/14/ee38b1398d47697425c3d4396705fc83bdfdb0fb34d06891ea6bf6d7a54a/django-grpc-1.0.19.tar.gz +BuildArch: noarch + +Requires: python3-setuptools + +%description +# django-grpc + +[](https://circleci.com/gh/gluk-w/django-grpc) + + +Easy way to launch gRPC server with access to Django ORM and other handy stuff. +gRPC calls are much faster that traditional HTTP requests because communicate over +persistent connection and are compressed. Underlying gRPC library is written in C which +makes it work faster than any RESTful framework where a lot of time is spent on serialization/deserialization. + +Note that you need this project only if you want to use Django functionality in gRPC service. +For pure python implementation [read this](https://grpc.io/docs/languages/python/quickstart/) + +* Supported Python: 3.4+ +* Supported Django: 2.X and 3.X + +## Installation + +```bash +pip install django-grpc +``` + +Update settings.py +```python +INSTALLED_APPS = [ + # ... + 'django_grpc', +] + +GRPCSERVER = { + 'servicers': ['dotted.path.to.callback.eg.grpc_hook'], # see `grpc_hook()` below + 'interceptors': ['dotted.path.to.interceptor_class',], # optional, interceprots are similar to middleware in Django + 'maximum_concurrent_rpcs': None, + 'options': [("grpc.max_receive_message_length", 1024 * 1024 * 100)], # optional, list of key-value pairs to configure the channel. The full list of available channel arguments: https://grpc.github.io/grpc/core/group__grpc__arg__keys.html + 'credentials': [{ + 'private_key': 'private_key.pem', + 'certificate_chain': 'certificate_chain.pem' + }], # required only if SSL/TLS support is required to be enabled + 'async': False # Default: False, if True then gRPC server will start in ASYNC mode +} +``` + +The callback that initializes "servicer" must look like following: +```python +import my_pb2 +import my_pb2_grpc + +def grpc_hook(server): + my_pb2_grpc.add_MYServicer_to_server(MYServicer(), server) + +... +class MYServicer(my_pb2_grpc.MYServicer): + + def GetPage(self, request, context): + response = my_pb2.PageResponse(title="Demo object") + return response +``` + +## Usage +```bash +python manage.py grpcserver +``` + +For developer's convenience add `--autoreload` flag during development. + + +## Signals +The package uses Django signals to allow decoupled applications get notified when some actions occur: +* `django_grpc.signals.grpc_request_started` - sent before gRPC server begins processing a request +* `django_grpc.signals.grpc_request_finished` - sent when gRPC server finishes delivering response to the client +* `django_grpc.signals.grpc_got_request_exception` - this signal is sent whenever RPC encounters an exception while +processing an incoming request. + +Note that signal names are similar to Django's built-in signals, but have "grpc_" prefix. + + +## Serializers +There is an easy way to serialize django model to gRPC message using `django_grpc.serializers.serialize_model`. + +## Helpers + +### Ratelimits + +You can limit number of requests to your procedures by using decorator `django_grpc.helpers.ratelimit.ratelimit`. + +```python +from tests.sampleapp import helloworld_pb2_grpc, helloworld_pb2 +from django_grpc.helpers import ratelimit + + +class Greeter(helloworld_pb2_grpc.GreeterServicer): + + @ratelimit(max_calls=10, time_period=60) + def SayHello(self, request, context): + return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) +``` +> When limit is reached for given time period decorator will abort with status `grpc.StatusCode.RESOURCE_EXHAUSTED` + +As storage for state of calls [Django's cache framework](https://docs.djangoproject.com/en/4.0/topics/cache/#django-s-cache-framework) +is used. By default `"default"` cache system is used but you can specify any other in settings `RATELIMIT_USE_CACHE` + +#### Advanced usage + +Using groups +```python +@ratelimit(max_calls=10, time_period=60, group="main") +def foo(request, context): + ... + +@ratelimit(max_calls=5, time_period=60, group="main") +def bar(request, context): + ... +``` +`foo` and `bar` will share the same counter because they are in the same group + +Using keys +```python +@ratelimit(max_calls=5, time_period=10, keys=["request:dot.path.to.field"]) +@ratelimit(max_calls=5, time_period=10, keys=["metadata:user-agent"]) +@ratelimit(max_calls=5, time_period=10, keys=[lambda request, context: context.peer()]) +``` +Right now 3 type of keys are supported with prefixes `"request:"`, `"metadata:"` and as callable. + +- `"request:"` allows to extract request's field value by doted path +- `"metadata:"` allows to extract metadata from `context.invocation_metadata()` +- callable function that takes request and context and returns string + +> NOTE: if value of key is empty string it still will be considered a valid value +> and can cause sharing of ratelimits between different RPCs in the same group + +> TIP: To use the same configuration for different RPCs use dict variable +> ```python +> MAIN_GROUP = {"max_calls": 5, "time_period": 60, "group": "main"} +> +> @ratelimit(**MAIN_GROUP) +> def foo(request, context): +> ... +> +> @ratelimit(**MAIN_GROUP) +> def bar(request, context): +> ... +> ``` + + +## Testing +Test your RPCs just like regular python methods which return some +structure or generator. You need to provide them with only 2 parameters: +request (protobuf structure or generator) and context (use `FakeServicerContext` from the example below). + +### Fake Context +You can pass instance of `django_grpc_testtools.context.FakeServicerContext` to your gRPC method +to verify how it works with context (aborts, metadata and etc.). +```python +import grpc +from django_grpc_testtools.context import FakeServicerContext +from tests.sampleapp.servicer import Greeter +from tests.sampleapp.helloworld_pb2 import HelloRequest + +servicer = Greeter() +context = FakeServicerContext() +request = HelloRequest(name='Tester') + +# To check metadata set by RPC +response = servicer.SayHello(request, context) +assert context.get_trailing_metadata("Header1") == '...' + +# To check status code +try: + servicer.SayHello(request, context) +except Exception: + pass + +assert context.abort_status == grpc.StatusCode.INVALID_ARGUMENT +assert context.abort_message == 'Cannot say hello to John' +``` + +In addition to standard gRPC context methods, FakeServicerContext provides: + * `.set_invocation_metadata()` allows to simulate metadata from client to server. + * `.get_trailing_metadata()` to get metadata set by your server + * `.abort_status` and `.abort_message` to check if `.abort()` was called + + +%package -n python3-django-grpc +Summary: Easy Django based gRPC service +Provides: python-django-grpc +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-django-grpc +# django-grpc + +[](https://circleci.com/gh/gluk-w/django-grpc) + + +Easy way to launch gRPC server with access to Django ORM and other handy stuff. +gRPC calls are much faster that traditional HTTP requests because communicate over +persistent connection and are compressed. Underlying gRPC library is written in C which +makes it work faster than any RESTful framework where a lot of time is spent on serialization/deserialization. + +Note that you need this project only if you want to use Django functionality in gRPC service. +For pure python implementation [read this](https://grpc.io/docs/languages/python/quickstart/) + +* Supported Python: 3.4+ +* Supported Django: 2.X and 3.X + +## Installation + +```bash +pip install django-grpc +``` + +Update settings.py +```python +INSTALLED_APPS = [ + # ... + 'django_grpc', +] + +GRPCSERVER = { + 'servicers': ['dotted.path.to.callback.eg.grpc_hook'], # see `grpc_hook()` below + 'interceptors': ['dotted.path.to.interceptor_class',], # optional, interceprots are similar to middleware in Django + 'maximum_concurrent_rpcs': None, + 'options': [("grpc.max_receive_message_length", 1024 * 1024 * 100)], # optional, list of key-value pairs to configure the channel. The full list of available channel arguments: https://grpc.github.io/grpc/core/group__grpc__arg__keys.html + 'credentials': [{ + 'private_key': 'private_key.pem', + 'certificate_chain': 'certificate_chain.pem' + }], # required only if SSL/TLS support is required to be enabled + 'async': False # Default: False, if True then gRPC server will start in ASYNC mode +} +``` + +The callback that initializes "servicer" must look like following: +```python +import my_pb2 +import my_pb2_grpc + +def grpc_hook(server): + my_pb2_grpc.add_MYServicer_to_server(MYServicer(), server) + +... +class MYServicer(my_pb2_grpc.MYServicer): + + def GetPage(self, request, context): + response = my_pb2.PageResponse(title="Demo object") + return response +``` + +## Usage +```bash +python manage.py grpcserver +``` + +For developer's convenience add `--autoreload` flag during development. + + +## Signals +The package uses Django signals to allow decoupled applications get notified when some actions occur: +* `django_grpc.signals.grpc_request_started` - sent before gRPC server begins processing a request +* `django_grpc.signals.grpc_request_finished` - sent when gRPC server finishes delivering response to the client +* `django_grpc.signals.grpc_got_request_exception` - this signal is sent whenever RPC encounters an exception while +processing an incoming request. + +Note that signal names are similar to Django's built-in signals, but have "grpc_" prefix. + + +## Serializers +There is an easy way to serialize django model to gRPC message using `django_grpc.serializers.serialize_model`. + +## Helpers + +### Ratelimits + +You can limit number of requests to your procedures by using decorator `django_grpc.helpers.ratelimit.ratelimit`. + +```python +from tests.sampleapp import helloworld_pb2_grpc, helloworld_pb2 +from django_grpc.helpers import ratelimit + + +class Greeter(helloworld_pb2_grpc.GreeterServicer): + + @ratelimit(max_calls=10, time_period=60) + def SayHello(self, request, context): + return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) +``` +> When limit is reached for given time period decorator will abort with status `grpc.StatusCode.RESOURCE_EXHAUSTED` + +As storage for state of calls [Django's cache framework](https://docs.djangoproject.com/en/4.0/topics/cache/#django-s-cache-framework) +is used. By default `"default"` cache system is used but you can specify any other in settings `RATELIMIT_USE_CACHE` + +#### Advanced usage + +Using groups +```python +@ratelimit(max_calls=10, time_period=60, group="main") +def foo(request, context): + ... + +@ratelimit(max_calls=5, time_period=60, group="main") +def bar(request, context): + ... +``` +`foo` and `bar` will share the same counter because they are in the same group + +Using keys +```python +@ratelimit(max_calls=5, time_period=10, keys=["request:dot.path.to.field"]) +@ratelimit(max_calls=5, time_period=10, keys=["metadata:user-agent"]) +@ratelimit(max_calls=5, time_period=10, keys=[lambda request, context: context.peer()]) +``` +Right now 3 type of keys are supported with prefixes `"request:"`, `"metadata:"` and as callable. + +- `"request:"` allows to extract request's field value by doted path +- `"metadata:"` allows to extract metadata from `context.invocation_metadata()` +- callable function that takes request and context and returns string + +> NOTE: if value of key is empty string it still will be considered a valid value +> and can cause sharing of ratelimits between different RPCs in the same group + +> TIP: To use the same configuration for different RPCs use dict variable +> ```python +> MAIN_GROUP = {"max_calls": 5, "time_period": 60, "group": "main"} +> +> @ratelimit(**MAIN_GROUP) +> def foo(request, context): +> ... +> +> @ratelimit(**MAIN_GROUP) +> def bar(request, context): +> ... +> ``` + + +## Testing +Test your RPCs just like regular python methods which return some +structure or generator. You need to provide them with only 2 parameters: +request (protobuf structure or generator) and context (use `FakeServicerContext` from the example below). + +### Fake Context +You can pass instance of `django_grpc_testtools.context.FakeServicerContext` to your gRPC method +to verify how it works with context (aborts, metadata and etc.). +```python +import grpc +from django_grpc_testtools.context import FakeServicerContext +from tests.sampleapp.servicer import Greeter +from tests.sampleapp.helloworld_pb2 import HelloRequest + +servicer = Greeter() +context = FakeServicerContext() +request = HelloRequest(name='Tester') + +# To check metadata set by RPC +response = servicer.SayHello(request, context) +assert context.get_trailing_metadata("Header1") == '...' + +# To check status code +try: + servicer.SayHello(request, context) +except Exception: + pass + +assert context.abort_status == grpc.StatusCode.INVALID_ARGUMENT +assert context.abort_message == 'Cannot say hello to John' +``` + +In addition to standard gRPC context methods, FakeServicerContext provides: + * `.set_invocation_metadata()` allows to simulate metadata from client to server. + * `.get_trailing_metadata()` to get metadata set by your server + * `.abort_status` and `.abort_message` to check if `.abort()` was called + + +%package help +Summary: Development documents and examples for django-grpc +Provides: python3-django-grpc-doc +%description help +# django-grpc + +[](https://circleci.com/gh/gluk-w/django-grpc) + + +Easy way to launch gRPC server with access to Django ORM and other handy stuff. +gRPC calls are much faster that traditional HTTP requests because communicate over +persistent connection and are compressed. Underlying gRPC library is written in C which +makes it work faster than any RESTful framework where a lot of time is spent on serialization/deserialization. + +Note that you need this project only if you want to use Django functionality in gRPC service. +For pure python implementation [read this](https://grpc.io/docs/languages/python/quickstart/) + +* Supported Python: 3.4+ +* Supported Django: 2.X and 3.X + +## Installation + +```bash +pip install django-grpc +``` + +Update settings.py +```python +INSTALLED_APPS = [ + # ... + 'django_grpc', +] + +GRPCSERVER = { + 'servicers': ['dotted.path.to.callback.eg.grpc_hook'], # see `grpc_hook()` below + 'interceptors': ['dotted.path.to.interceptor_class',], # optional, interceprots are similar to middleware in Django + 'maximum_concurrent_rpcs': None, + 'options': [("grpc.max_receive_message_length", 1024 * 1024 * 100)], # optional, list of key-value pairs to configure the channel. The full list of available channel arguments: https://grpc.github.io/grpc/core/group__grpc__arg__keys.html + 'credentials': [{ + 'private_key': 'private_key.pem', + 'certificate_chain': 'certificate_chain.pem' + }], # required only if SSL/TLS support is required to be enabled + 'async': False # Default: False, if True then gRPC server will start in ASYNC mode +} +``` + +The callback that initializes "servicer" must look like following: +```python +import my_pb2 +import my_pb2_grpc + +def grpc_hook(server): + my_pb2_grpc.add_MYServicer_to_server(MYServicer(), server) + +... +class MYServicer(my_pb2_grpc.MYServicer): + + def GetPage(self, request, context): + response = my_pb2.PageResponse(title="Demo object") + return response +``` + +## Usage +```bash +python manage.py grpcserver +``` + +For developer's convenience add `--autoreload` flag during development. + + +## Signals +The package uses Django signals to allow decoupled applications get notified when some actions occur: +* `django_grpc.signals.grpc_request_started` - sent before gRPC server begins processing a request +* `django_grpc.signals.grpc_request_finished` - sent when gRPC server finishes delivering response to the client +* `django_grpc.signals.grpc_got_request_exception` - this signal is sent whenever RPC encounters an exception while +processing an incoming request. + +Note that signal names are similar to Django's built-in signals, but have "grpc_" prefix. + + +## Serializers +There is an easy way to serialize django model to gRPC message using `django_grpc.serializers.serialize_model`. + +## Helpers + +### Ratelimits + +You can limit number of requests to your procedures by using decorator `django_grpc.helpers.ratelimit.ratelimit`. + +```python +from tests.sampleapp import helloworld_pb2_grpc, helloworld_pb2 +from django_grpc.helpers import ratelimit + + +class Greeter(helloworld_pb2_grpc.GreeterServicer): + + @ratelimit(max_calls=10, time_period=60) + def SayHello(self, request, context): + return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) +``` +> When limit is reached for given time period decorator will abort with status `grpc.StatusCode.RESOURCE_EXHAUSTED` + +As storage for state of calls [Django's cache framework](https://docs.djangoproject.com/en/4.0/topics/cache/#django-s-cache-framework) +is used. By default `"default"` cache system is used but you can specify any other in settings `RATELIMIT_USE_CACHE` + +#### Advanced usage + +Using groups +```python +@ratelimit(max_calls=10, time_period=60, group="main") +def foo(request, context): + ... + +@ratelimit(max_calls=5, time_period=60, group="main") +def bar(request, context): + ... +``` +`foo` and `bar` will share the same counter because they are in the same group + +Using keys +```python +@ratelimit(max_calls=5, time_period=10, keys=["request:dot.path.to.field"]) +@ratelimit(max_calls=5, time_period=10, keys=["metadata:user-agent"]) +@ratelimit(max_calls=5, time_period=10, keys=[lambda request, context: context.peer()]) +``` +Right now 3 type of keys are supported with prefixes `"request:"`, `"metadata:"` and as callable. + +- `"request:"` allows to extract request's field value by doted path +- `"metadata:"` allows to extract metadata from `context.invocation_metadata()` +- callable function that takes request and context and returns string + +> NOTE: if value of key is empty string it still will be considered a valid value +> and can cause sharing of ratelimits between different RPCs in the same group + +> TIP: To use the same configuration for different RPCs use dict variable +> ```python +> MAIN_GROUP = {"max_calls": 5, "time_period": 60, "group": "main"} +> +> @ratelimit(**MAIN_GROUP) +> def foo(request, context): +> ... +> +> @ratelimit(**MAIN_GROUP) +> def bar(request, context): +> ... +> ``` + + +## Testing +Test your RPCs just like regular python methods which return some +structure or generator. You need to provide them with only 2 parameters: +request (protobuf structure or generator) and context (use `FakeServicerContext` from the example below). + +### Fake Context +You can pass instance of `django_grpc_testtools.context.FakeServicerContext` to your gRPC method +to verify how it works with context (aborts, metadata and etc.). +```python +import grpc +from django_grpc_testtools.context import FakeServicerContext +from tests.sampleapp.servicer import Greeter +from tests.sampleapp.helloworld_pb2 import HelloRequest + +servicer = Greeter() +context = FakeServicerContext() +request = HelloRequest(name='Tester') + +# To check metadata set by RPC +response = servicer.SayHello(request, context) +assert context.get_trailing_metadata("Header1") == '...' + +# To check status code +try: + servicer.SayHello(request, context) +except Exception: + pass + +assert context.abort_status == grpc.StatusCode.INVALID_ARGUMENT +assert context.abort_message == 'Cannot say hello to John' +``` + +In addition to standard gRPC context methods, FakeServicerContext provides: + * `.set_invocation_metadata()` allows to simulate metadata from client to server. + * `.get_trailing_metadata()` to get metadata set by your server + * `.abort_status` and `.abort_message` to check if `.abort()` was called + + +%prep +%autosetup -n django-grpc-1.0.19 + +%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-django-grpc -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Wed Apr 12 2023 Python_Bot <Python_Bot@openeuler.org> - 1.0.19-1 +- Package Spec generated |
