Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +2 -0
- .venv/lib/python3.11/site-packages/google/protobuf/__pycache__/descriptor_pb2.cpython-311.pyc +3 -0
- .venv/lib/python3.11/site-packages/grpc/__init__.py +2348 -0
- .venv/lib/python3.11/site-packages/grpc/_auth.py +80 -0
- .venv/lib/python3.11/site-packages/grpc/_channel.py +2267 -0
- .venv/lib/python3.11/site-packages/grpc/_common.py +183 -0
- .venv/lib/python3.11/site-packages/grpc/_compression.py +71 -0
- .venv/lib/python3.11/site-packages/grpc/_grpcio_metadata.py +1 -0
- .venv/lib/python3.11/site-packages/grpc/_interceptor.py +813 -0
- .venv/lib/python3.11/site-packages/grpc/_observability.py +299 -0
- .venv/lib/python3.11/site-packages/grpc/_plugin_wrapping.py +136 -0
- .venv/lib/python3.11/site-packages/grpc/_runtime_protos.py +165 -0
- .venv/lib/python3.11/site-packages/grpc/_server.py +1528 -0
- .venv/lib/python3.11/site-packages/grpc/_simple_stubs.py +588 -0
- .venv/lib/python3.11/site-packages/grpc/_typing.py +95 -0
- .venv/lib/python3.11/site-packages/grpc/_utilities.py +222 -0
- .venv/lib/python3.11/site-packages/grpc/beta/__init__.py +13 -0
- .venv/lib/python3.11/site-packages/grpc/beta/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/beta/__pycache__/_client_adaptations.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/beta/__pycache__/_metadata.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/beta/__pycache__/_server_adaptations.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/beta/__pycache__/implementations.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/beta/__pycache__/interfaces.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/beta/__pycache__/utilities.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/beta/_client_adaptations.py +1015 -0
- .venv/lib/python3.11/site-packages/grpc/beta/_metadata.py +56 -0
- .venv/lib/python3.11/site-packages/grpc/beta/_server_adaptations.py +465 -0
- .venv/lib/python3.11/site-packages/grpc/beta/implementations.py +345 -0
- .venv/lib/python3.11/site-packages/grpc/beta/interfaces.py +163 -0
- .venv/lib/python3.11/site-packages/grpc/beta/utilities.py +153 -0
- .venv/lib/python3.11/site-packages/grpc/framework/__init__.py +13 -0
- .venv/lib/python3.11/site-packages/grpc/framework/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/framework/common/__pycache__/cardinality.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/framework/common/__pycache__/style.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/framework/common/cardinality.py +26 -0
- .venv/lib/python3.11/site-packages/grpc/framework/common/style.py +24 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/__init__.py +13 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/abandonment.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/callable_util.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/future.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/logging_pool.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/stream.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/stream_util.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/abandonment.py +22 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/callable_util.py +98 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/future.py +219 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/logging_pool.py +72 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/stream.py +43 -0
- .venv/lib/python3.11/site-packages/grpc/framework/foundation/stream_util.py +148 -0
.gitattributes
CHANGED
|
@@ -197,3 +197,5 @@ tuning-competition-baseline/.venv/lib/python3.11/site-packages/torch/_inductor/_
|
|
| 197 |
.venv/lib/python3.11/site-packages/google/ai/generativelanguage_v1beta/services/retriever_service/transports/__pycache__/rest.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
|
| 198 |
.venv/lib/python3.11/site-packages/google/ai/generativelanguage_v1beta/services/retriever_service/__pycache__/client.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
|
| 199 |
.venv/lib/python3.11/site-packages/google/_upb/_message.abi3.so filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 197 |
.venv/lib/python3.11/site-packages/google/ai/generativelanguage_v1beta/services/retriever_service/transports/__pycache__/rest.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
|
| 198 |
.venv/lib/python3.11/site-packages/google/ai/generativelanguage_v1beta/services/retriever_service/__pycache__/client.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
|
| 199 |
.venv/lib/python3.11/site-packages/google/_upb/_message.abi3.so filter=lfs diff=lfs merge=lfs -text
|
| 200 |
+
.venv/lib/python3.11/site-packages/google/protobuf/__pycache__/descriptor_pb2.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
|
| 201 |
+
.venv/lib/python3.11/site-packages/jinja2/__pycache__/compiler.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
|
.venv/lib/python3.11/site-packages/google/protobuf/__pycache__/descriptor_pb2.cpython-311.pyc
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ec80fe54a25929c21e89c448134a6a374d7c2cb8c3f5630b36efe1cb827a7c39
|
| 3 |
+
size 281657
|
.venv/lib/python3.11/site-packages/grpc/__init__.py
ADDED
|
@@ -0,0 +1,2348 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015-2016 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""gRPC's Python API."""
|
| 15 |
+
|
| 16 |
+
import abc
|
| 17 |
+
import contextlib
|
| 18 |
+
import enum
|
| 19 |
+
import logging
|
| 20 |
+
import sys
|
| 21 |
+
|
| 22 |
+
from grpc import _compression
|
| 23 |
+
from grpc._cython import cygrpc as _cygrpc
|
| 24 |
+
from grpc._runtime_protos import protos
|
| 25 |
+
from grpc._runtime_protos import protos_and_services
|
| 26 |
+
from grpc._runtime_protos import services
|
| 27 |
+
|
| 28 |
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
| 29 |
+
|
| 30 |
+
try:
|
| 31 |
+
# pylint: disable=ungrouped-imports
|
| 32 |
+
from grpc._grpcio_metadata import __version__
|
| 33 |
+
except ImportError:
|
| 34 |
+
__version__ = "dev0"
|
| 35 |
+
|
| 36 |
+
############################## Future Interface ###############################
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class FutureTimeoutError(Exception):
|
| 40 |
+
"""Indicates that a method call on a Future timed out."""
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class FutureCancelledError(Exception):
|
| 44 |
+
"""Indicates that the computation underlying a Future was cancelled."""
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class Future(abc.ABC):
|
| 48 |
+
"""A representation of a computation in another control flow.
|
| 49 |
+
|
| 50 |
+
Computations represented by a Future may be yet to be begun,
|
| 51 |
+
may be ongoing, or may have already completed.
|
| 52 |
+
"""
|
| 53 |
+
|
| 54 |
+
@abc.abstractmethod
|
| 55 |
+
def cancel(self):
|
| 56 |
+
"""Attempts to cancel the computation.
|
| 57 |
+
|
| 58 |
+
This method does not block.
|
| 59 |
+
|
| 60 |
+
Returns:
|
| 61 |
+
bool:
|
| 62 |
+
Returns True if the computation was canceled.
|
| 63 |
+
|
| 64 |
+
Returns False under all other circumstances, for example:
|
| 65 |
+
|
| 66 |
+
1. computation has begun and could not be canceled.
|
| 67 |
+
2. computation has finished
|
| 68 |
+
3. computation is scheduled for execution and it is impossible
|
| 69 |
+
to determine its state without blocking.
|
| 70 |
+
"""
|
| 71 |
+
raise NotImplementedError()
|
| 72 |
+
|
| 73 |
+
@abc.abstractmethod
|
| 74 |
+
def cancelled(self):
|
| 75 |
+
"""Describes whether the computation was cancelled.
|
| 76 |
+
|
| 77 |
+
This method does not block.
|
| 78 |
+
|
| 79 |
+
Returns:
|
| 80 |
+
bool:
|
| 81 |
+
Returns True if the computation was cancelled before its result became
|
| 82 |
+
available.
|
| 83 |
+
|
| 84 |
+
Returns False under all other circumstances, for example:
|
| 85 |
+
|
| 86 |
+
1. computation was not cancelled.
|
| 87 |
+
2. computation's result is available.
|
| 88 |
+
"""
|
| 89 |
+
raise NotImplementedError()
|
| 90 |
+
|
| 91 |
+
@abc.abstractmethod
|
| 92 |
+
def running(self):
|
| 93 |
+
"""Describes whether the computation is taking place.
|
| 94 |
+
|
| 95 |
+
This method does not block.
|
| 96 |
+
|
| 97 |
+
Returns:
|
| 98 |
+
Returns True if the computation is scheduled for execution or
|
| 99 |
+
currently executing.
|
| 100 |
+
|
| 101 |
+
Returns False if the computation already executed or was cancelled.
|
| 102 |
+
"""
|
| 103 |
+
raise NotImplementedError()
|
| 104 |
+
|
| 105 |
+
@abc.abstractmethod
|
| 106 |
+
def done(self):
|
| 107 |
+
"""Describes whether the computation has taken place.
|
| 108 |
+
|
| 109 |
+
This method does not block.
|
| 110 |
+
|
| 111 |
+
Returns:
|
| 112 |
+
bool:
|
| 113 |
+
Returns True if the computation already executed or was cancelled.
|
| 114 |
+
Returns False if the computation is scheduled for execution or
|
| 115 |
+
currently executing.
|
| 116 |
+
This is exactly opposite of the running() method's result.
|
| 117 |
+
"""
|
| 118 |
+
raise NotImplementedError()
|
| 119 |
+
|
| 120 |
+
@abc.abstractmethod
|
| 121 |
+
def result(self, timeout=None):
|
| 122 |
+
"""Returns the result of the computation or raises its exception.
|
| 123 |
+
|
| 124 |
+
This method may return immediately or may block.
|
| 125 |
+
|
| 126 |
+
Args:
|
| 127 |
+
timeout: The length of time in seconds to wait for the computation to
|
| 128 |
+
finish or be cancelled. If None, the call will block until the
|
| 129 |
+
computations's termination.
|
| 130 |
+
|
| 131 |
+
Returns:
|
| 132 |
+
The return value of the computation.
|
| 133 |
+
|
| 134 |
+
Raises:
|
| 135 |
+
FutureTimeoutError: If a timeout value is passed and the computation
|
| 136 |
+
does not terminate within the allotted time.
|
| 137 |
+
FutureCancelledError: If the computation was cancelled.
|
| 138 |
+
Exception: If the computation raised an exception, this call will
|
| 139 |
+
raise the same exception.
|
| 140 |
+
"""
|
| 141 |
+
raise NotImplementedError()
|
| 142 |
+
|
| 143 |
+
@abc.abstractmethod
|
| 144 |
+
def exception(self, timeout=None):
|
| 145 |
+
"""Return the exception raised by the computation.
|
| 146 |
+
|
| 147 |
+
This method may return immediately or may block.
|
| 148 |
+
|
| 149 |
+
Args:
|
| 150 |
+
timeout: The length of time in seconds to wait for the computation to
|
| 151 |
+
terminate or be cancelled. If None, the call will block until the
|
| 152 |
+
computations's termination.
|
| 153 |
+
|
| 154 |
+
Returns:
|
| 155 |
+
The exception raised by the computation, or None if the computation
|
| 156 |
+
did not raise an exception.
|
| 157 |
+
|
| 158 |
+
Raises:
|
| 159 |
+
FutureTimeoutError: If a timeout value is passed and the computation
|
| 160 |
+
does not terminate within the allotted time.
|
| 161 |
+
FutureCancelledError: If the computation was cancelled.
|
| 162 |
+
"""
|
| 163 |
+
raise NotImplementedError()
|
| 164 |
+
|
| 165 |
+
@abc.abstractmethod
|
| 166 |
+
def traceback(self, timeout=None):
|
| 167 |
+
"""Access the traceback of the exception raised by the computation.
|
| 168 |
+
|
| 169 |
+
This method may return immediately or may block.
|
| 170 |
+
|
| 171 |
+
Args:
|
| 172 |
+
timeout: The length of time in seconds to wait for the computation
|
| 173 |
+
to terminate or be cancelled. If None, the call will block until
|
| 174 |
+
the computation's termination.
|
| 175 |
+
|
| 176 |
+
Returns:
|
| 177 |
+
The traceback of the exception raised by the computation, or None
|
| 178 |
+
if the computation did not raise an exception.
|
| 179 |
+
|
| 180 |
+
Raises:
|
| 181 |
+
FutureTimeoutError: If a timeout value is passed and the computation
|
| 182 |
+
does not terminate within the allotted time.
|
| 183 |
+
FutureCancelledError: If the computation was cancelled.
|
| 184 |
+
"""
|
| 185 |
+
raise NotImplementedError()
|
| 186 |
+
|
| 187 |
+
@abc.abstractmethod
|
| 188 |
+
def add_done_callback(self, fn):
|
| 189 |
+
"""Adds a function to be called at completion of the computation.
|
| 190 |
+
|
| 191 |
+
The callback will be passed this Future object describing the outcome
|
| 192 |
+
of the computation. Callbacks will be invoked after the future is
|
| 193 |
+
terminated, whether successfully or not.
|
| 194 |
+
|
| 195 |
+
If the computation has already completed, the callback will be called
|
| 196 |
+
immediately.
|
| 197 |
+
|
| 198 |
+
Exceptions raised in the callback will be logged at ERROR level, but
|
| 199 |
+
will not terminate any threads of execution.
|
| 200 |
+
|
| 201 |
+
Args:
|
| 202 |
+
fn: A callable taking this Future object as its single parameter.
|
| 203 |
+
"""
|
| 204 |
+
raise NotImplementedError()
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
################################ gRPC Enums ##################################
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
@enum.unique
|
| 211 |
+
class ChannelConnectivity(enum.Enum):
|
| 212 |
+
"""Mirrors grpc_connectivity_state in the gRPC Core.
|
| 213 |
+
|
| 214 |
+
Attributes:
|
| 215 |
+
IDLE: The channel is idle.
|
| 216 |
+
CONNECTING: The channel is connecting.
|
| 217 |
+
READY: The channel is ready to conduct RPCs.
|
| 218 |
+
TRANSIENT_FAILURE: The channel has seen a failure from which it expects
|
| 219 |
+
to recover.
|
| 220 |
+
SHUTDOWN: The channel has seen a failure from which it cannot recover.
|
| 221 |
+
"""
|
| 222 |
+
|
| 223 |
+
IDLE = (_cygrpc.ConnectivityState.idle, "idle")
|
| 224 |
+
CONNECTING = (_cygrpc.ConnectivityState.connecting, "connecting")
|
| 225 |
+
READY = (_cygrpc.ConnectivityState.ready, "ready")
|
| 226 |
+
TRANSIENT_FAILURE = (
|
| 227 |
+
_cygrpc.ConnectivityState.transient_failure,
|
| 228 |
+
"transient failure",
|
| 229 |
+
)
|
| 230 |
+
SHUTDOWN = (_cygrpc.ConnectivityState.shutdown, "shutdown")
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
@enum.unique
|
| 234 |
+
class StatusCode(enum.Enum):
|
| 235 |
+
"""Mirrors grpc_status_code in the gRPC Core.
|
| 236 |
+
|
| 237 |
+
Attributes:
|
| 238 |
+
OK: Not an error; returned on success
|
| 239 |
+
CANCELLED: The operation was cancelled (typically by the caller).
|
| 240 |
+
UNKNOWN: Unknown error.
|
| 241 |
+
INVALID_ARGUMENT: Client specified an invalid argument.
|
| 242 |
+
DEADLINE_EXCEEDED: Deadline expired before operation could complete.
|
| 243 |
+
NOT_FOUND: Some requested entity (e.g., file or directory) was not found.
|
| 244 |
+
ALREADY_EXISTS: Some entity that we attempted to create (e.g., file or directory)
|
| 245 |
+
already exists.
|
| 246 |
+
PERMISSION_DENIED: The caller does not have permission to execute the specified
|
| 247 |
+
operation.
|
| 248 |
+
UNAUTHENTICATED: The request does not have valid authentication credentials for the
|
| 249 |
+
operation.
|
| 250 |
+
RESOURCE_EXHAUSTED: Some resource has been exhausted, perhaps a per-user quota, or
|
| 251 |
+
perhaps the entire file system is out of space.
|
| 252 |
+
FAILED_PRECONDITION: Operation was rejected because the system is not in a state
|
| 253 |
+
required for the operation's execution.
|
| 254 |
+
ABORTED: The operation was aborted, typically due to a concurrency issue
|
| 255 |
+
like sequencer check failures, transaction aborts, etc.
|
| 256 |
+
UNIMPLEMENTED: Operation is not implemented or not supported/enabled in this service.
|
| 257 |
+
INTERNAL: Internal errors. Means some invariants expected by underlying
|
| 258 |
+
system has been broken.
|
| 259 |
+
UNAVAILABLE: The service is currently unavailable.
|
| 260 |
+
DATA_LOSS: Unrecoverable data loss or corruption.
|
| 261 |
+
"""
|
| 262 |
+
|
| 263 |
+
OK = (_cygrpc.StatusCode.ok, "ok")
|
| 264 |
+
CANCELLED = (_cygrpc.StatusCode.cancelled, "cancelled")
|
| 265 |
+
UNKNOWN = (_cygrpc.StatusCode.unknown, "unknown")
|
| 266 |
+
INVALID_ARGUMENT = (_cygrpc.StatusCode.invalid_argument, "invalid argument")
|
| 267 |
+
DEADLINE_EXCEEDED = (
|
| 268 |
+
_cygrpc.StatusCode.deadline_exceeded,
|
| 269 |
+
"deadline exceeded",
|
| 270 |
+
)
|
| 271 |
+
NOT_FOUND = (_cygrpc.StatusCode.not_found, "not found")
|
| 272 |
+
ALREADY_EXISTS = (_cygrpc.StatusCode.already_exists, "already exists")
|
| 273 |
+
PERMISSION_DENIED = (
|
| 274 |
+
_cygrpc.StatusCode.permission_denied,
|
| 275 |
+
"permission denied",
|
| 276 |
+
)
|
| 277 |
+
RESOURCE_EXHAUSTED = (
|
| 278 |
+
_cygrpc.StatusCode.resource_exhausted,
|
| 279 |
+
"resource exhausted",
|
| 280 |
+
)
|
| 281 |
+
FAILED_PRECONDITION = (
|
| 282 |
+
_cygrpc.StatusCode.failed_precondition,
|
| 283 |
+
"failed precondition",
|
| 284 |
+
)
|
| 285 |
+
ABORTED = (_cygrpc.StatusCode.aborted, "aborted")
|
| 286 |
+
OUT_OF_RANGE = (_cygrpc.StatusCode.out_of_range, "out of range")
|
| 287 |
+
UNIMPLEMENTED = (_cygrpc.StatusCode.unimplemented, "unimplemented")
|
| 288 |
+
INTERNAL = (_cygrpc.StatusCode.internal, "internal")
|
| 289 |
+
UNAVAILABLE = (_cygrpc.StatusCode.unavailable, "unavailable")
|
| 290 |
+
DATA_LOSS = (_cygrpc.StatusCode.data_loss, "data loss")
|
| 291 |
+
UNAUTHENTICATED = (_cygrpc.StatusCode.unauthenticated, "unauthenticated")
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
############################# gRPC Status ################################
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
class Status(abc.ABC):
|
| 298 |
+
"""Describes the status of an RPC.
|
| 299 |
+
|
| 300 |
+
This is an EXPERIMENTAL API.
|
| 301 |
+
|
| 302 |
+
Attributes:
|
| 303 |
+
code: A StatusCode object to be sent to the client.
|
| 304 |
+
details: A UTF-8-encodable string to be sent to the client upon
|
| 305 |
+
termination of the RPC.
|
| 306 |
+
trailing_metadata: The trailing :term:`metadata` in the RPC.
|
| 307 |
+
"""
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
############################# gRPC Exceptions ################################
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
class RpcError(Exception):
|
| 314 |
+
"""Raised by the gRPC library to indicate non-OK-status RPC termination."""
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
############################## Shared Context ################################
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
class RpcContext(abc.ABC):
|
| 321 |
+
"""Provides RPC-related information and action."""
|
| 322 |
+
|
| 323 |
+
@abc.abstractmethod
|
| 324 |
+
def is_active(self):
|
| 325 |
+
"""Describes whether the RPC is active or has terminated.
|
| 326 |
+
|
| 327 |
+
Returns:
|
| 328 |
+
bool:
|
| 329 |
+
True if RPC is active, False otherwise.
|
| 330 |
+
"""
|
| 331 |
+
raise NotImplementedError()
|
| 332 |
+
|
| 333 |
+
@abc.abstractmethod
|
| 334 |
+
def time_remaining(self):
|
| 335 |
+
"""Describes the length of allowed time remaining for the RPC.
|
| 336 |
+
|
| 337 |
+
Returns:
|
| 338 |
+
A nonnegative float indicating the length of allowed time in seconds
|
| 339 |
+
remaining for the RPC to complete before it is considered to have
|
| 340 |
+
timed out, or None if no deadline was specified for the RPC.
|
| 341 |
+
"""
|
| 342 |
+
raise NotImplementedError()
|
| 343 |
+
|
| 344 |
+
@abc.abstractmethod
|
| 345 |
+
def cancel(self):
|
| 346 |
+
"""Cancels the RPC.
|
| 347 |
+
|
| 348 |
+
Idempotent and has no effect if the RPC has already terminated.
|
| 349 |
+
"""
|
| 350 |
+
raise NotImplementedError()
|
| 351 |
+
|
| 352 |
+
@abc.abstractmethod
|
| 353 |
+
def add_callback(self, callback):
|
| 354 |
+
"""Registers a callback to be called on RPC termination.
|
| 355 |
+
|
| 356 |
+
Args:
|
| 357 |
+
callback: A no-parameter callable to be called on RPC termination.
|
| 358 |
+
|
| 359 |
+
Returns:
|
| 360 |
+
True if the callback was added and will be called later; False if
|
| 361 |
+
the callback was not added and will not be called (because the RPC
|
| 362 |
+
already terminated or some other reason).
|
| 363 |
+
"""
|
| 364 |
+
raise NotImplementedError()
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
######################### Invocation-Side Context ############################
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
class Call(RpcContext, metaclass=abc.ABCMeta):
|
| 371 |
+
"""Invocation-side utility object for an RPC."""
|
| 372 |
+
|
| 373 |
+
@abc.abstractmethod
|
| 374 |
+
def initial_metadata(self):
|
| 375 |
+
"""Accesses the initial metadata sent by the server.
|
| 376 |
+
|
| 377 |
+
This method blocks until the value is available.
|
| 378 |
+
|
| 379 |
+
Returns:
|
| 380 |
+
The initial :term:`metadata`.
|
| 381 |
+
"""
|
| 382 |
+
raise NotImplementedError()
|
| 383 |
+
|
| 384 |
+
@abc.abstractmethod
|
| 385 |
+
def trailing_metadata(self):
|
| 386 |
+
"""Accesses the trailing metadata sent by the server.
|
| 387 |
+
|
| 388 |
+
This method blocks until the value is available.
|
| 389 |
+
|
| 390 |
+
Returns:
|
| 391 |
+
The trailing :term:`metadata`.
|
| 392 |
+
"""
|
| 393 |
+
raise NotImplementedError()
|
| 394 |
+
|
| 395 |
+
@abc.abstractmethod
|
| 396 |
+
def code(self):
|
| 397 |
+
"""Accesses the status code sent by the server.
|
| 398 |
+
|
| 399 |
+
This method blocks until the value is available.
|
| 400 |
+
|
| 401 |
+
Returns:
|
| 402 |
+
The StatusCode value for the RPC.
|
| 403 |
+
"""
|
| 404 |
+
raise NotImplementedError()
|
| 405 |
+
|
| 406 |
+
@abc.abstractmethod
|
| 407 |
+
def details(self):
|
| 408 |
+
"""Accesses the details sent by the server.
|
| 409 |
+
|
| 410 |
+
This method blocks until the value is available.
|
| 411 |
+
|
| 412 |
+
Returns:
|
| 413 |
+
The details string of the RPC.
|
| 414 |
+
"""
|
| 415 |
+
raise NotImplementedError()
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
############## Invocation-Side Interceptor Interfaces & Classes ##############
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
class ClientCallDetails(abc.ABC):
|
| 422 |
+
"""Describes an RPC to be invoked.
|
| 423 |
+
|
| 424 |
+
Attributes:
|
| 425 |
+
method: The method name of the RPC.
|
| 426 |
+
timeout: An optional duration of time in seconds to allow for the RPC.
|
| 427 |
+
metadata: Optional :term:`metadata` to be transmitted to
|
| 428 |
+
the service-side of the RPC.
|
| 429 |
+
credentials: An optional CallCredentials for the RPC.
|
| 430 |
+
wait_for_ready: An optional flag to enable :term:`wait_for_ready` mechanism.
|
| 431 |
+
compression: An element of grpc.compression, e.g.
|
| 432 |
+
grpc.compression.Gzip.
|
| 433 |
+
"""
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
class UnaryUnaryClientInterceptor(abc.ABC):
|
| 437 |
+
"""Affords intercepting unary-unary invocations."""
|
| 438 |
+
|
| 439 |
+
@abc.abstractmethod
|
| 440 |
+
def intercept_unary_unary(self, continuation, client_call_details, request):
|
| 441 |
+
"""Intercepts a unary-unary invocation asynchronously.
|
| 442 |
+
|
| 443 |
+
Args:
|
| 444 |
+
continuation: A function that proceeds with the invocation by
|
| 445 |
+
executing the next interceptor in chain or invoking the
|
| 446 |
+
actual RPC on the underlying Channel. It is the interceptor's
|
| 447 |
+
responsibility to call it if it decides to move the RPC forward.
|
| 448 |
+
The interceptor can use
|
| 449 |
+
`response_future = continuation(client_call_details, request)`
|
| 450 |
+
to continue with the RPC. `continuation` returns an object that is
|
| 451 |
+
both a Call for the RPC and a Future. In the event of RPC
|
| 452 |
+
completion, the return Call-Future's result value will be
|
| 453 |
+
the response message of the RPC. Should the event terminate
|
| 454 |
+
with non-OK status, the returned Call-Future's exception value
|
| 455 |
+
will be an RpcError.
|
| 456 |
+
client_call_details: A ClientCallDetails object describing the
|
| 457 |
+
outgoing RPC.
|
| 458 |
+
request: The request value for the RPC.
|
| 459 |
+
|
| 460 |
+
Returns:
|
| 461 |
+
An object that is both a Call for the RPC and a Future.
|
| 462 |
+
In the event of RPC completion, the return Call-Future's
|
| 463 |
+
result value will be the response message of the RPC.
|
| 464 |
+
Should the event terminate with non-OK status, the returned
|
| 465 |
+
Call-Future's exception value will be an RpcError.
|
| 466 |
+
"""
|
| 467 |
+
raise NotImplementedError()
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
class UnaryStreamClientInterceptor(abc.ABC):
|
| 471 |
+
"""Affords intercepting unary-stream invocations."""
|
| 472 |
+
|
| 473 |
+
@abc.abstractmethod
|
| 474 |
+
def intercept_unary_stream(
|
| 475 |
+
self, continuation, client_call_details, request
|
| 476 |
+
):
|
| 477 |
+
"""Intercepts a unary-stream invocation.
|
| 478 |
+
|
| 479 |
+
Args:
|
| 480 |
+
continuation: A function that proceeds with the invocation by
|
| 481 |
+
executing the next interceptor in chain or invoking the
|
| 482 |
+
actual RPC on the underlying Channel. It is the interceptor's
|
| 483 |
+
responsibility to call it if it decides to move the RPC forward.
|
| 484 |
+
The interceptor can use
|
| 485 |
+
`response_iterator = continuation(client_call_details, request)`
|
| 486 |
+
to continue with the RPC. `continuation` returns an object that is
|
| 487 |
+
both a Call for the RPC and an iterator for response values.
|
| 488 |
+
Drawing response values from the returned Call-iterator may
|
| 489 |
+
raise RpcError indicating termination of the RPC with non-OK
|
| 490 |
+
status.
|
| 491 |
+
client_call_details: A ClientCallDetails object describing the
|
| 492 |
+
outgoing RPC.
|
| 493 |
+
request: The request value for the RPC.
|
| 494 |
+
|
| 495 |
+
Returns:
|
| 496 |
+
An object that is both a Call for the RPC and an iterator of
|
| 497 |
+
response values. Drawing response values from the returned
|
| 498 |
+
Call-iterator may raise RpcError indicating termination of
|
| 499 |
+
the RPC with non-OK status. This object *should* also fulfill the
|
| 500 |
+
Future interface, though it may not.
|
| 501 |
+
"""
|
| 502 |
+
raise NotImplementedError()
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
class StreamUnaryClientInterceptor(abc.ABC):
|
| 506 |
+
"""Affords intercepting stream-unary invocations."""
|
| 507 |
+
|
| 508 |
+
@abc.abstractmethod
|
| 509 |
+
def intercept_stream_unary(
|
| 510 |
+
self, continuation, client_call_details, request_iterator
|
| 511 |
+
):
|
| 512 |
+
"""Intercepts a stream-unary invocation asynchronously.
|
| 513 |
+
|
| 514 |
+
Args:
|
| 515 |
+
continuation: A function that proceeds with the invocation by
|
| 516 |
+
executing the next interceptor in chain or invoking the
|
| 517 |
+
actual RPC on the underlying Channel. It is the interceptor's
|
| 518 |
+
responsibility to call it if it decides to move the RPC forward.
|
| 519 |
+
The interceptor can use
|
| 520 |
+
`response_future = continuation(client_call_details, request_iterator)`
|
| 521 |
+
to continue with the RPC. `continuation` returns an object that is
|
| 522 |
+
both a Call for the RPC and a Future. In the event of RPC completion,
|
| 523 |
+
the return Call-Future's result value will be the response message
|
| 524 |
+
of the RPC. Should the event terminate with non-OK status, the
|
| 525 |
+
returned Call-Future's exception value will be an RpcError.
|
| 526 |
+
client_call_details: A ClientCallDetails object describing the
|
| 527 |
+
outgoing RPC.
|
| 528 |
+
request_iterator: An iterator that yields request values for the RPC.
|
| 529 |
+
|
| 530 |
+
Returns:
|
| 531 |
+
An object that is both a Call for the RPC and a Future.
|
| 532 |
+
In the event of RPC completion, the return Call-Future's
|
| 533 |
+
result value will be the response message of the RPC.
|
| 534 |
+
Should the event terminate with non-OK status, the returned
|
| 535 |
+
Call-Future's exception value will be an RpcError.
|
| 536 |
+
"""
|
| 537 |
+
raise NotImplementedError()
|
| 538 |
+
|
| 539 |
+
|
| 540 |
+
class StreamStreamClientInterceptor(abc.ABC):
|
| 541 |
+
"""Affords intercepting stream-stream invocations."""
|
| 542 |
+
|
| 543 |
+
@abc.abstractmethod
|
| 544 |
+
def intercept_stream_stream(
|
| 545 |
+
self, continuation, client_call_details, request_iterator
|
| 546 |
+
):
|
| 547 |
+
"""Intercepts a stream-stream invocation.
|
| 548 |
+
|
| 549 |
+
Args:
|
| 550 |
+
continuation: A function that proceeds with the invocation by
|
| 551 |
+
executing the next interceptor in chain or invoking the
|
| 552 |
+
actual RPC on the underlying Channel. It is the interceptor's
|
| 553 |
+
responsibility to call it if it decides to move the RPC forward.
|
| 554 |
+
The interceptor can use
|
| 555 |
+
`response_iterator = continuation(client_call_details, request_iterator)`
|
| 556 |
+
to continue with the RPC. `continuation` returns an object that is
|
| 557 |
+
both a Call for the RPC and an iterator for response values.
|
| 558 |
+
Drawing response values from the returned Call-iterator may
|
| 559 |
+
raise RpcError indicating termination of the RPC with non-OK
|
| 560 |
+
status.
|
| 561 |
+
client_call_details: A ClientCallDetails object describing the
|
| 562 |
+
outgoing RPC.
|
| 563 |
+
request_iterator: An iterator that yields request values for the RPC.
|
| 564 |
+
|
| 565 |
+
Returns:
|
| 566 |
+
An object that is both a Call for the RPC and an iterator of
|
| 567 |
+
response values. Drawing response values from the returned
|
| 568 |
+
Call-iterator may raise RpcError indicating termination of
|
| 569 |
+
the RPC with non-OK status. This object *should* also fulfill the
|
| 570 |
+
Future interface, though it may not.
|
| 571 |
+
"""
|
| 572 |
+
raise NotImplementedError()
|
| 573 |
+
|
| 574 |
+
|
| 575 |
+
############ Authentication & Authorization Interfaces & Classes #############
|
| 576 |
+
|
| 577 |
+
|
| 578 |
+
class ChannelCredentials(object):
|
| 579 |
+
"""An encapsulation of the data required to create a secure Channel.
|
| 580 |
+
|
| 581 |
+
This class has no supported interface - it exists to define the type of its
|
| 582 |
+
instances and its instances exist to be passed to other functions. For
|
| 583 |
+
example, ssl_channel_credentials returns an instance of this class and
|
| 584 |
+
secure_channel requires an instance of this class.
|
| 585 |
+
"""
|
| 586 |
+
|
| 587 |
+
def __init__(self, credentials):
|
| 588 |
+
self._credentials = credentials
|
| 589 |
+
|
| 590 |
+
|
| 591 |
+
class CallCredentials(object):
|
| 592 |
+
"""An encapsulation of the data required to assert an identity over a call.
|
| 593 |
+
|
| 594 |
+
A CallCredentials has to be used with secure Channel, otherwise the
|
| 595 |
+
metadata will not be transmitted to the server.
|
| 596 |
+
|
| 597 |
+
A CallCredentials may be composed with ChannelCredentials to always assert
|
| 598 |
+
identity for every call over that Channel.
|
| 599 |
+
|
| 600 |
+
This class has no supported interface - it exists to define the type of its
|
| 601 |
+
instances and its instances exist to be passed to other functions.
|
| 602 |
+
"""
|
| 603 |
+
|
| 604 |
+
def __init__(self, credentials):
|
| 605 |
+
self._credentials = credentials
|
| 606 |
+
|
| 607 |
+
|
| 608 |
+
class AuthMetadataContext(abc.ABC):
|
| 609 |
+
"""Provides information to call credentials metadata plugins.
|
| 610 |
+
|
| 611 |
+
Attributes:
|
| 612 |
+
service_url: A string URL of the service being called into.
|
| 613 |
+
method_name: A string of the fully qualified method name being called.
|
| 614 |
+
"""
|
| 615 |
+
|
| 616 |
+
|
| 617 |
+
class AuthMetadataPluginCallback(abc.ABC):
|
| 618 |
+
"""Callback object received by a metadata plugin."""
|
| 619 |
+
|
| 620 |
+
def __call__(self, metadata, error):
|
| 621 |
+
"""Passes to the gRPC runtime authentication metadata for an RPC.
|
| 622 |
+
|
| 623 |
+
Args:
|
| 624 |
+
metadata: The :term:`metadata` used to construct the CallCredentials.
|
| 625 |
+
error: An Exception to indicate error or None to indicate success.
|
| 626 |
+
"""
|
| 627 |
+
raise NotImplementedError()
|
| 628 |
+
|
| 629 |
+
|
| 630 |
+
class AuthMetadataPlugin(abc.ABC):
|
| 631 |
+
"""A specification for custom authentication."""
|
| 632 |
+
|
| 633 |
+
def __call__(self, context, callback):
|
| 634 |
+
"""Implements authentication by passing metadata to a callback.
|
| 635 |
+
|
| 636 |
+
This method will be invoked asynchronously in a separate thread.
|
| 637 |
+
|
| 638 |
+
Args:
|
| 639 |
+
context: An AuthMetadataContext providing information on the RPC that
|
| 640 |
+
the plugin is being called to authenticate.
|
| 641 |
+
callback: An AuthMetadataPluginCallback to be invoked either
|
| 642 |
+
synchronously or asynchronously.
|
| 643 |
+
"""
|
| 644 |
+
raise NotImplementedError()
|
| 645 |
+
|
| 646 |
+
|
| 647 |
+
class ServerCredentials(object):
|
| 648 |
+
"""An encapsulation of the data required to open a secure port on a Server.
|
| 649 |
+
|
| 650 |
+
This class has no supported interface - it exists to define the type of its
|
| 651 |
+
instances and its instances exist to be passed to other functions.
|
| 652 |
+
"""
|
| 653 |
+
|
| 654 |
+
def __init__(self, credentials):
|
| 655 |
+
self._credentials = credentials
|
| 656 |
+
|
| 657 |
+
|
| 658 |
+
class ServerCertificateConfiguration(object):
|
| 659 |
+
"""A certificate configuration for use with an SSL-enabled Server.
|
| 660 |
+
|
| 661 |
+
Instances of this class can be returned in the certificate configuration
|
| 662 |
+
fetching callback.
|
| 663 |
+
|
| 664 |
+
This class has no supported interface -- it exists to define the
|
| 665 |
+
type of its instances and its instances exist to be passed to
|
| 666 |
+
other functions.
|
| 667 |
+
"""
|
| 668 |
+
|
| 669 |
+
def __init__(self, certificate_configuration):
|
| 670 |
+
self._certificate_configuration = certificate_configuration
|
| 671 |
+
|
| 672 |
+
|
| 673 |
+
######################## Multi-Callable Interfaces ###########################
|
| 674 |
+
|
| 675 |
+
|
| 676 |
+
class UnaryUnaryMultiCallable(abc.ABC):
|
| 677 |
+
"""Affords invoking a unary-unary RPC from client-side."""
|
| 678 |
+
|
| 679 |
+
@abc.abstractmethod
|
| 680 |
+
def __call__(
|
| 681 |
+
self,
|
| 682 |
+
request,
|
| 683 |
+
timeout=None,
|
| 684 |
+
metadata=None,
|
| 685 |
+
credentials=None,
|
| 686 |
+
wait_for_ready=None,
|
| 687 |
+
compression=None,
|
| 688 |
+
):
|
| 689 |
+
"""Synchronously invokes the underlying RPC.
|
| 690 |
+
|
| 691 |
+
Args:
|
| 692 |
+
request: The request value for the RPC.
|
| 693 |
+
timeout: An optional duration of time in seconds to allow
|
| 694 |
+
for the RPC.
|
| 695 |
+
metadata: Optional :term:`metadata` to be transmitted to the
|
| 696 |
+
service-side of the RPC.
|
| 697 |
+
credentials: An optional CallCredentials for the RPC. Only valid for
|
| 698 |
+
secure Channel.
|
| 699 |
+
wait_for_ready: An optional flag to enable :term:`wait_for_ready` mechanism.
|
| 700 |
+
compression: An element of grpc.compression, e.g.
|
| 701 |
+
grpc.compression.Gzip.
|
| 702 |
+
|
| 703 |
+
Returns:
|
| 704 |
+
The response value for the RPC.
|
| 705 |
+
|
| 706 |
+
Raises:
|
| 707 |
+
RpcError: Indicating that the RPC terminated with non-OK status. The
|
| 708 |
+
raised RpcError will also be a Call for the RPC affording the RPC's
|
| 709 |
+
metadata, status code, and details.
|
| 710 |
+
"""
|
| 711 |
+
raise NotImplementedError()
|
| 712 |
+
|
| 713 |
+
@abc.abstractmethod
|
| 714 |
+
def with_call(
|
| 715 |
+
self,
|
| 716 |
+
request,
|
| 717 |
+
timeout=None,
|
| 718 |
+
metadata=None,
|
| 719 |
+
credentials=None,
|
| 720 |
+
wait_for_ready=None,
|
| 721 |
+
compression=None,
|
| 722 |
+
):
|
| 723 |
+
"""Synchronously invokes the underlying RPC.
|
| 724 |
+
|
| 725 |
+
Args:
|
| 726 |
+
request: The request value for the RPC.
|
| 727 |
+
timeout: An optional durating of time in seconds to allow for
|
| 728 |
+
the RPC.
|
| 729 |
+
metadata: Optional :term:`metadata` to be transmitted to the
|
| 730 |
+
service-side of the RPC.
|
| 731 |
+
credentials: An optional CallCredentials for the RPC. Only valid for
|
| 732 |
+
secure Channel.
|
| 733 |
+
wait_for_ready: An optional flag to enable :term:`wait_for_ready` mechanism.
|
| 734 |
+
compression: An element of grpc.compression, e.g.
|
| 735 |
+
grpc.compression.Gzip.
|
| 736 |
+
|
| 737 |
+
Returns:
|
| 738 |
+
The response value for the RPC and a Call value for the RPC.
|
| 739 |
+
|
| 740 |
+
Raises:
|
| 741 |
+
RpcError: Indicating that the RPC terminated with non-OK status. The
|
| 742 |
+
raised RpcError will also be a Call for the RPC affording the RPC's
|
| 743 |
+
metadata, status code, and details.
|
| 744 |
+
"""
|
| 745 |
+
raise NotImplementedError()
|
| 746 |
+
|
| 747 |
+
@abc.abstractmethod
|
| 748 |
+
def future(
|
| 749 |
+
self,
|
| 750 |
+
request,
|
| 751 |
+
timeout=None,
|
| 752 |
+
metadata=None,
|
| 753 |
+
credentials=None,
|
| 754 |
+
wait_for_ready=None,
|
| 755 |
+
compression=None,
|
| 756 |
+
):
|
| 757 |
+
"""Asynchronously invokes the underlying RPC.
|
| 758 |
+
|
| 759 |
+
Args:
|
| 760 |
+
request: The request value for the RPC.
|
| 761 |
+
timeout: An optional duration of time in seconds to allow for
|
| 762 |
+
the RPC.
|
| 763 |
+
metadata: Optional :term:`metadata` to be transmitted to the
|
| 764 |
+
service-side of the RPC.
|
| 765 |
+
credentials: An optional CallCredentials for the RPC. Only valid for
|
| 766 |
+
secure Channel.
|
| 767 |
+
wait_for_ready: An optional flag to enable :term:`wait_for_ready` mechanism.
|
| 768 |
+
compression: An element of grpc.compression, e.g.
|
| 769 |
+
grpc.compression.Gzip.
|
| 770 |
+
|
| 771 |
+
Returns:
|
| 772 |
+
An object that is both a Call for the RPC and a Future.
|
| 773 |
+
In the event of RPC completion, the return Call-Future's result
|
| 774 |
+
value will be the response message of the RPC.
|
| 775 |
+
Should the event terminate with non-OK status,
|
| 776 |
+
the returned Call-Future's exception value will be an RpcError.
|
| 777 |
+
"""
|
| 778 |
+
raise NotImplementedError()
|
| 779 |
+
|
| 780 |
+
|
| 781 |
+
class UnaryStreamMultiCallable(abc.ABC):
|
| 782 |
+
"""Affords invoking a unary-stream RPC from client-side."""
|
| 783 |
+
|
| 784 |
+
@abc.abstractmethod
|
| 785 |
+
def __call__(
|
| 786 |
+
self,
|
| 787 |
+
request,
|
| 788 |
+
timeout=None,
|
| 789 |
+
metadata=None,
|
| 790 |
+
credentials=None,
|
| 791 |
+
wait_for_ready=None,
|
| 792 |
+
compression=None,
|
| 793 |
+
):
|
| 794 |
+
"""Invokes the underlying RPC.
|
| 795 |
+
|
| 796 |
+
Args:
|
| 797 |
+
request: The request value for the RPC.
|
| 798 |
+
timeout: An optional duration of time in seconds to allow for
|
| 799 |
+
the RPC. If None, the timeout is considered infinite.
|
| 800 |
+
metadata: An optional :term:`metadata` to be transmitted to the
|
| 801 |
+
service-side of the RPC.
|
| 802 |
+
credentials: An optional CallCredentials for the RPC. Only valid for
|
| 803 |
+
secure Channel.
|
| 804 |
+
wait_for_ready: An optional flag to enable :term:`wait_for_ready` mechanism.
|
| 805 |
+
compression: An element of grpc.compression, e.g.
|
| 806 |
+
grpc.compression.Gzip.
|
| 807 |
+
|
| 808 |
+
Returns:
|
| 809 |
+
An object that is a Call for the RPC, an iterator of response
|
| 810 |
+
values, and a Future for the RPC. Drawing response values from the
|
| 811 |
+
returned Call-iterator may raise RpcError indicating termination of
|
| 812 |
+
the RPC with non-OK status.
|
| 813 |
+
"""
|
| 814 |
+
raise NotImplementedError()
|
| 815 |
+
|
| 816 |
+
|
| 817 |
+
class StreamUnaryMultiCallable(abc.ABC):
|
| 818 |
+
"""Affords invoking a stream-unary RPC from client-side."""
|
| 819 |
+
|
| 820 |
+
@abc.abstractmethod
|
| 821 |
+
def __call__(
|
| 822 |
+
self,
|
| 823 |
+
request_iterator,
|
| 824 |
+
timeout=None,
|
| 825 |
+
metadata=None,
|
| 826 |
+
credentials=None,
|
| 827 |
+
wait_for_ready=None,
|
| 828 |
+
compression=None,
|
| 829 |
+
):
|
| 830 |
+
"""Synchronously invokes the underlying RPC.
|
| 831 |
+
|
| 832 |
+
Args:
|
| 833 |
+
request_iterator: An iterator that yields request values for
|
| 834 |
+
the RPC.
|
| 835 |
+
timeout: An optional duration of time in seconds to allow for
|
| 836 |
+
the RPC. If None, the timeout is considered infinite.
|
| 837 |
+
metadata: Optional :term:`metadata` to be transmitted to the
|
| 838 |
+
service-side of the RPC.
|
| 839 |
+
credentials: An optional CallCredentials for the RPC. Only valid for
|
| 840 |
+
secure Channel.
|
| 841 |
+
wait_for_ready: An optional flag to enable :term:`wait_for_ready` mechanism.
|
| 842 |
+
compression: An element of grpc.compression, e.g.
|
| 843 |
+
grpc.compression.Gzip.
|
| 844 |
+
|
| 845 |
+
Returns:
|
| 846 |
+
The response value for the RPC.
|
| 847 |
+
|
| 848 |
+
Raises:
|
| 849 |
+
RpcError: Indicating that the RPC terminated with non-OK status. The
|
| 850 |
+
raised RpcError will also implement grpc.Call, affording methods
|
| 851 |
+
such as metadata, code, and details.
|
| 852 |
+
"""
|
| 853 |
+
raise NotImplementedError()
|
| 854 |
+
|
| 855 |
+
@abc.abstractmethod
|
| 856 |
+
def with_call(
|
| 857 |
+
self,
|
| 858 |
+
request_iterator,
|
| 859 |
+
timeout=None,
|
| 860 |
+
metadata=None,
|
| 861 |
+
credentials=None,
|
| 862 |
+
wait_for_ready=None,
|
| 863 |
+
compression=None,
|
| 864 |
+
):
|
| 865 |
+
"""Synchronously invokes the underlying RPC on the client.
|
| 866 |
+
|
| 867 |
+
Args:
|
| 868 |
+
request_iterator: An iterator that yields request values for
|
| 869 |
+
the RPC.
|
| 870 |
+
timeout: An optional duration of time in seconds to allow for
|
| 871 |
+
the RPC. If None, the timeout is considered infinite.
|
| 872 |
+
metadata: Optional :term:`metadata` to be transmitted to the
|
| 873 |
+
service-side of the RPC.
|
| 874 |
+
credentials: An optional CallCredentials for the RPC. Only valid for
|
| 875 |
+
secure Channel.
|
| 876 |
+
wait_for_ready: An optional flag to enable :term:`wait_for_ready` mechanism.
|
| 877 |
+
compression: An element of grpc.compression, e.g.
|
| 878 |
+
grpc.compression.Gzip.
|
| 879 |
+
|
| 880 |
+
Returns:
|
| 881 |
+
The response value for the RPC and a Call object for the RPC.
|
| 882 |
+
|
| 883 |
+
Raises:
|
| 884 |
+
RpcError: Indicating that the RPC terminated with non-OK status. The
|
| 885 |
+
raised RpcError will also be a Call for the RPC affording the RPC's
|
| 886 |
+
metadata, status code, and details.
|
| 887 |
+
"""
|
| 888 |
+
raise NotImplementedError()
|
| 889 |
+
|
| 890 |
+
@abc.abstractmethod
|
| 891 |
+
def future(
|
| 892 |
+
self,
|
| 893 |
+
request_iterator,
|
| 894 |
+
timeout=None,
|
| 895 |
+
metadata=None,
|
| 896 |
+
credentials=None,
|
| 897 |
+
wait_for_ready=None,
|
| 898 |
+
compression=None,
|
| 899 |
+
):
|
| 900 |
+
"""Asynchronously invokes the underlying RPC on the client.
|
| 901 |
+
|
| 902 |
+
Args:
|
| 903 |
+
request_iterator: An iterator that yields request values for the RPC.
|
| 904 |
+
timeout: An optional duration of time in seconds to allow for
|
| 905 |
+
the RPC. If None, the timeout is considered infinite.
|
| 906 |
+
metadata: Optional :term:`metadata` to be transmitted to the
|
| 907 |
+
service-side of the RPC.
|
| 908 |
+
credentials: An optional CallCredentials for the RPC. Only valid for
|
| 909 |
+
secure Channel.
|
| 910 |
+
wait_for_ready: An optional flag to enable :term:`wait_for_ready` mechanism.
|
| 911 |
+
compression: An element of grpc.compression, e.g.
|
| 912 |
+
grpc.compression.Gzip.
|
| 913 |
+
|
| 914 |
+
Returns:
|
| 915 |
+
An object that is both a Call for the RPC and a Future.
|
| 916 |
+
In the event of RPC completion, the return Call-Future's result value
|
| 917 |
+
will be the response message of the RPC. Should the event terminate
|
| 918 |
+
with non-OK status, the returned Call-Future's exception value will
|
| 919 |
+
be an RpcError.
|
| 920 |
+
"""
|
| 921 |
+
raise NotImplementedError()
|
| 922 |
+
|
| 923 |
+
|
| 924 |
+
class StreamStreamMultiCallable(abc.ABC):
|
| 925 |
+
"""Affords invoking a stream-stream RPC on client-side."""
|
| 926 |
+
|
| 927 |
+
@abc.abstractmethod
|
| 928 |
+
def __call__(
|
| 929 |
+
self,
|
| 930 |
+
request_iterator,
|
| 931 |
+
timeout=None,
|
| 932 |
+
metadata=None,
|
| 933 |
+
credentials=None,
|
| 934 |
+
wait_for_ready=None,
|
| 935 |
+
compression=None,
|
| 936 |
+
):
|
| 937 |
+
"""Invokes the underlying RPC on the client.
|
| 938 |
+
|
| 939 |
+
Args:
|
| 940 |
+
request_iterator: An iterator that yields request values for the RPC.
|
| 941 |
+
timeout: An optional duration of time in seconds to allow for
|
| 942 |
+
the RPC. If not specified, the timeout is considered infinite.
|
| 943 |
+
metadata: Optional :term:`metadata` to be transmitted to the
|
| 944 |
+
service-side of the RPC.
|
| 945 |
+
credentials: An optional CallCredentials for the RPC. Only valid for
|
| 946 |
+
secure Channel.
|
| 947 |
+
wait_for_ready: An optional flag to enable :term:`wait_for_ready` mechanism.
|
| 948 |
+
compression: An element of grpc.compression, e.g.
|
| 949 |
+
grpc.compression.Gzip.
|
| 950 |
+
|
| 951 |
+
Returns:
|
| 952 |
+
An object that is a Call for the RPC, an iterator of response
|
| 953 |
+
values, and a Future for the RPC. Drawing response values from the
|
| 954 |
+
returned Call-iterator may raise RpcError indicating termination of
|
| 955 |
+
the RPC with non-OK status.
|
| 956 |
+
"""
|
| 957 |
+
raise NotImplementedError()
|
| 958 |
+
|
| 959 |
+
|
| 960 |
+
############################# Channel Interface ##############################
|
| 961 |
+
|
| 962 |
+
|
| 963 |
+
class Channel(abc.ABC):
|
| 964 |
+
"""Affords RPC invocation via generic methods on client-side.
|
| 965 |
+
|
| 966 |
+
Channel objects implement the Context Manager type, although they need not
|
| 967 |
+
support being entered and exited multiple times.
|
| 968 |
+
"""
|
| 969 |
+
|
| 970 |
+
@abc.abstractmethod
|
| 971 |
+
def subscribe(self, callback, try_to_connect=False):
|
| 972 |
+
"""Subscribe to this Channel's connectivity state machine.
|
| 973 |
+
|
| 974 |
+
A Channel may be in any of the states described by ChannelConnectivity.
|
| 975 |
+
This method allows application to monitor the state transitions.
|
| 976 |
+
The typical use case is to debug or gain better visibility into gRPC
|
| 977 |
+
runtime's state.
|
| 978 |
+
|
| 979 |
+
Args:
|
| 980 |
+
callback: A callable to be invoked with ChannelConnectivity argument.
|
| 981 |
+
ChannelConnectivity describes current state of the channel.
|
| 982 |
+
The callable will be invoked immediately upon subscription
|
| 983 |
+
and again for every change to ChannelConnectivity until it
|
| 984 |
+
is unsubscribed or this Channel object goes out of scope.
|
| 985 |
+
try_to_connect: A boolean indicating whether or not this Channel
|
| 986 |
+
should attempt to connect immediately. If set to False, gRPC
|
| 987 |
+
runtime decides when to connect.
|
| 988 |
+
"""
|
| 989 |
+
raise NotImplementedError()
|
| 990 |
+
|
| 991 |
+
@abc.abstractmethod
|
| 992 |
+
def unsubscribe(self, callback):
|
| 993 |
+
"""Unsubscribes a subscribed callback from this Channel's connectivity.
|
| 994 |
+
|
| 995 |
+
Args:
|
| 996 |
+
callback: A callable previously registered with this Channel from
|
| 997 |
+
having been passed to its "subscribe" method.
|
| 998 |
+
"""
|
| 999 |
+
raise NotImplementedError()
|
| 1000 |
+
|
| 1001 |
+
@abc.abstractmethod
|
| 1002 |
+
def unary_unary(
|
| 1003 |
+
self,
|
| 1004 |
+
method,
|
| 1005 |
+
request_serializer=None,
|
| 1006 |
+
response_deserializer=None,
|
| 1007 |
+
_registered_method=False,
|
| 1008 |
+
):
|
| 1009 |
+
"""Creates a UnaryUnaryMultiCallable for a unary-unary method.
|
| 1010 |
+
|
| 1011 |
+
Args:
|
| 1012 |
+
method: The name of the RPC method.
|
| 1013 |
+
request_serializer: Optional :term:`serializer` for serializing the request
|
| 1014 |
+
message. Request goes unserialized in case None is passed.
|
| 1015 |
+
response_deserializer: Optional :term:`deserializer` for deserializing the
|
| 1016 |
+
response message. Response goes undeserialized in case None
|
| 1017 |
+
is passed.
|
| 1018 |
+
_registered_method: Implementation Private. A bool representing whether the method
|
| 1019 |
+
is registered.
|
| 1020 |
+
|
| 1021 |
+
Returns:
|
| 1022 |
+
A UnaryUnaryMultiCallable value for the named unary-unary method.
|
| 1023 |
+
"""
|
| 1024 |
+
raise NotImplementedError()
|
| 1025 |
+
|
| 1026 |
+
@abc.abstractmethod
|
| 1027 |
+
def unary_stream(
|
| 1028 |
+
self,
|
| 1029 |
+
method,
|
| 1030 |
+
request_serializer=None,
|
| 1031 |
+
response_deserializer=None,
|
| 1032 |
+
_registered_method=False,
|
| 1033 |
+
):
|
| 1034 |
+
"""Creates a UnaryStreamMultiCallable for a unary-stream method.
|
| 1035 |
+
|
| 1036 |
+
Args:
|
| 1037 |
+
method: The name of the RPC method.
|
| 1038 |
+
request_serializer: Optional :term:`serializer` for serializing the request
|
| 1039 |
+
message. Request goes unserialized in case None is passed.
|
| 1040 |
+
response_deserializer: Optional :term:`deserializer` for deserializing the
|
| 1041 |
+
response message. Response goes undeserialized in case None is
|
| 1042 |
+
passed.
|
| 1043 |
+
_registered_method: Implementation Private. A bool representing whether the method
|
| 1044 |
+
is registered.
|
| 1045 |
+
|
| 1046 |
+
Returns:
|
| 1047 |
+
A UnaryStreamMultiCallable value for the name unary-stream method.
|
| 1048 |
+
"""
|
| 1049 |
+
raise NotImplementedError()
|
| 1050 |
+
|
| 1051 |
+
@abc.abstractmethod
|
| 1052 |
+
def stream_unary(
|
| 1053 |
+
self,
|
| 1054 |
+
method,
|
| 1055 |
+
request_serializer=None,
|
| 1056 |
+
response_deserializer=None,
|
| 1057 |
+
_registered_method=False,
|
| 1058 |
+
):
|
| 1059 |
+
"""Creates a StreamUnaryMultiCallable for a stream-unary method.
|
| 1060 |
+
|
| 1061 |
+
Args:
|
| 1062 |
+
method: The name of the RPC method.
|
| 1063 |
+
request_serializer: Optional :term:`serializer` for serializing the request
|
| 1064 |
+
message. Request goes unserialized in case None is passed.
|
| 1065 |
+
response_deserializer: Optional :term:`deserializer` for deserializing the
|
| 1066 |
+
response message. Response goes undeserialized in case None is
|
| 1067 |
+
passed.
|
| 1068 |
+
_registered_method: Implementation Private. A bool representing whether the method
|
| 1069 |
+
is registered.
|
| 1070 |
+
|
| 1071 |
+
Returns:
|
| 1072 |
+
A StreamUnaryMultiCallable value for the named stream-unary method.
|
| 1073 |
+
"""
|
| 1074 |
+
raise NotImplementedError()
|
| 1075 |
+
|
| 1076 |
+
@abc.abstractmethod
|
| 1077 |
+
def stream_stream(
|
| 1078 |
+
self,
|
| 1079 |
+
method,
|
| 1080 |
+
request_serializer=None,
|
| 1081 |
+
response_deserializer=None,
|
| 1082 |
+
_registered_method=False,
|
| 1083 |
+
):
|
| 1084 |
+
"""Creates a StreamStreamMultiCallable for a stream-stream method.
|
| 1085 |
+
|
| 1086 |
+
Args:
|
| 1087 |
+
method: The name of the RPC method.
|
| 1088 |
+
request_serializer: Optional :term:`serializer` for serializing the request
|
| 1089 |
+
message. Request goes unserialized in case None is passed.
|
| 1090 |
+
response_deserializer: Optional :term:`deserializer` for deserializing the
|
| 1091 |
+
response message. Response goes undeserialized in case None
|
| 1092 |
+
is passed.
|
| 1093 |
+
_registered_method: Implementation Private. A bool representing whether the method
|
| 1094 |
+
is registered.
|
| 1095 |
+
|
| 1096 |
+
Returns:
|
| 1097 |
+
A StreamStreamMultiCallable value for the named stream-stream method.
|
| 1098 |
+
"""
|
| 1099 |
+
raise NotImplementedError()
|
| 1100 |
+
|
| 1101 |
+
@abc.abstractmethod
|
| 1102 |
+
def close(self):
|
| 1103 |
+
"""Closes this Channel and releases all resources held by it.
|
| 1104 |
+
|
| 1105 |
+
Closing the Channel will immediately terminate all RPCs active with the
|
| 1106 |
+
Channel and it is not valid to invoke new RPCs with the Channel.
|
| 1107 |
+
|
| 1108 |
+
This method is idempotent.
|
| 1109 |
+
"""
|
| 1110 |
+
raise NotImplementedError()
|
| 1111 |
+
|
| 1112 |
+
def __enter__(self):
|
| 1113 |
+
"""Enters the runtime context related to the channel object."""
|
| 1114 |
+
raise NotImplementedError()
|
| 1115 |
+
|
| 1116 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 1117 |
+
"""Exits the runtime context related to the channel object."""
|
| 1118 |
+
raise NotImplementedError()
|
| 1119 |
+
|
| 1120 |
+
|
| 1121 |
+
########################## Service-Side Context ##############################
|
| 1122 |
+
|
| 1123 |
+
|
| 1124 |
+
class ServicerContext(RpcContext, metaclass=abc.ABCMeta):
|
| 1125 |
+
"""A context object passed to method implementations."""
|
| 1126 |
+
|
| 1127 |
+
@abc.abstractmethod
|
| 1128 |
+
def invocation_metadata(self):
|
| 1129 |
+
"""Accesses the metadata sent by the client.
|
| 1130 |
+
|
| 1131 |
+
Returns:
|
| 1132 |
+
The invocation :term:`metadata`.
|
| 1133 |
+
"""
|
| 1134 |
+
raise NotImplementedError()
|
| 1135 |
+
|
| 1136 |
+
@abc.abstractmethod
|
| 1137 |
+
def peer(self):
|
| 1138 |
+
"""Identifies the peer that invoked the RPC being serviced.
|
| 1139 |
+
|
| 1140 |
+
Returns:
|
| 1141 |
+
A string identifying the peer that invoked the RPC being serviced.
|
| 1142 |
+
The string format is determined by gRPC runtime.
|
| 1143 |
+
"""
|
| 1144 |
+
raise NotImplementedError()
|
| 1145 |
+
|
| 1146 |
+
@abc.abstractmethod
|
| 1147 |
+
def peer_identities(self):
|
| 1148 |
+
"""Gets one or more peer identity(s).
|
| 1149 |
+
|
| 1150 |
+
Equivalent to
|
| 1151 |
+
servicer_context.auth_context().get(servicer_context.peer_identity_key())
|
| 1152 |
+
|
| 1153 |
+
Returns:
|
| 1154 |
+
An iterable of the identities, or None if the call is not
|
| 1155 |
+
authenticated. Each identity is returned as a raw bytes type.
|
| 1156 |
+
"""
|
| 1157 |
+
raise NotImplementedError()
|
| 1158 |
+
|
| 1159 |
+
@abc.abstractmethod
|
| 1160 |
+
def peer_identity_key(self):
|
| 1161 |
+
"""The auth property used to identify the peer.
|
| 1162 |
+
|
| 1163 |
+
For example, "x509_common_name" or "x509_subject_alternative_name" are
|
| 1164 |
+
used to identify an SSL peer.
|
| 1165 |
+
|
| 1166 |
+
Returns:
|
| 1167 |
+
The auth property (string) that indicates the
|
| 1168 |
+
peer identity, or None if the call is not authenticated.
|
| 1169 |
+
"""
|
| 1170 |
+
raise NotImplementedError()
|
| 1171 |
+
|
| 1172 |
+
@abc.abstractmethod
|
| 1173 |
+
def auth_context(self):
|
| 1174 |
+
"""Gets the auth context for the call.
|
| 1175 |
+
|
| 1176 |
+
Returns:
|
| 1177 |
+
A map of strings to an iterable of bytes for each auth property.
|
| 1178 |
+
"""
|
| 1179 |
+
raise NotImplementedError()
|
| 1180 |
+
|
| 1181 |
+
def set_compression(self, compression):
|
| 1182 |
+
"""Set the compression algorithm to be used for the entire call.
|
| 1183 |
+
|
| 1184 |
+
Args:
|
| 1185 |
+
compression: An element of grpc.compression, e.g.
|
| 1186 |
+
grpc.compression.Gzip.
|
| 1187 |
+
"""
|
| 1188 |
+
raise NotImplementedError()
|
| 1189 |
+
|
| 1190 |
+
@abc.abstractmethod
|
| 1191 |
+
def send_initial_metadata(self, initial_metadata):
|
| 1192 |
+
"""Sends the initial metadata value to the client.
|
| 1193 |
+
|
| 1194 |
+
This method need not be called by implementations if they have no
|
| 1195 |
+
metadata to add to what the gRPC runtime will transmit.
|
| 1196 |
+
|
| 1197 |
+
Args:
|
| 1198 |
+
initial_metadata: The initial :term:`metadata`.
|
| 1199 |
+
"""
|
| 1200 |
+
raise NotImplementedError()
|
| 1201 |
+
|
| 1202 |
+
@abc.abstractmethod
|
| 1203 |
+
def set_trailing_metadata(self, trailing_metadata):
|
| 1204 |
+
"""Sets the trailing metadata for the RPC.
|
| 1205 |
+
|
| 1206 |
+
Sets the trailing metadata to be sent upon completion of the RPC.
|
| 1207 |
+
|
| 1208 |
+
If this method is invoked multiple times throughout the lifetime of an
|
| 1209 |
+
RPC, the value supplied in the final invocation will be the value sent
|
| 1210 |
+
over the wire.
|
| 1211 |
+
|
| 1212 |
+
This method need not be called by implementations if they have no
|
| 1213 |
+
metadata to add to what the gRPC runtime will transmit.
|
| 1214 |
+
|
| 1215 |
+
Args:
|
| 1216 |
+
trailing_metadata: The trailing :term:`metadata`.
|
| 1217 |
+
"""
|
| 1218 |
+
raise NotImplementedError()
|
| 1219 |
+
|
| 1220 |
+
def trailing_metadata(self):
|
| 1221 |
+
"""Access value to be used as trailing metadata upon RPC completion.
|
| 1222 |
+
|
| 1223 |
+
This is an EXPERIMENTAL API.
|
| 1224 |
+
|
| 1225 |
+
Returns:
|
| 1226 |
+
The trailing :term:`metadata` for the RPC.
|
| 1227 |
+
"""
|
| 1228 |
+
raise NotImplementedError()
|
| 1229 |
+
|
| 1230 |
+
@abc.abstractmethod
|
| 1231 |
+
def abort(self, code, details):
|
| 1232 |
+
"""Raises an exception to terminate the RPC with a non-OK status.
|
| 1233 |
+
|
| 1234 |
+
The code and details passed as arguments will supersede any existing
|
| 1235 |
+
ones.
|
| 1236 |
+
|
| 1237 |
+
Args:
|
| 1238 |
+
code: A StatusCode object to be sent to the client.
|
| 1239 |
+
It must not be StatusCode.OK.
|
| 1240 |
+
details: A UTF-8-encodable string to be sent to the client upon
|
| 1241 |
+
termination of the RPC.
|
| 1242 |
+
|
| 1243 |
+
Raises:
|
| 1244 |
+
Exception: An exception is always raised to signal the abortion the
|
| 1245 |
+
RPC to the gRPC runtime.
|
| 1246 |
+
"""
|
| 1247 |
+
raise NotImplementedError()
|
| 1248 |
+
|
| 1249 |
+
@abc.abstractmethod
|
| 1250 |
+
def abort_with_status(self, status):
|
| 1251 |
+
"""Raises an exception to terminate the RPC with a non-OK status.
|
| 1252 |
+
|
| 1253 |
+
The status passed as argument will supersede any existing status code,
|
| 1254 |
+
status message and trailing metadata.
|
| 1255 |
+
|
| 1256 |
+
This is an EXPERIMENTAL API.
|
| 1257 |
+
|
| 1258 |
+
Args:
|
| 1259 |
+
status: A grpc.Status object. The status code in it must not be
|
| 1260 |
+
StatusCode.OK.
|
| 1261 |
+
|
| 1262 |
+
Raises:
|
| 1263 |
+
Exception: An exception is always raised to signal the abortion the
|
| 1264 |
+
RPC to the gRPC runtime.
|
| 1265 |
+
"""
|
| 1266 |
+
raise NotImplementedError()
|
| 1267 |
+
|
| 1268 |
+
@abc.abstractmethod
|
| 1269 |
+
def set_code(self, code):
|
| 1270 |
+
"""Sets the value to be used as status code upon RPC completion.
|
| 1271 |
+
|
| 1272 |
+
This method need not be called by method implementations if they wish
|
| 1273 |
+
the gRPC runtime to determine the status code of the RPC.
|
| 1274 |
+
|
| 1275 |
+
Args:
|
| 1276 |
+
code: A StatusCode object to be sent to the client.
|
| 1277 |
+
"""
|
| 1278 |
+
raise NotImplementedError()
|
| 1279 |
+
|
| 1280 |
+
@abc.abstractmethod
|
| 1281 |
+
def set_details(self, details):
|
| 1282 |
+
"""Sets the value to be used as detail string upon RPC completion.
|
| 1283 |
+
|
| 1284 |
+
This method need not be called by method implementations if they have
|
| 1285 |
+
no details to transmit.
|
| 1286 |
+
|
| 1287 |
+
Args:
|
| 1288 |
+
details: A UTF-8-encodable string to be sent to the client upon
|
| 1289 |
+
termination of the RPC.
|
| 1290 |
+
"""
|
| 1291 |
+
raise NotImplementedError()
|
| 1292 |
+
|
| 1293 |
+
def code(self):
|
| 1294 |
+
"""Accesses the value to be used as status code upon RPC completion.
|
| 1295 |
+
|
| 1296 |
+
This is an EXPERIMENTAL API.
|
| 1297 |
+
|
| 1298 |
+
Returns:
|
| 1299 |
+
The StatusCode value for the RPC.
|
| 1300 |
+
"""
|
| 1301 |
+
raise NotImplementedError()
|
| 1302 |
+
|
| 1303 |
+
def details(self):
|
| 1304 |
+
"""Accesses the value to be used as detail string upon RPC completion.
|
| 1305 |
+
|
| 1306 |
+
This is an EXPERIMENTAL API.
|
| 1307 |
+
|
| 1308 |
+
Returns:
|
| 1309 |
+
The details string of the RPC.
|
| 1310 |
+
"""
|
| 1311 |
+
raise NotImplementedError()
|
| 1312 |
+
|
| 1313 |
+
def disable_next_message_compression(self):
|
| 1314 |
+
"""Disables compression for the next response message.
|
| 1315 |
+
|
| 1316 |
+
This method will override any compression configuration set during
|
| 1317 |
+
server creation or set on the call.
|
| 1318 |
+
"""
|
| 1319 |
+
raise NotImplementedError()
|
| 1320 |
+
|
| 1321 |
+
|
| 1322 |
+
##################### Service-Side Handler Interfaces ########################
|
| 1323 |
+
|
| 1324 |
+
|
| 1325 |
+
class RpcMethodHandler(abc.ABC):
|
| 1326 |
+
"""An implementation of a single RPC method.
|
| 1327 |
+
|
| 1328 |
+
Attributes:
|
| 1329 |
+
request_streaming: Whether the RPC supports exactly one request message
|
| 1330 |
+
or any arbitrary number of request messages.
|
| 1331 |
+
response_streaming: Whether the RPC supports exactly one response message
|
| 1332 |
+
or any arbitrary number of response messages.
|
| 1333 |
+
request_deserializer: A callable :term:`deserializer` that accepts a byte string and
|
| 1334 |
+
returns an object suitable to be passed to this object's business
|
| 1335 |
+
logic, or None to indicate that this object's business logic should be
|
| 1336 |
+
passed the raw request bytes.
|
| 1337 |
+
response_serializer: A callable :term:`serializer` that accepts an object produced
|
| 1338 |
+
by this object's business logic and returns a byte string, or None to
|
| 1339 |
+
indicate that the byte strings produced by this object's business logic
|
| 1340 |
+
should be transmitted on the wire as they are.
|
| 1341 |
+
unary_unary: This object's application-specific business logic as a
|
| 1342 |
+
callable value that takes a request value and a ServicerContext object
|
| 1343 |
+
and returns a response value. Only non-None if both request_streaming
|
| 1344 |
+
and response_streaming are False.
|
| 1345 |
+
unary_stream: This object's application-specific business logic as a
|
| 1346 |
+
callable value that takes a request value and a ServicerContext object
|
| 1347 |
+
and returns an iterator of response values. Only non-None if
|
| 1348 |
+
request_streaming is False and response_streaming is True.
|
| 1349 |
+
stream_unary: This object's application-specific business logic as a
|
| 1350 |
+
callable value that takes an iterator of request values and a
|
| 1351 |
+
ServicerContext object and returns a response value. Only non-None if
|
| 1352 |
+
request_streaming is True and response_streaming is False.
|
| 1353 |
+
stream_stream: This object's application-specific business logic as a
|
| 1354 |
+
callable value that takes an iterator of request values and a
|
| 1355 |
+
ServicerContext object and returns an iterator of response values.
|
| 1356 |
+
Only non-None if request_streaming and response_streaming are both
|
| 1357 |
+
True.
|
| 1358 |
+
"""
|
| 1359 |
+
|
| 1360 |
+
|
| 1361 |
+
class HandlerCallDetails(abc.ABC):
|
| 1362 |
+
"""Describes an RPC that has just arrived for service.
|
| 1363 |
+
|
| 1364 |
+
Attributes:
|
| 1365 |
+
method: The method name of the RPC.
|
| 1366 |
+
invocation_metadata: The :term:`metadata` sent by the client.
|
| 1367 |
+
"""
|
| 1368 |
+
|
| 1369 |
+
|
| 1370 |
+
class GenericRpcHandler(abc.ABC):
|
| 1371 |
+
"""An implementation of arbitrarily many RPC methods."""
|
| 1372 |
+
|
| 1373 |
+
@abc.abstractmethod
|
| 1374 |
+
def service(self, handler_call_details):
|
| 1375 |
+
"""Returns the handler for servicing the RPC.
|
| 1376 |
+
|
| 1377 |
+
Args:
|
| 1378 |
+
handler_call_details: A HandlerCallDetails describing the RPC.
|
| 1379 |
+
|
| 1380 |
+
Returns:
|
| 1381 |
+
An RpcMethodHandler with which the RPC may be serviced if the
|
| 1382 |
+
implementation chooses to service this RPC, or None otherwise.
|
| 1383 |
+
"""
|
| 1384 |
+
raise NotImplementedError()
|
| 1385 |
+
|
| 1386 |
+
|
| 1387 |
+
class ServiceRpcHandler(GenericRpcHandler, metaclass=abc.ABCMeta):
|
| 1388 |
+
"""An implementation of RPC methods belonging to a service.
|
| 1389 |
+
|
| 1390 |
+
A service handles RPC methods with structured names of the form
|
| 1391 |
+
'/Service.Name/Service.Method', where 'Service.Name' is the value
|
| 1392 |
+
returned by service_name(), and 'Service.Method' is the method
|
| 1393 |
+
name. A service can have multiple method names, but only a single
|
| 1394 |
+
service name.
|
| 1395 |
+
"""
|
| 1396 |
+
|
| 1397 |
+
@abc.abstractmethod
|
| 1398 |
+
def service_name(self):
|
| 1399 |
+
"""Returns this service's name.
|
| 1400 |
+
|
| 1401 |
+
Returns:
|
| 1402 |
+
The service name.
|
| 1403 |
+
"""
|
| 1404 |
+
raise NotImplementedError()
|
| 1405 |
+
|
| 1406 |
+
|
| 1407 |
+
#################### Service-Side Interceptor Interfaces #####################
|
| 1408 |
+
|
| 1409 |
+
|
| 1410 |
+
class ServerInterceptor(abc.ABC):
|
| 1411 |
+
"""Affords intercepting incoming RPCs on the service-side."""
|
| 1412 |
+
|
| 1413 |
+
@abc.abstractmethod
|
| 1414 |
+
def intercept_service(self, continuation, handler_call_details):
|
| 1415 |
+
"""Intercepts incoming RPCs before handing them over to a handler.
|
| 1416 |
+
|
| 1417 |
+
State can be passed from an interceptor to downstream interceptors
|
| 1418 |
+
via contextvars. The first interceptor is called from an empty
|
| 1419 |
+
contextvars.Context, and the same Context is used for downstream
|
| 1420 |
+
interceptors and for the final handler call. Note that there are no
|
| 1421 |
+
guarantees that interceptors and handlers will be called from the
|
| 1422 |
+
same thread.
|
| 1423 |
+
|
| 1424 |
+
Args:
|
| 1425 |
+
continuation: A function that takes a HandlerCallDetails and
|
| 1426 |
+
proceeds to invoke the next interceptor in the chain, if any,
|
| 1427 |
+
or the RPC handler lookup logic, with the call details passed
|
| 1428 |
+
as an argument, and returns an RpcMethodHandler instance if
|
| 1429 |
+
the RPC is considered serviced, or None otherwise.
|
| 1430 |
+
handler_call_details: A HandlerCallDetails describing the RPC.
|
| 1431 |
+
|
| 1432 |
+
Returns:
|
| 1433 |
+
An RpcMethodHandler with which the RPC may be serviced if the
|
| 1434 |
+
interceptor chooses to service this RPC, or None otherwise.
|
| 1435 |
+
"""
|
| 1436 |
+
raise NotImplementedError()
|
| 1437 |
+
|
| 1438 |
+
|
| 1439 |
+
############################# Server Interface ###############################
|
| 1440 |
+
|
| 1441 |
+
|
| 1442 |
+
class Server(abc.ABC):
|
| 1443 |
+
"""Services RPCs."""
|
| 1444 |
+
|
| 1445 |
+
@abc.abstractmethod
|
| 1446 |
+
def add_generic_rpc_handlers(self, generic_rpc_handlers):
|
| 1447 |
+
"""Registers GenericRpcHandlers with this Server.
|
| 1448 |
+
|
| 1449 |
+
This method is only safe to call before the server is started.
|
| 1450 |
+
|
| 1451 |
+
Args:
|
| 1452 |
+
generic_rpc_handlers: An iterable of GenericRpcHandlers that will be
|
| 1453 |
+
used to service RPCs.
|
| 1454 |
+
"""
|
| 1455 |
+
raise NotImplementedError()
|
| 1456 |
+
|
| 1457 |
+
def add_registered_method_handlers(self, service_name, method_handlers):
|
| 1458 |
+
"""Registers GenericRpcHandlers with this Server.
|
| 1459 |
+
|
| 1460 |
+
This method is only safe to call before the server is started.
|
| 1461 |
+
|
| 1462 |
+
If the same method have both generic and registered handler,
|
| 1463 |
+
registered handler will take precedence.
|
| 1464 |
+
|
| 1465 |
+
Args:
|
| 1466 |
+
service_name: The service name.
|
| 1467 |
+
method_handlers: A dictionary that maps method names to corresponding
|
| 1468 |
+
RpcMethodHandler.
|
| 1469 |
+
"""
|
| 1470 |
+
|
| 1471 |
+
@abc.abstractmethod
|
| 1472 |
+
def add_insecure_port(self, address):
|
| 1473 |
+
"""Opens an insecure port for accepting RPCs.
|
| 1474 |
+
|
| 1475 |
+
This method may only be called before starting the server.
|
| 1476 |
+
|
| 1477 |
+
Args:
|
| 1478 |
+
address: The address for which to open a port. If the port is 0,
|
| 1479 |
+
or not specified in the address, then gRPC runtime will choose a port.
|
| 1480 |
+
|
| 1481 |
+
Returns:
|
| 1482 |
+
An integer port on which server will accept RPC requests.
|
| 1483 |
+
"""
|
| 1484 |
+
raise NotImplementedError()
|
| 1485 |
+
|
| 1486 |
+
@abc.abstractmethod
|
| 1487 |
+
def add_secure_port(self, address, server_credentials):
|
| 1488 |
+
"""Opens a secure port for accepting RPCs.
|
| 1489 |
+
|
| 1490 |
+
This method may only be called before starting the server.
|
| 1491 |
+
|
| 1492 |
+
Args:
|
| 1493 |
+
address: The address for which to open a port.
|
| 1494 |
+
if the port is 0, or not specified in the address, then gRPC
|
| 1495 |
+
runtime will choose a port.
|
| 1496 |
+
server_credentials: A ServerCredentials object.
|
| 1497 |
+
|
| 1498 |
+
Returns:
|
| 1499 |
+
An integer port on which server will accept RPC requests.
|
| 1500 |
+
"""
|
| 1501 |
+
raise NotImplementedError()
|
| 1502 |
+
|
| 1503 |
+
@abc.abstractmethod
|
| 1504 |
+
def start(self):
|
| 1505 |
+
"""Starts this Server.
|
| 1506 |
+
|
| 1507 |
+
This method may only be called once. (i.e. it is not idempotent).
|
| 1508 |
+
"""
|
| 1509 |
+
raise NotImplementedError()
|
| 1510 |
+
|
| 1511 |
+
@abc.abstractmethod
|
| 1512 |
+
def stop(self, grace):
|
| 1513 |
+
"""Stops this Server.
|
| 1514 |
+
|
| 1515 |
+
This method immediately stop service of new RPCs in all cases.
|
| 1516 |
+
|
| 1517 |
+
If a grace period is specified, this method waits until all active
|
| 1518 |
+
RPCs are finished or until the grace period is reached. RPCs that haven't
|
| 1519 |
+
been terminated within the grace period are aborted.
|
| 1520 |
+
If a grace period is not specified (by passing None for `grace`),
|
| 1521 |
+
all existing RPCs are aborted immediately and this method
|
| 1522 |
+
blocks until the last RPC handler terminates.
|
| 1523 |
+
|
| 1524 |
+
This method is idempotent and may be called at any time.
|
| 1525 |
+
Passing a smaller grace value in a subsequent call will have
|
| 1526 |
+
the effect of stopping the Server sooner (passing None will
|
| 1527 |
+
have the effect of stopping the server immediately). Passing
|
| 1528 |
+
a larger grace value in a subsequent call *will not* have the
|
| 1529 |
+
effect of stopping the server later (i.e. the most restrictive
|
| 1530 |
+
grace value is used).
|
| 1531 |
+
|
| 1532 |
+
Args:
|
| 1533 |
+
grace: A duration of time in seconds or None.
|
| 1534 |
+
|
| 1535 |
+
Returns:
|
| 1536 |
+
A threading.Event that will be set when this Server has completely
|
| 1537 |
+
stopped, i.e. when running RPCs either complete or are aborted and
|
| 1538 |
+
all handlers have terminated.
|
| 1539 |
+
"""
|
| 1540 |
+
raise NotImplementedError()
|
| 1541 |
+
|
| 1542 |
+
def wait_for_termination(self, timeout=None):
|
| 1543 |
+
"""Block current thread until the server stops.
|
| 1544 |
+
|
| 1545 |
+
This is an EXPERIMENTAL API.
|
| 1546 |
+
|
| 1547 |
+
The wait will not consume computational resources during blocking, and
|
| 1548 |
+
it will block until one of the two following conditions are met:
|
| 1549 |
+
|
| 1550 |
+
1) The server is stopped or terminated;
|
| 1551 |
+
2) A timeout occurs if timeout is not `None`.
|
| 1552 |
+
|
| 1553 |
+
The timeout argument works in the same way as `threading.Event.wait()`.
|
| 1554 |
+
https://docs.python.org/3/library/threading.html#threading.Event.wait
|
| 1555 |
+
|
| 1556 |
+
Args:
|
| 1557 |
+
timeout: A floating point number specifying a timeout for the
|
| 1558 |
+
operation in seconds.
|
| 1559 |
+
|
| 1560 |
+
Returns:
|
| 1561 |
+
A bool indicates if the operation times out.
|
| 1562 |
+
"""
|
| 1563 |
+
raise NotImplementedError()
|
| 1564 |
+
|
| 1565 |
+
|
| 1566 |
+
################################# Functions ################################
|
| 1567 |
+
|
| 1568 |
+
|
| 1569 |
+
def unary_unary_rpc_method_handler(
|
| 1570 |
+
behavior, request_deserializer=None, response_serializer=None
|
| 1571 |
+
):
|
| 1572 |
+
"""Creates an RpcMethodHandler for a unary-unary RPC method.
|
| 1573 |
+
|
| 1574 |
+
Args:
|
| 1575 |
+
behavior: The implementation of an RPC that accepts one request
|
| 1576 |
+
and returns one response.
|
| 1577 |
+
request_deserializer: An optional :term:`deserializer` for request deserialization.
|
| 1578 |
+
response_serializer: An optional :term:`serializer` for response serialization.
|
| 1579 |
+
|
| 1580 |
+
Returns:
|
| 1581 |
+
An RpcMethodHandler object that is typically used by grpc.Server.
|
| 1582 |
+
"""
|
| 1583 |
+
from grpc import _utilities # pylint: disable=cyclic-import
|
| 1584 |
+
|
| 1585 |
+
return _utilities.RpcMethodHandler(
|
| 1586 |
+
False,
|
| 1587 |
+
False,
|
| 1588 |
+
request_deserializer,
|
| 1589 |
+
response_serializer,
|
| 1590 |
+
behavior,
|
| 1591 |
+
None,
|
| 1592 |
+
None,
|
| 1593 |
+
None,
|
| 1594 |
+
)
|
| 1595 |
+
|
| 1596 |
+
|
| 1597 |
+
def unary_stream_rpc_method_handler(
|
| 1598 |
+
behavior, request_deserializer=None, response_serializer=None
|
| 1599 |
+
):
|
| 1600 |
+
"""Creates an RpcMethodHandler for a unary-stream RPC method.
|
| 1601 |
+
|
| 1602 |
+
Args:
|
| 1603 |
+
behavior: The implementation of an RPC that accepts one request
|
| 1604 |
+
and returns an iterator of response values.
|
| 1605 |
+
request_deserializer: An optional :term:`deserializer` for request deserialization.
|
| 1606 |
+
response_serializer: An optional :term:`serializer` for response serialization.
|
| 1607 |
+
|
| 1608 |
+
Returns:
|
| 1609 |
+
An RpcMethodHandler object that is typically used by grpc.Server.
|
| 1610 |
+
"""
|
| 1611 |
+
from grpc import _utilities # pylint: disable=cyclic-import
|
| 1612 |
+
|
| 1613 |
+
return _utilities.RpcMethodHandler(
|
| 1614 |
+
False,
|
| 1615 |
+
True,
|
| 1616 |
+
request_deserializer,
|
| 1617 |
+
response_serializer,
|
| 1618 |
+
None,
|
| 1619 |
+
behavior,
|
| 1620 |
+
None,
|
| 1621 |
+
None,
|
| 1622 |
+
)
|
| 1623 |
+
|
| 1624 |
+
|
| 1625 |
+
def stream_unary_rpc_method_handler(
|
| 1626 |
+
behavior, request_deserializer=None, response_serializer=None
|
| 1627 |
+
):
|
| 1628 |
+
"""Creates an RpcMethodHandler for a stream-unary RPC method.
|
| 1629 |
+
|
| 1630 |
+
Args:
|
| 1631 |
+
behavior: The implementation of an RPC that accepts an iterator of
|
| 1632 |
+
request values and returns a single response value.
|
| 1633 |
+
request_deserializer: An optional :term:`deserializer` for request deserialization.
|
| 1634 |
+
response_serializer: An optional :term:`serializer` for response serialization.
|
| 1635 |
+
|
| 1636 |
+
Returns:
|
| 1637 |
+
An RpcMethodHandler object that is typically used by grpc.Server.
|
| 1638 |
+
"""
|
| 1639 |
+
from grpc import _utilities # pylint: disable=cyclic-import
|
| 1640 |
+
|
| 1641 |
+
return _utilities.RpcMethodHandler(
|
| 1642 |
+
True,
|
| 1643 |
+
False,
|
| 1644 |
+
request_deserializer,
|
| 1645 |
+
response_serializer,
|
| 1646 |
+
None,
|
| 1647 |
+
None,
|
| 1648 |
+
behavior,
|
| 1649 |
+
None,
|
| 1650 |
+
)
|
| 1651 |
+
|
| 1652 |
+
|
| 1653 |
+
def stream_stream_rpc_method_handler(
|
| 1654 |
+
behavior, request_deserializer=None, response_serializer=None
|
| 1655 |
+
):
|
| 1656 |
+
"""Creates an RpcMethodHandler for a stream-stream RPC method.
|
| 1657 |
+
|
| 1658 |
+
Args:
|
| 1659 |
+
behavior: The implementation of an RPC that accepts an iterator of
|
| 1660 |
+
request values and returns an iterator of response values.
|
| 1661 |
+
request_deserializer: An optional :term:`deserializer` for request deserialization.
|
| 1662 |
+
response_serializer: An optional :term:`serializer` for response serialization.
|
| 1663 |
+
|
| 1664 |
+
Returns:
|
| 1665 |
+
An RpcMethodHandler object that is typically used by grpc.Server.
|
| 1666 |
+
"""
|
| 1667 |
+
from grpc import _utilities # pylint: disable=cyclic-import
|
| 1668 |
+
|
| 1669 |
+
return _utilities.RpcMethodHandler(
|
| 1670 |
+
True,
|
| 1671 |
+
True,
|
| 1672 |
+
request_deserializer,
|
| 1673 |
+
response_serializer,
|
| 1674 |
+
None,
|
| 1675 |
+
None,
|
| 1676 |
+
None,
|
| 1677 |
+
behavior,
|
| 1678 |
+
)
|
| 1679 |
+
|
| 1680 |
+
|
| 1681 |
+
def method_handlers_generic_handler(service, method_handlers):
|
| 1682 |
+
"""Creates a GenericRpcHandler from RpcMethodHandlers.
|
| 1683 |
+
|
| 1684 |
+
Args:
|
| 1685 |
+
service: The name of the service that is implemented by the
|
| 1686 |
+
method_handlers.
|
| 1687 |
+
method_handlers: A dictionary that maps method names to corresponding
|
| 1688 |
+
RpcMethodHandler.
|
| 1689 |
+
|
| 1690 |
+
Returns:
|
| 1691 |
+
A GenericRpcHandler. This is typically added to the grpc.Server object
|
| 1692 |
+
with add_generic_rpc_handlers() before starting the server.
|
| 1693 |
+
"""
|
| 1694 |
+
from grpc import _utilities # pylint: disable=cyclic-import
|
| 1695 |
+
|
| 1696 |
+
return _utilities.DictionaryGenericHandler(service, method_handlers)
|
| 1697 |
+
|
| 1698 |
+
|
| 1699 |
+
def ssl_channel_credentials(
|
| 1700 |
+
root_certificates=None, private_key=None, certificate_chain=None
|
| 1701 |
+
):
|
| 1702 |
+
"""Creates a ChannelCredentials for use with an SSL-enabled Channel.
|
| 1703 |
+
|
| 1704 |
+
Args:
|
| 1705 |
+
root_certificates: The PEM-encoded root certificates as a byte string,
|
| 1706 |
+
or None to retrieve them from a default location chosen by gRPC
|
| 1707 |
+
runtime.
|
| 1708 |
+
private_key: The PEM-encoded private key as a byte string, or None if no
|
| 1709 |
+
private key should be used.
|
| 1710 |
+
certificate_chain: The PEM-encoded certificate chain as a byte string
|
| 1711 |
+
to use or None if no certificate chain should be used.
|
| 1712 |
+
|
| 1713 |
+
Returns:
|
| 1714 |
+
A ChannelCredentials for use with an SSL-enabled Channel.
|
| 1715 |
+
"""
|
| 1716 |
+
return ChannelCredentials(
|
| 1717 |
+
_cygrpc.SSLChannelCredentials(
|
| 1718 |
+
root_certificates, private_key, certificate_chain
|
| 1719 |
+
)
|
| 1720 |
+
)
|
| 1721 |
+
|
| 1722 |
+
|
| 1723 |
+
def xds_channel_credentials(fallback_credentials=None):
|
| 1724 |
+
"""Creates a ChannelCredentials for use with xDS. This is an EXPERIMENTAL
|
| 1725 |
+
API.
|
| 1726 |
+
|
| 1727 |
+
Args:
|
| 1728 |
+
fallback_credentials: Credentials to use in case it is not possible to
|
| 1729 |
+
establish a secure connection via xDS. If no fallback_credentials
|
| 1730 |
+
argument is supplied, a default SSLChannelCredentials is used.
|
| 1731 |
+
"""
|
| 1732 |
+
fallback_credentials = (
|
| 1733 |
+
ssl_channel_credentials()
|
| 1734 |
+
if fallback_credentials is None
|
| 1735 |
+
else fallback_credentials
|
| 1736 |
+
)
|
| 1737 |
+
return ChannelCredentials(
|
| 1738 |
+
_cygrpc.XDSChannelCredentials(fallback_credentials._credentials)
|
| 1739 |
+
)
|
| 1740 |
+
|
| 1741 |
+
|
| 1742 |
+
def metadata_call_credentials(metadata_plugin, name=None):
|
| 1743 |
+
"""Construct CallCredentials from an AuthMetadataPlugin.
|
| 1744 |
+
|
| 1745 |
+
Args:
|
| 1746 |
+
metadata_plugin: An AuthMetadataPlugin to use for authentication.
|
| 1747 |
+
name: An optional name for the plugin.
|
| 1748 |
+
|
| 1749 |
+
Returns:
|
| 1750 |
+
A CallCredentials.
|
| 1751 |
+
"""
|
| 1752 |
+
from grpc import _plugin_wrapping # pylint: disable=cyclic-import
|
| 1753 |
+
|
| 1754 |
+
return _plugin_wrapping.metadata_plugin_call_credentials(
|
| 1755 |
+
metadata_plugin, name
|
| 1756 |
+
)
|
| 1757 |
+
|
| 1758 |
+
|
| 1759 |
+
def access_token_call_credentials(access_token):
|
| 1760 |
+
"""Construct CallCredentials from an access token.
|
| 1761 |
+
|
| 1762 |
+
Args:
|
| 1763 |
+
access_token: A string to place directly in the http request
|
| 1764 |
+
authorization header, for example
|
| 1765 |
+
"authorization: Bearer <access_token>".
|
| 1766 |
+
|
| 1767 |
+
Returns:
|
| 1768 |
+
A CallCredentials.
|
| 1769 |
+
"""
|
| 1770 |
+
from grpc import _auth # pylint: disable=cyclic-import
|
| 1771 |
+
from grpc import _plugin_wrapping # pylint: disable=cyclic-import
|
| 1772 |
+
|
| 1773 |
+
return _plugin_wrapping.metadata_plugin_call_credentials(
|
| 1774 |
+
_auth.AccessTokenAuthMetadataPlugin(access_token), None
|
| 1775 |
+
)
|
| 1776 |
+
|
| 1777 |
+
|
| 1778 |
+
def composite_call_credentials(*call_credentials):
|
| 1779 |
+
"""Compose multiple CallCredentials to make a new CallCredentials.
|
| 1780 |
+
|
| 1781 |
+
Args:
|
| 1782 |
+
*call_credentials: At least two CallCredentials objects.
|
| 1783 |
+
|
| 1784 |
+
Returns:
|
| 1785 |
+
A CallCredentials object composed of the given CallCredentials objects.
|
| 1786 |
+
"""
|
| 1787 |
+
return CallCredentials(
|
| 1788 |
+
_cygrpc.CompositeCallCredentials(
|
| 1789 |
+
tuple(
|
| 1790 |
+
single_call_credentials._credentials
|
| 1791 |
+
for single_call_credentials in call_credentials
|
| 1792 |
+
)
|
| 1793 |
+
)
|
| 1794 |
+
)
|
| 1795 |
+
|
| 1796 |
+
|
| 1797 |
+
def composite_channel_credentials(channel_credentials, *call_credentials):
|
| 1798 |
+
"""Compose a ChannelCredentials and one or more CallCredentials objects.
|
| 1799 |
+
|
| 1800 |
+
Args:
|
| 1801 |
+
channel_credentials: A ChannelCredentials object.
|
| 1802 |
+
*call_credentials: One or more CallCredentials objects.
|
| 1803 |
+
|
| 1804 |
+
Returns:
|
| 1805 |
+
A ChannelCredentials composed of the given ChannelCredentials and
|
| 1806 |
+
CallCredentials objects.
|
| 1807 |
+
"""
|
| 1808 |
+
return ChannelCredentials(
|
| 1809 |
+
_cygrpc.CompositeChannelCredentials(
|
| 1810 |
+
tuple(
|
| 1811 |
+
single_call_credentials._credentials
|
| 1812 |
+
for single_call_credentials in call_credentials
|
| 1813 |
+
),
|
| 1814 |
+
channel_credentials._credentials,
|
| 1815 |
+
)
|
| 1816 |
+
)
|
| 1817 |
+
|
| 1818 |
+
|
| 1819 |
+
def ssl_server_credentials(
|
| 1820 |
+
private_key_certificate_chain_pairs,
|
| 1821 |
+
root_certificates=None,
|
| 1822 |
+
require_client_auth=False,
|
| 1823 |
+
):
|
| 1824 |
+
"""Creates a ServerCredentials for use with an SSL-enabled Server.
|
| 1825 |
+
|
| 1826 |
+
Args:
|
| 1827 |
+
private_key_certificate_chain_pairs: A list of pairs of the form
|
| 1828 |
+
[PEM-encoded private key, PEM-encoded certificate chain].
|
| 1829 |
+
root_certificates: An optional byte string of PEM-encoded client root
|
| 1830 |
+
certificates that the server will use to verify client authentication.
|
| 1831 |
+
If omitted, require_client_auth must also be False.
|
| 1832 |
+
require_client_auth: A boolean indicating whether or not to require
|
| 1833 |
+
clients to be authenticated. May only be True if root_certificates
|
| 1834 |
+
is not None.
|
| 1835 |
+
|
| 1836 |
+
Returns:
|
| 1837 |
+
A ServerCredentials for use with an SSL-enabled Server. Typically, this
|
| 1838 |
+
object is an argument to add_secure_port() method during server setup.
|
| 1839 |
+
"""
|
| 1840 |
+
if not private_key_certificate_chain_pairs:
|
| 1841 |
+
raise ValueError(
|
| 1842 |
+
"At least one private key-certificate chain pair is required!"
|
| 1843 |
+
)
|
| 1844 |
+
elif require_client_auth and root_certificates is None:
|
| 1845 |
+
raise ValueError(
|
| 1846 |
+
"Illegal to require client auth without providing root"
|
| 1847 |
+
" certificates!"
|
| 1848 |
+
)
|
| 1849 |
+
else:
|
| 1850 |
+
return ServerCredentials(
|
| 1851 |
+
_cygrpc.server_credentials_ssl(
|
| 1852 |
+
root_certificates,
|
| 1853 |
+
[
|
| 1854 |
+
_cygrpc.SslPemKeyCertPair(key, pem)
|
| 1855 |
+
for key, pem in private_key_certificate_chain_pairs
|
| 1856 |
+
],
|
| 1857 |
+
require_client_auth,
|
| 1858 |
+
)
|
| 1859 |
+
)
|
| 1860 |
+
|
| 1861 |
+
|
| 1862 |
+
def xds_server_credentials(fallback_credentials):
|
| 1863 |
+
"""Creates a ServerCredentials for use with xDS. This is an EXPERIMENTAL
|
| 1864 |
+
API.
|
| 1865 |
+
|
| 1866 |
+
Args:
|
| 1867 |
+
fallback_credentials: Credentials to use in case it is not possible to
|
| 1868 |
+
establish a secure connection via xDS. No default value is provided.
|
| 1869 |
+
"""
|
| 1870 |
+
return ServerCredentials(
|
| 1871 |
+
_cygrpc.xds_server_credentials(fallback_credentials._credentials)
|
| 1872 |
+
)
|
| 1873 |
+
|
| 1874 |
+
|
| 1875 |
+
def insecure_server_credentials():
|
| 1876 |
+
"""Creates a credentials object directing the server to use no credentials.
|
| 1877 |
+
This is an EXPERIMENTAL API.
|
| 1878 |
+
|
| 1879 |
+
This object cannot be used directly in a call to `add_secure_port`.
|
| 1880 |
+
Instead, it should be used to construct other credentials objects, e.g.
|
| 1881 |
+
with xds_server_credentials.
|
| 1882 |
+
"""
|
| 1883 |
+
return ServerCredentials(_cygrpc.insecure_server_credentials())
|
| 1884 |
+
|
| 1885 |
+
|
| 1886 |
+
def ssl_server_certificate_configuration(
|
| 1887 |
+
private_key_certificate_chain_pairs, root_certificates=None
|
| 1888 |
+
):
|
| 1889 |
+
"""Creates a ServerCertificateConfiguration for use with a Server.
|
| 1890 |
+
|
| 1891 |
+
Args:
|
| 1892 |
+
private_key_certificate_chain_pairs: A collection of pairs of
|
| 1893 |
+
the form [PEM-encoded private key, PEM-encoded certificate
|
| 1894 |
+
chain].
|
| 1895 |
+
root_certificates: An optional byte string of PEM-encoded client root
|
| 1896 |
+
certificates that the server will use to verify client authentication.
|
| 1897 |
+
|
| 1898 |
+
Returns:
|
| 1899 |
+
A ServerCertificateConfiguration that can be returned in the certificate
|
| 1900 |
+
configuration fetching callback.
|
| 1901 |
+
"""
|
| 1902 |
+
if private_key_certificate_chain_pairs:
|
| 1903 |
+
return ServerCertificateConfiguration(
|
| 1904 |
+
_cygrpc.server_certificate_config_ssl(
|
| 1905 |
+
root_certificates,
|
| 1906 |
+
[
|
| 1907 |
+
_cygrpc.SslPemKeyCertPair(key, pem)
|
| 1908 |
+
for key, pem in private_key_certificate_chain_pairs
|
| 1909 |
+
],
|
| 1910 |
+
)
|
| 1911 |
+
)
|
| 1912 |
+
else:
|
| 1913 |
+
raise ValueError(
|
| 1914 |
+
"At least one private key-certificate chain pair is required!"
|
| 1915 |
+
)
|
| 1916 |
+
|
| 1917 |
+
|
| 1918 |
+
def dynamic_ssl_server_credentials(
|
| 1919 |
+
initial_certificate_configuration,
|
| 1920 |
+
certificate_configuration_fetcher,
|
| 1921 |
+
require_client_authentication=False,
|
| 1922 |
+
):
|
| 1923 |
+
"""Creates a ServerCredentials for use with an SSL-enabled Server.
|
| 1924 |
+
|
| 1925 |
+
Args:
|
| 1926 |
+
initial_certificate_configuration (ServerCertificateConfiguration): The
|
| 1927 |
+
certificate configuration with which the server will be initialized.
|
| 1928 |
+
certificate_configuration_fetcher (callable): A callable that takes no
|
| 1929 |
+
arguments and should return a ServerCertificateConfiguration to
|
| 1930 |
+
replace the server's current certificate, or None for no change
|
| 1931 |
+
(i.e., the server will continue its current certificate
|
| 1932 |
+
config). The library will call this callback on *every* new
|
| 1933 |
+
client connection before starting the TLS handshake with the
|
| 1934 |
+
client, thus allowing the user application to optionally
|
| 1935 |
+
return a new ServerCertificateConfiguration that the server will then
|
| 1936 |
+
use for the handshake.
|
| 1937 |
+
require_client_authentication: A boolean indicating whether or not to
|
| 1938 |
+
require clients to be authenticated.
|
| 1939 |
+
|
| 1940 |
+
Returns:
|
| 1941 |
+
A ServerCredentials.
|
| 1942 |
+
"""
|
| 1943 |
+
return ServerCredentials(
|
| 1944 |
+
_cygrpc.server_credentials_ssl_dynamic_cert_config(
|
| 1945 |
+
initial_certificate_configuration,
|
| 1946 |
+
certificate_configuration_fetcher,
|
| 1947 |
+
require_client_authentication,
|
| 1948 |
+
)
|
| 1949 |
+
)
|
| 1950 |
+
|
| 1951 |
+
|
| 1952 |
+
@enum.unique
|
| 1953 |
+
class LocalConnectionType(enum.Enum):
|
| 1954 |
+
"""Types of local connection for local credential creation.
|
| 1955 |
+
|
| 1956 |
+
Attributes:
|
| 1957 |
+
UDS: Unix domain socket connections
|
| 1958 |
+
LOCAL_TCP: Local TCP connections.
|
| 1959 |
+
"""
|
| 1960 |
+
|
| 1961 |
+
UDS = _cygrpc.LocalConnectionType.uds
|
| 1962 |
+
LOCAL_TCP = _cygrpc.LocalConnectionType.local_tcp
|
| 1963 |
+
|
| 1964 |
+
|
| 1965 |
+
def local_channel_credentials(local_connect_type=LocalConnectionType.LOCAL_TCP):
|
| 1966 |
+
"""Creates a local ChannelCredentials used for local connections.
|
| 1967 |
+
|
| 1968 |
+
This is an EXPERIMENTAL API.
|
| 1969 |
+
|
| 1970 |
+
Local credentials are used by local TCP endpoints (e.g. localhost:10000)
|
| 1971 |
+
also UDS connections.
|
| 1972 |
+
|
| 1973 |
+
The connections created by local channel credentials are not
|
| 1974 |
+
encrypted, but will be checked if they are local or not.
|
| 1975 |
+
The UDS connections are considered secure by providing peer authentication
|
| 1976 |
+
and data confidentiality while TCP connections are considered insecure.
|
| 1977 |
+
|
| 1978 |
+
It is allowed to transmit call credentials over connections created by
|
| 1979 |
+
local channel credentials.
|
| 1980 |
+
|
| 1981 |
+
Local channel credentials are useful for 1) eliminating insecure_channel usage;
|
| 1982 |
+
2) enable unit testing for call credentials without setting up secrets.
|
| 1983 |
+
|
| 1984 |
+
Args:
|
| 1985 |
+
local_connect_type: Local connection type (either
|
| 1986 |
+
grpc.LocalConnectionType.UDS or grpc.LocalConnectionType.LOCAL_TCP)
|
| 1987 |
+
|
| 1988 |
+
Returns:
|
| 1989 |
+
A ChannelCredentials for use with a local Channel
|
| 1990 |
+
"""
|
| 1991 |
+
return ChannelCredentials(
|
| 1992 |
+
_cygrpc.channel_credentials_local(local_connect_type.value)
|
| 1993 |
+
)
|
| 1994 |
+
|
| 1995 |
+
|
| 1996 |
+
def local_server_credentials(local_connect_type=LocalConnectionType.LOCAL_TCP):
|
| 1997 |
+
"""Creates a local ServerCredentials used for local connections.
|
| 1998 |
+
|
| 1999 |
+
This is an EXPERIMENTAL API.
|
| 2000 |
+
|
| 2001 |
+
Local credentials are used by local TCP endpoints (e.g. localhost:10000)
|
| 2002 |
+
also UDS connections.
|
| 2003 |
+
|
| 2004 |
+
The connections created by local server credentials are not
|
| 2005 |
+
encrypted, but will be checked if they are local or not.
|
| 2006 |
+
The UDS connections are considered secure by providing peer authentication
|
| 2007 |
+
and data confidentiality while TCP connections are considered insecure.
|
| 2008 |
+
|
| 2009 |
+
It is allowed to transmit call credentials over connections created by local
|
| 2010 |
+
server credentials.
|
| 2011 |
+
|
| 2012 |
+
Local server credentials are useful for 1) eliminating insecure_channel usage;
|
| 2013 |
+
2) enable unit testing for call credentials without setting up secrets.
|
| 2014 |
+
|
| 2015 |
+
Args:
|
| 2016 |
+
local_connect_type: Local connection type (either
|
| 2017 |
+
grpc.LocalConnectionType.UDS or grpc.LocalConnectionType.LOCAL_TCP)
|
| 2018 |
+
|
| 2019 |
+
Returns:
|
| 2020 |
+
A ServerCredentials for use with a local Server
|
| 2021 |
+
"""
|
| 2022 |
+
return ServerCredentials(
|
| 2023 |
+
_cygrpc.server_credentials_local(local_connect_type.value)
|
| 2024 |
+
)
|
| 2025 |
+
|
| 2026 |
+
|
| 2027 |
+
def alts_channel_credentials(service_accounts=None):
|
| 2028 |
+
"""Creates a ChannelCredentials for use with an ALTS-enabled Channel.
|
| 2029 |
+
|
| 2030 |
+
This is an EXPERIMENTAL API.
|
| 2031 |
+
ALTS credentials API can only be used in GCP environment as it relies on
|
| 2032 |
+
handshaker service being available. For more info about ALTS see
|
| 2033 |
+
https://cloud.google.com/security/encryption-in-transit/application-layer-transport-security
|
| 2034 |
+
|
| 2035 |
+
Args:
|
| 2036 |
+
service_accounts: A list of server identities accepted by the client.
|
| 2037 |
+
If target service accounts are provided and none of them matches the
|
| 2038 |
+
peer identity of the server, handshake will fail. The arg can be empty
|
| 2039 |
+
if the client does not have any information about trusted server
|
| 2040 |
+
identity.
|
| 2041 |
+
Returns:
|
| 2042 |
+
A ChannelCredentials for use with an ALTS-enabled Channel
|
| 2043 |
+
"""
|
| 2044 |
+
return ChannelCredentials(
|
| 2045 |
+
_cygrpc.channel_credentials_alts(service_accounts or [])
|
| 2046 |
+
)
|
| 2047 |
+
|
| 2048 |
+
|
| 2049 |
+
def alts_server_credentials():
|
| 2050 |
+
"""Creates a ServerCredentials for use with an ALTS-enabled connection.
|
| 2051 |
+
|
| 2052 |
+
This is an EXPERIMENTAL API.
|
| 2053 |
+
ALTS credentials API can only be used in GCP environment as it relies on
|
| 2054 |
+
handshaker service being available. For more info about ALTS see
|
| 2055 |
+
https://cloud.google.com/security/encryption-in-transit/application-layer-transport-security
|
| 2056 |
+
|
| 2057 |
+
Returns:
|
| 2058 |
+
A ServerCredentials for use with an ALTS-enabled Server
|
| 2059 |
+
"""
|
| 2060 |
+
return ServerCredentials(_cygrpc.server_credentials_alts())
|
| 2061 |
+
|
| 2062 |
+
|
| 2063 |
+
def compute_engine_channel_credentials(call_credentials):
|
| 2064 |
+
"""Creates a compute engine channel credential.
|
| 2065 |
+
|
| 2066 |
+
This credential can only be used in a GCP environment as it relies on
|
| 2067 |
+
a handshaker service. For more info about ALTS, see
|
| 2068 |
+
https://cloud.google.com/security/encryption-in-transit/application-layer-transport-security
|
| 2069 |
+
|
| 2070 |
+
This channel credential is expected to be used as part of a composite
|
| 2071 |
+
credential in conjunction with a call credentials that authenticates the
|
| 2072 |
+
VM's default service account. If used with any other sort of call
|
| 2073 |
+
credential, the connection may suddenly and unexpectedly begin failing RPCs.
|
| 2074 |
+
"""
|
| 2075 |
+
return ChannelCredentials(
|
| 2076 |
+
_cygrpc.channel_credentials_compute_engine(
|
| 2077 |
+
call_credentials._credentials
|
| 2078 |
+
)
|
| 2079 |
+
)
|
| 2080 |
+
|
| 2081 |
+
|
| 2082 |
+
def channel_ready_future(channel):
|
| 2083 |
+
"""Creates a Future that tracks when a Channel is ready.
|
| 2084 |
+
|
| 2085 |
+
Cancelling the Future does not affect the channel's state machine.
|
| 2086 |
+
It merely decouples the Future from channel state machine.
|
| 2087 |
+
|
| 2088 |
+
Args:
|
| 2089 |
+
channel: A Channel object.
|
| 2090 |
+
|
| 2091 |
+
Returns:
|
| 2092 |
+
A Future object that matures when the channel connectivity is
|
| 2093 |
+
ChannelConnectivity.READY.
|
| 2094 |
+
"""
|
| 2095 |
+
from grpc import _utilities # pylint: disable=cyclic-import
|
| 2096 |
+
|
| 2097 |
+
return _utilities.channel_ready_future(channel)
|
| 2098 |
+
|
| 2099 |
+
|
| 2100 |
+
def insecure_channel(target, options=None, compression=None):
|
| 2101 |
+
"""Creates an insecure Channel to a server.
|
| 2102 |
+
|
| 2103 |
+
The returned Channel is thread-safe.
|
| 2104 |
+
|
| 2105 |
+
Args:
|
| 2106 |
+
target: The server address
|
| 2107 |
+
options: An optional list of key-value pairs (:term:`channel_arguments`
|
| 2108 |
+
in gRPC Core runtime) to configure the channel.
|
| 2109 |
+
compression: An optional value indicating the compression method to be
|
| 2110 |
+
used over the lifetime of the channel.
|
| 2111 |
+
|
| 2112 |
+
Returns:
|
| 2113 |
+
A Channel.
|
| 2114 |
+
"""
|
| 2115 |
+
from grpc import _channel # pylint: disable=cyclic-import
|
| 2116 |
+
|
| 2117 |
+
return _channel.Channel(
|
| 2118 |
+
target, () if options is None else options, None, compression
|
| 2119 |
+
)
|
| 2120 |
+
|
| 2121 |
+
|
| 2122 |
+
def secure_channel(target, credentials, options=None, compression=None):
|
| 2123 |
+
"""Creates a secure Channel to a server.
|
| 2124 |
+
|
| 2125 |
+
The returned Channel is thread-safe.
|
| 2126 |
+
|
| 2127 |
+
Args:
|
| 2128 |
+
target: The server address.
|
| 2129 |
+
credentials: A ChannelCredentials instance.
|
| 2130 |
+
options: An optional list of key-value pairs (:term:`channel_arguments`
|
| 2131 |
+
in gRPC Core runtime) to configure the channel.
|
| 2132 |
+
compression: An optional value indicating the compression method to be
|
| 2133 |
+
used over the lifetime of the channel.
|
| 2134 |
+
|
| 2135 |
+
Returns:
|
| 2136 |
+
A Channel.
|
| 2137 |
+
"""
|
| 2138 |
+
from grpc import _channel # pylint: disable=cyclic-import
|
| 2139 |
+
from grpc.experimental import _insecure_channel_credentials
|
| 2140 |
+
|
| 2141 |
+
if credentials._credentials is _insecure_channel_credentials:
|
| 2142 |
+
raise ValueError(
|
| 2143 |
+
"secure_channel cannot be called with insecure credentials."
|
| 2144 |
+
+ " Call insecure_channel instead."
|
| 2145 |
+
)
|
| 2146 |
+
return _channel.Channel(
|
| 2147 |
+
target,
|
| 2148 |
+
() if options is None else options,
|
| 2149 |
+
credentials._credentials,
|
| 2150 |
+
compression,
|
| 2151 |
+
)
|
| 2152 |
+
|
| 2153 |
+
|
| 2154 |
+
def intercept_channel(channel, *interceptors):
|
| 2155 |
+
"""Intercepts a channel through a set of interceptors.
|
| 2156 |
+
|
| 2157 |
+
Args:
|
| 2158 |
+
channel: A Channel.
|
| 2159 |
+
interceptors: Zero or more objects of type
|
| 2160 |
+
UnaryUnaryClientInterceptor,
|
| 2161 |
+
UnaryStreamClientInterceptor,
|
| 2162 |
+
StreamUnaryClientInterceptor, or
|
| 2163 |
+
StreamStreamClientInterceptor.
|
| 2164 |
+
Interceptors are given control in the order they are listed.
|
| 2165 |
+
|
| 2166 |
+
Returns:
|
| 2167 |
+
A Channel that intercepts each invocation via the provided interceptors.
|
| 2168 |
+
|
| 2169 |
+
Raises:
|
| 2170 |
+
TypeError: If interceptor does not derive from any of
|
| 2171 |
+
UnaryUnaryClientInterceptor,
|
| 2172 |
+
UnaryStreamClientInterceptor,
|
| 2173 |
+
StreamUnaryClientInterceptor, or
|
| 2174 |
+
StreamStreamClientInterceptor.
|
| 2175 |
+
"""
|
| 2176 |
+
from grpc import _interceptor # pylint: disable=cyclic-import
|
| 2177 |
+
|
| 2178 |
+
return _interceptor.intercept_channel(channel, *interceptors)
|
| 2179 |
+
|
| 2180 |
+
|
| 2181 |
+
def server(
|
| 2182 |
+
thread_pool,
|
| 2183 |
+
handlers=None,
|
| 2184 |
+
interceptors=None,
|
| 2185 |
+
options=None,
|
| 2186 |
+
maximum_concurrent_rpcs=None,
|
| 2187 |
+
compression=None,
|
| 2188 |
+
xds=False,
|
| 2189 |
+
):
|
| 2190 |
+
"""Creates a Server with which RPCs can be serviced.
|
| 2191 |
+
|
| 2192 |
+
Args:
|
| 2193 |
+
thread_pool: A futures.ThreadPoolExecutor to be used by the Server
|
| 2194 |
+
to execute RPC handlers.
|
| 2195 |
+
handlers: An optional list of GenericRpcHandlers used for executing RPCs.
|
| 2196 |
+
More handlers may be added by calling add_generic_rpc_handlers any time
|
| 2197 |
+
before the server is started.
|
| 2198 |
+
interceptors: An optional list of ServerInterceptor objects that observe
|
| 2199 |
+
and optionally manipulate the incoming RPCs before handing them over to
|
| 2200 |
+
handlers. The interceptors are given control in the order they are
|
| 2201 |
+
specified. This is an EXPERIMENTAL API.
|
| 2202 |
+
options: An optional list of key-value pairs (:term:`channel_arguments` in gRPC runtime)
|
| 2203 |
+
to configure the channel.
|
| 2204 |
+
maximum_concurrent_rpcs: The maximum number of concurrent RPCs this server
|
| 2205 |
+
will service before returning RESOURCE_EXHAUSTED status, or None to
|
| 2206 |
+
indicate no limit.
|
| 2207 |
+
compression: An element of grpc.compression, e.g.
|
| 2208 |
+
grpc.compression.Gzip. This compression algorithm will be used for the
|
| 2209 |
+
lifetime of the server unless overridden.
|
| 2210 |
+
xds: If set to true, retrieves server configuration via xDS. This is an
|
| 2211 |
+
EXPERIMENTAL option.
|
| 2212 |
+
|
| 2213 |
+
Returns:
|
| 2214 |
+
A Server object.
|
| 2215 |
+
"""
|
| 2216 |
+
from grpc import _server # pylint: disable=cyclic-import
|
| 2217 |
+
|
| 2218 |
+
return _server.create_server(
|
| 2219 |
+
thread_pool,
|
| 2220 |
+
() if handlers is None else handlers,
|
| 2221 |
+
() if interceptors is None else interceptors,
|
| 2222 |
+
() if options is None else options,
|
| 2223 |
+
maximum_concurrent_rpcs,
|
| 2224 |
+
compression,
|
| 2225 |
+
xds,
|
| 2226 |
+
)
|
| 2227 |
+
|
| 2228 |
+
|
| 2229 |
+
@contextlib.contextmanager
|
| 2230 |
+
def _create_servicer_context(rpc_event, state, request_deserializer):
|
| 2231 |
+
from grpc import _server # pylint: disable=cyclic-import
|
| 2232 |
+
|
| 2233 |
+
context = _server._Context(rpc_event, state, request_deserializer)
|
| 2234 |
+
yield context
|
| 2235 |
+
context._finalize_state() # pylint: disable=protected-access
|
| 2236 |
+
|
| 2237 |
+
|
| 2238 |
+
@enum.unique
|
| 2239 |
+
class Compression(enum.IntEnum):
|
| 2240 |
+
"""Indicates the compression method to be used for an RPC.
|
| 2241 |
+
|
| 2242 |
+
Attributes:
|
| 2243 |
+
NoCompression: Do not use compression algorithm.
|
| 2244 |
+
Deflate: Use "Deflate" compression algorithm.
|
| 2245 |
+
Gzip: Use "Gzip" compression algorithm.
|
| 2246 |
+
"""
|
| 2247 |
+
|
| 2248 |
+
NoCompression = _compression.NoCompression
|
| 2249 |
+
Deflate = _compression.Deflate
|
| 2250 |
+
Gzip = _compression.Gzip
|
| 2251 |
+
|
| 2252 |
+
|
| 2253 |
+
################################### __all__ #################################
|
| 2254 |
+
|
| 2255 |
+
__all__ = (
|
| 2256 |
+
"FutureTimeoutError",
|
| 2257 |
+
"FutureCancelledError",
|
| 2258 |
+
"Future",
|
| 2259 |
+
"ChannelConnectivity",
|
| 2260 |
+
"StatusCode",
|
| 2261 |
+
"Status",
|
| 2262 |
+
"RpcError",
|
| 2263 |
+
"RpcContext",
|
| 2264 |
+
"Call",
|
| 2265 |
+
"ChannelCredentials",
|
| 2266 |
+
"CallCredentials",
|
| 2267 |
+
"AuthMetadataContext",
|
| 2268 |
+
"AuthMetadataPluginCallback",
|
| 2269 |
+
"AuthMetadataPlugin",
|
| 2270 |
+
"Compression",
|
| 2271 |
+
"ClientCallDetails",
|
| 2272 |
+
"ServerCertificateConfiguration",
|
| 2273 |
+
"ServerCredentials",
|
| 2274 |
+
"LocalConnectionType",
|
| 2275 |
+
"UnaryUnaryMultiCallable",
|
| 2276 |
+
"UnaryStreamMultiCallable",
|
| 2277 |
+
"StreamUnaryMultiCallable",
|
| 2278 |
+
"StreamStreamMultiCallable",
|
| 2279 |
+
"UnaryUnaryClientInterceptor",
|
| 2280 |
+
"UnaryStreamClientInterceptor",
|
| 2281 |
+
"StreamUnaryClientInterceptor",
|
| 2282 |
+
"StreamStreamClientInterceptor",
|
| 2283 |
+
"Channel",
|
| 2284 |
+
"ServicerContext",
|
| 2285 |
+
"RpcMethodHandler",
|
| 2286 |
+
"HandlerCallDetails",
|
| 2287 |
+
"GenericRpcHandler",
|
| 2288 |
+
"ServiceRpcHandler",
|
| 2289 |
+
"Server",
|
| 2290 |
+
"ServerInterceptor",
|
| 2291 |
+
"unary_unary_rpc_method_handler",
|
| 2292 |
+
"unary_stream_rpc_method_handler",
|
| 2293 |
+
"stream_unary_rpc_method_handler",
|
| 2294 |
+
"stream_stream_rpc_method_handler",
|
| 2295 |
+
"method_handlers_generic_handler",
|
| 2296 |
+
"ssl_channel_credentials",
|
| 2297 |
+
"metadata_call_credentials",
|
| 2298 |
+
"access_token_call_credentials",
|
| 2299 |
+
"composite_call_credentials",
|
| 2300 |
+
"composite_channel_credentials",
|
| 2301 |
+
"compute_engine_channel_credentials",
|
| 2302 |
+
"local_channel_credentials",
|
| 2303 |
+
"local_server_credentials",
|
| 2304 |
+
"alts_channel_credentials",
|
| 2305 |
+
"alts_server_credentials",
|
| 2306 |
+
"ssl_server_credentials",
|
| 2307 |
+
"ssl_server_certificate_configuration",
|
| 2308 |
+
"dynamic_ssl_server_credentials",
|
| 2309 |
+
"channel_ready_future",
|
| 2310 |
+
"insecure_channel",
|
| 2311 |
+
"secure_channel",
|
| 2312 |
+
"intercept_channel",
|
| 2313 |
+
"server",
|
| 2314 |
+
"protos",
|
| 2315 |
+
"services",
|
| 2316 |
+
"protos_and_services",
|
| 2317 |
+
"xds_channel_credentials",
|
| 2318 |
+
"xds_server_credentials",
|
| 2319 |
+
"insecure_server_credentials",
|
| 2320 |
+
)
|
| 2321 |
+
|
| 2322 |
+
############################### Extension Shims ################################
|
| 2323 |
+
|
| 2324 |
+
# Here to maintain backwards compatibility; avoid using these in new code!
|
| 2325 |
+
try:
|
| 2326 |
+
import grpc_tools
|
| 2327 |
+
|
| 2328 |
+
sys.modules.update({"grpc.tools": grpc_tools})
|
| 2329 |
+
except ImportError:
|
| 2330 |
+
pass
|
| 2331 |
+
try:
|
| 2332 |
+
import grpc_health
|
| 2333 |
+
|
| 2334 |
+
sys.modules.update({"grpc.health": grpc_health})
|
| 2335 |
+
except ImportError:
|
| 2336 |
+
pass
|
| 2337 |
+
try:
|
| 2338 |
+
import grpc_reflection
|
| 2339 |
+
|
| 2340 |
+
sys.modules.update({"grpc.reflection": grpc_reflection})
|
| 2341 |
+
except ImportError:
|
| 2342 |
+
pass
|
| 2343 |
+
|
| 2344 |
+
# Prevents import order issue in the case of renamed path.
|
| 2345 |
+
if sys.version_info >= (3, 6) and __name__ == "grpc":
|
| 2346 |
+
from grpc import aio # pylint: disable=ungrouped-imports
|
| 2347 |
+
|
| 2348 |
+
sys.modules.update({"grpc.aio": aio})
|
.venv/lib/python3.11/site-packages/grpc/_auth.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""GRPCAuthMetadataPlugins for standard authentication."""
|
| 15 |
+
|
| 16 |
+
import inspect
|
| 17 |
+
from typing import Any, Optional
|
| 18 |
+
|
| 19 |
+
import grpc
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def _sign_request(
|
| 23 |
+
callback: grpc.AuthMetadataPluginCallback,
|
| 24 |
+
token: Optional[str],
|
| 25 |
+
error: Optional[Exception],
|
| 26 |
+
):
|
| 27 |
+
metadata = (("authorization", "Bearer {}".format(token)),)
|
| 28 |
+
callback(metadata, error)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class GoogleCallCredentials(grpc.AuthMetadataPlugin):
|
| 32 |
+
"""Metadata wrapper for GoogleCredentials from the oauth2client library."""
|
| 33 |
+
|
| 34 |
+
_is_jwt: bool
|
| 35 |
+
_credentials: Any
|
| 36 |
+
|
| 37 |
+
# TODO(xuanwn): Give credentials an actual type.
|
| 38 |
+
def __init__(self, credentials: Any):
|
| 39 |
+
self._credentials = credentials
|
| 40 |
+
# Hack to determine if these are JWT creds and we need to pass
|
| 41 |
+
# additional_claims when getting a token
|
| 42 |
+
self._is_jwt = (
|
| 43 |
+
"additional_claims"
|
| 44 |
+
in inspect.getfullargspec(credentials.get_access_token).args
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
def __call__(
|
| 48 |
+
self,
|
| 49 |
+
context: grpc.AuthMetadataContext,
|
| 50 |
+
callback: grpc.AuthMetadataPluginCallback,
|
| 51 |
+
):
|
| 52 |
+
try:
|
| 53 |
+
if self._is_jwt:
|
| 54 |
+
access_token = self._credentials.get_access_token(
|
| 55 |
+
additional_claims={
|
| 56 |
+
"aud": context.service_url # pytype: disable=attribute-error
|
| 57 |
+
}
|
| 58 |
+
).access_token
|
| 59 |
+
else:
|
| 60 |
+
access_token = self._credentials.get_access_token().access_token
|
| 61 |
+
except Exception as exception: # pylint: disable=broad-except
|
| 62 |
+
_sign_request(callback, None, exception)
|
| 63 |
+
else:
|
| 64 |
+
_sign_request(callback, access_token, None)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class AccessTokenAuthMetadataPlugin(grpc.AuthMetadataPlugin):
|
| 68 |
+
"""Metadata wrapper for raw access token credentials."""
|
| 69 |
+
|
| 70 |
+
_access_token: str
|
| 71 |
+
|
| 72 |
+
def __init__(self, access_token: str):
|
| 73 |
+
self._access_token = access_token
|
| 74 |
+
|
| 75 |
+
def __call__(
|
| 76 |
+
self,
|
| 77 |
+
context: grpc.AuthMetadataContext,
|
| 78 |
+
callback: grpc.AuthMetadataPluginCallback,
|
| 79 |
+
):
|
| 80 |
+
_sign_request(callback, self._access_token, None)
|
.venv/lib/python3.11/site-packages/grpc/_channel.py
ADDED
|
@@ -0,0 +1,2267 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Invocation-side implementation of gRPC Python."""
|
| 15 |
+
|
| 16 |
+
import copy
|
| 17 |
+
import functools
|
| 18 |
+
import logging
|
| 19 |
+
import os
|
| 20 |
+
import sys
|
| 21 |
+
import threading
|
| 22 |
+
import time
|
| 23 |
+
import types
|
| 24 |
+
from typing import (
|
| 25 |
+
Any,
|
| 26 |
+
Callable,
|
| 27 |
+
Dict,
|
| 28 |
+
Iterator,
|
| 29 |
+
List,
|
| 30 |
+
Optional,
|
| 31 |
+
Sequence,
|
| 32 |
+
Set,
|
| 33 |
+
Tuple,
|
| 34 |
+
Union,
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
import grpc # pytype: disable=pyi-error
|
| 38 |
+
from grpc import _common # pytype: disable=pyi-error
|
| 39 |
+
from grpc import _compression # pytype: disable=pyi-error
|
| 40 |
+
from grpc import _grpcio_metadata # pytype: disable=pyi-error
|
| 41 |
+
from grpc import _observability # pytype: disable=pyi-error
|
| 42 |
+
from grpc._cython import cygrpc
|
| 43 |
+
from grpc._typing import ChannelArgumentType
|
| 44 |
+
from grpc._typing import DeserializingFunction
|
| 45 |
+
from grpc._typing import IntegratedCallFactory
|
| 46 |
+
from grpc._typing import MetadataType
|
| 47 |
+
from grpc._typing import NullaryCallbackType
|
| 48 |
+
from grpc._typing import ResponseType
|
| 49 |
+
from grpc._typing import SerializingFunction
|
| 50 |
+
from grpc._typing import UserTag
|
| 51 |
+
import grpc.experimental # pytype: disable=pyi-error
|
| 52 |
+
|
| 53 |
+
_LOGGER = logging.getLogger(__name__)
|
| 54 |
+
|
| 55 |
+
_USER_AGENT = "grpc-python/{}".format(_grpcio_metadata.__version__)
|
| 56 |
+
|
| 57 |
+
_EMPTY_FLAGS = 0
|
| 58 |
+
|
| 59 |
+
# NOTE(rbellevi): No guarantees are given about the maintenance of this
|
| 60 |
+
# environment variable.
|
| 61 |
+
_DEFAULT_SINGLE_THREADED_UNARY_STREAM = (
|
| 62 |
+
os.getenv("GRPC_SINGLE_THREADED_UNARY_STREAM") is not None
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
_UNARY_UNARY_INITIAL_DUE = (
|
| 66 |
+
cygrpc.OperationType.send_initial_metadata,
|
| 67 |
+
cygrpc.OperationType.send_message,
|
| 68 |
+
cygrpc.OperationType.send_close_from_client,
|
| 69 |
+
cygrpc.OperationType.receive_initial_metadata,
|
| 70 |
+
cygrpc.OperationType.receive_message,
|
| 71 |
+
cygrpc.OperationType.receive_status_on_client,
|
| 72 |
+
)
|
| 73 |
+
_UNARY_STREAM_INITIAL_DUE = (
|
| 74 |
+
cygrpc.OperationType.send_initial_metadata,
|
| 75 |
+
cygrpc.OperationType.send_message,
|
| 76 |
+
cygrpc.OperationType.send_close_from_client,
|
| 77 |
+
cygrpc.OperationType.receive_initial_metadata,
|
| 78 |
+
cygrpc.OperationType.receive_status_on_client,
|
| 79 |
+
)
|
| 80 |
+
_STREAM_UNARY_INITIAL_DUE = (
|
| 81 |
+
cygrpc.OperationType.send_initial_metadata,
|
| 82 |
+
cygrpc.OperationType.receive_initial_metadata,
|
| 83 |
+
cygrpc.OperationType.receive_message,
|
| 84 |
+
cygrpc.OperationType.receive_status_on_client,
|
| 85 |
+
)
|
| 86 |
+
_STREAM_STREAM_INITIAL_DUE = (
|
| 87 |
+
cygrpc.OperationType.send_initial_metadata,
|
| 88 |
+
cygrpc.OperationType.receive_initial_metadata,
|
| 89 |
+
cygrpc.OperationType.receive_status_on_client,
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
_CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = (
|
| 93 |
+
"Exception calling channel subscription callback!"
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
_OK_RENDEZVOUS_REPR_FORMAT = (
|
| 97 |
+
'<{} of RPC that terminated with:\n\tstatus = {}\n\tdetails = "{}"\n>'
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
_NON_OK_RENDEZVOUS_REPR_FORMAT = (
|
| 101 |
+
"<{} of RPC that terminated with:\n"
|
| 102 |
+
"\tstatus = {}\n"
|
| 103 |
+
'\tdetails = "{}"\n'
|
| 104 |
+
'\tdebug_error_string = "{}"\n'
|
| 105 |
+
">"
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def _deadline(timeout: Optional[float]) -> Optional[float]:
|
| 110 |
+
return None if timeout is None else time.time() + timeout
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def _unknown_code_details(
|
| 114 |
+
unknown_cygrpc_code: Optional[grpc.StatusCode], details: Optional[str]
|
| 115 |
+
) -> str:
|
| 116 |
+
return 'Server sent unknown code {} and details "{}"'.format(
|
| 117 |
+
unknown_cygrpc_code, details
|
| 118 |
+
)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
class _RPCState(object):
|
| 122 |
+
condition: threading.Condition
|
| 123 |
+
due: Set[cygrpc.OperationType]
|
| 124 |
+
initial_metadata: Optional[MetadataType]
|
| 125 |
+
response: Any
|
| 126 |
+
trailing_metadata: Optional[MetadataType]
|
| 127 |
+
code: Optional[grpc.StatusCode]
|
| 128 |
+
details: Optional[str]
|
| 129 |
+
debug_error_string: Optional[str]
|
| 130 |
+
cancelled: bool
|
| 131 |
+
callbacks: List[NullaryCallbackType]
|
| 132 |
+
fork_epoch: Optional[int]
|
| 133 |
+
rpc_start_time: Optional[float] # In relative seconds
|
| 134 |
+
rpc_end_time: Optional[float] # In relative seconds
|
| 135 |
+
method: Optional[str]
|
| 136 |
+
target: Optional[str]
|
| 137 |
+
|
| 138 |
+
def __init__(
|
| 139 |
+
self,
|
| 140 |
+
due: Sequence[cygrpc.OperationType],
|
| 141 |
+
initial_metadata: Optional[MetadataType],
|
| 142 |
+
trailing_metadata: Optional[MetadataType],
|
| 143 |
+
code: Optional[grpc.StatusCode],
|
| 144 |
+
details: Optional[str],
|
| 145 |
+
):
|
| 146 |
+
# `condition` guards all members of _RPCState. `notify_all` is called on
|
| 147 |
+
# `condition` when the state of the RPC has changed.
|
| 148 |
+
self.condition = threading.Condition()
|
| 149 |
+
|
| 150 |
+
# The cygrpc.OperationType objects representing events due from the RPC's
|
| 151 |
+
# completion queue. If an operation is in `due`, it is guaranteed that
|
| 152 |
+
# `operate()` has been called on a corresponding operation. But the
|
| 153 |
+
# converse is not true. That is, in the case of failed `operate()`
|
| 154 |
+
# calls, there may briefly be events in `due` that do not correspond to
|
| 155 |
+
# operations submitted to Core.
|
| 156 |
+
self.due = set(due)
|
| 157 |
+
self.initial_metadata = initial_metadata
|
| 158 |
+
self.response = None
|
| 159 |
+
self.trailing_metadata = trailing_metadata
|
| 160 |
+
self.code = code
|
| 161 |
+
self.details = details
|
| 162 |
+
self.debug_error_string = None
|
| 163 |
+
# The following three fields are used for observability.
|
| 164 |
+
# Updates to those fields do not trigger self.condition.
|
| 165 |
+
self.rpc_start_time = None
|
| 166 |
+
self.rpc_end_time = None
|
| 167 |
+
self.method = None
|
| 168 |
+
self.target = None
|
| 169 |
+
|
| 170 |
+
# The semantics of grpc.Future.cancel and grpc.Future.cancelled are
|
| 171 |
+
# slightly wonky, so they have to be tracked separately from the rest of the
|
| 172 |
+
# result of the RPC. This field tracks whether cancellation was requested
|
| 173 |
+
# prior to termination of the RPC.
|
| 174 |
+
self.cancelled = False
|
| 175 |
+
self.callbacks = []
|
| 176 |
+
self.fork_epoch = cygrpc.get_fork_epoch()
|
| 177 |
+
|
| 178 |
+
def reset_postfork_child(self):
|
| 179 |
+
self.condition = threading.Condition()
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def _abort(state: _RPCState, code: grpc.StatusCode, details: str) -> None:
|
| 183 |
+
if state.code is None:
|
| 184 |
+
state.code = code
|
| 185 |
+
state.details = details
|
| 186 |
+
if state.initial_metadata is None:
|
| 187 |
+
state.initial_metadata = ()
|
| 188 |
+
state.trailing_metadata = ()
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def _handle_event(
|
| 192 |
+
event: cygrpc.BaseEvent,
|
| 193 |
+
state: _RPCState,
|
| 194 |
+
response_deserializer: Optional[DeserializingFunction],
|
| 195 |
+
) -> List[NullaryCallbackType]:
|
| 196 |
+
callbacks = []
|
| 197 |
+
for batch_operation in event.batch_operations:
|
| 198 |
+
operation_type = batch_operation.type()
|
| 199 |
+
state.due.remove(operation_type)
|
| 200 |
+
if operation_type == cygrpc.OperationType.receive_initial_metadata:
|
| 201 |
+
state.initial_metadata = batch_operation.initial_metadata()
|
| 202 |
+
elif operation_type == cygrpc.OperationType.receive_message:
|
| 203 |
+
serialized_response = batch_operation.message()
|
| 204 |
+
if serialized_response is not None:
|
| 205 |
+
response = _common.deserialize(
|
| 206 |
+
serialized_response, response_deserializer
|
| 207 |
+
)
|
| 208 |
+
if response is None:
|
| 209 |
+
details = "Exception deserializing response!"
|
| 210 |
+
_abort(state, grpc.StatusCode.INTERNAL, details)
|
| 211 |
+
else:
|
| 212 |
+
state.response = response
|
| 213 |
+
elif operation_type == cygrpc.OperationType.receive_status_on_client:
|
| 214 |
+
state.trailing_metadata = batch_operation.trailing_metadata()
|
| 215 |
+
if state.code is None:
|
| 216 |
+
code = _common.CYGRPC_STATUS_CODE_TO_STATUS_CODE.get(
|
| 217 |
+
batch_operation.code()
|
| 218 |
+
)
|
| 219 |
+
if code is None:
|
| 220 |
+
state.code = grpc.StatusCode.UNKNOWN
|
| 221 |
+
state.details = _unknown_code_details(
|
| 222 |
+
code, batch_operation.details()
|
| 223 |
+
)
|
| 224 |
+
else:
|
| 225 |
+
state.code = code
|
| 226 |
+
state.details = batch_operation.details()
|
| 227 |
+
state.debug_error_string = batch_operation.error_string()
|
| 228 |
+
state.rpc_end_time = time.perf_counter()
|
| 229 |
+
_observability.maybe_record_rpc_latency(state)
|
| 230 |
+
callbacks.extend(state.callbacks)
|
| 231 |
+
state.callbacks = None
|
| 232 |
+
return callbacks
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
def _event_handler(
|
| 236 |
+
state: _RPCState, response_deserializer: Optional[DeserializingFunction]
|
| 237 |
+
) -> UserTag:
|
| 238 |
+
def handle_event(event):
|
| 239 |
+
with state.condition:
|
| 240 |
+
callbacks = _handle_event(event, state, response_deserializer)
|
| 241 |
+
state.condition.notify_all()
|
| 242 |
+
done = not state.due
|
| 243 |
+
for callback in callbacks:
|
| 244 |
+
try:
|
| 245 |
+
callback()
|
| 246 |
+
except Exception as e: # pylint: disable=broad-except
|
| 247 |
+
# NOTE(rbellevi): We suppress but log errors here so as not to
|
| 248 |
+
# kill the channel spin thread.
|
| 249 |
+
logging.error(
|
| 250 |
+
"Exception in callback %s: %s", repr(callback.func), repr(e)
|
| 251 |
+
)
|
| 252 |
+
return done and state.fork_epoch >= cygrpc.get_fork_epoch()
|
| 253 |
+
|
| 254 |
+
return handle_event
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
# TODO(xuanwn): Create a base class for IntegratedCall and SegregatedCall.
|
| 258 |
+
# pylint: disable=too-many-statements
|
| 259 |
+
def _consume_request_iterator(
|
| 260 |
+
request_iterator: Iterator,
|
| 261 |
+
state: _RPCState,
|
| 262 |
+
call: Union[cygrpc.IntegratedCall, cygrpc.SegregatedCall],
|
| 263 |
+
request_serializer: SerializingFunction,
|
| 264 |
+
event_handler: Optional[UserTag],
|
| 265 |
+
) -> None:
|
| 266 |
+
"""Consume a request supplied by the user."""
|
| 267 |
+
|
| 268 |
+
def consume_request_iterator(): # pylint: disable=too-many-branches
|
| 269 |
+
# Iterate over the request iterator until it is exhausted or an error
|
| 270 |
+
# condition is encountered.
|
| 271 |
+
while True:
|
| 272 |
+
return_from_user_request_generator_invoked = False
|
| 273 |
+
try:
|
| 274 |
+
# The thread may die in user-code. Do not block fork for this.
|
| 275 |
+
cygrpc.enter_user_request_generator()
|
| 276 |
+
request = next(request_iterator)
|
| 277 |
+
except StopIteration:
|
| 278 |
+
break
|
| 279 |
+
except Exception: # pylint: disable=broad-except
|
| 280 |
+
cygrpc.return_from_user_request_generator()
|
| 281 |
+
return_from_user_request_generator_invoked = True
|
| 282 |
+
code = grpc.StatusCode.UNKNOWN
|
| 283 |
+
details = "Exception iterating requests!"
|
| 284 |
+
_LOGGER.exception(details)
|
| 285 |
+
call.cancel(
|
| 286 |
+
_common.STATUS_CODE_TO_CYGRPC_STATUS_CODE[code], details
|
| 287 |
+
)
|
| 288 |
+
_abort(state, code, details)
|
| 289 |
+
return
|
| 290 |
+
finally:
|
| 291 |
+
if not return_from_user_request_generator_invoked:
|
| 292 |
+
cygrpc.return_from_user_request_generator()
|
| 293 |
+
serialized_request = _common.serialize(request, request_serializer)
|
| 294 |
+
with state.condition:
|
| 295 |
+
if state.code is None and not state.cancelled:
|
| 296 |
+
if serialized_request is None:
|
| 297 |
+
code = grpc.StatusCode.INTERNAL
|
| 298 |
+
details = "Exception serializing request!"
|
| 299 |
+
call.cancel(
|
| 300 |
+
_common.STATUS_CODE_TO_CYGRPC_STATUS_CODE[code],
|
| 301 |
+
details,
|
| 302 |
+
)
|
| 303 |
+
_abort(state, code, details)
|
| 304 |
+
return
|
| 305 |
+
else:
|
| 306 |
+
state.due.add(cygrpc.OperationType.send_message)
|
| 307 |
+
operations = (
|
| 308 |
+
cygrpc.SendMessageOperation(
|
| 309 |
+
serialized_request, _EMPTY_FLAGS
|
| 310 |
+
),
|
| 311 |
+
)
|
| 312 |
+
operating = call.operate(operations, event_handler)
|
| 313 |
+
if not operating:
|
| 314 |
+
state.due.remove(cygrpc.OperationType.send_message)
|
| 315 |
+
return
|
| 316 |
+
|
| 317 |
+
def _done():
|
| 318 |
+
return (
|
| 319 |
+
state.code is not None
|
| 320 |
+
or cygrpc.OperationType.send_message
|
| 321 |
+
not in state.due
|
| 322 |
+
)
|
| 323 |
+
|
| 324 |
+
_common.wait(
|
| 325 |
+
state.condition.wait,
|
| 326 |
+
_done,
|
| 327 |
+
spin_cb=functools.partial(
|
| 328 |
+
cygrpc.block_if_fork_in_progress, state
|
| 329 |
+
),
|
| 330 |
+
)
|
| 331 |
+
if state.code is not None:
|
| 332 |
+
return
|
| 333 |
+
else:
|
| 334 |
+
return
|
| 335 |
+
with state.condition:
|
| 336 |
+
if state.code is None:
|
| 337 |
+
state.due.add(cygrpc.OperationType.send_close_from_client)
|
| 338 |
+
operations = (
|
| 339 |
+
cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
|
| 340 |
+
)
|
| 341 |
+
operating = call.operate(operations, event_handler)
|
| 342 |
+
if not operating:
|
| 343 |
+
state.due.remove(
|
| 344 |
+
cygrpc.OperationType.send_close_from_client
|
| 345 |
+
)
|
| 346 |
+
|
| 347 |
+
consumption_thread = cygrpc.ForkManagedThread(
|
| 348 |
+
target=consume_request_iterator
|
| 349 |
+
)
|
| 350 |
+
consumption_thread.setDaemon(True)
|
| 351 |
+
consumption_thread.start()
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
def _rpc_state_string(class_name: str, rpc_state: _RPCState) -> str:
|
| 355 |
+
"""Calculates error string for RPC."""
|
| 356 |
+
with rpc_state.condition:
|
| 357 |
+
if rpc_state.code is None:
|
| 358 |
+
return "<{} object>".format(class_name)
|
| 359 |
+
elif rpc_state.code is grpc.StatusCode.OK:
|
| 360 |
+
return _OK_RENDEZVOUS_REPR_FORMAT.format(
|
| 361 |
+
class_name, rpc_state.code, rpc_state.details
|
| 362 |
+
)
|
| 363 |
+
else:
|
| 364 |
+
return _NON_OK_RENDEZVOUS_REPR_FORMAT.format(
|
| 365 |
+
class_name,
|
| 366 |
+
rpc_state.code,
|
| 367 |
+
rpc_state.details,
|
| 368 |
+
rpc_state.debug_error_string,
|
| 369 |
+
)
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
class _InactiveRpcError(grpc.RpcError, grpc.Call, grpc.Future):
|
| 373 |
+
"""An RPC error not tied to the execution of a particular RPC.
|
| 374 |
+
|
| 375 |
+
The RPC represented by the state object must not be in-progress or
|
| 376 |
+
cancelled.
|
| 377 |
+
|
| 378 |
+
Attributes:
|
| 379 |
+
_state: An instance of _RPCState.
|
| 380 |
+
"""
|
| 381 |
+
|
| 382 |
+
_state: _RPCState
|
| 383 |
+
|
| 384 |
+
def __init__(self, state: _RPCState):
|
| 385 |
+
with state.condition:
|
| 386 |
+
self._state = _RPCState(
|
| 387 |
+
(),
|
| 388 |
+
copy.deepcopy(state.initial_metadata),
|
| 389 |
+
copy.deepcopy(state.trailing_metadata),
|
| 390 |
+
state.code,
|
| 391 |
+
copy.deepcopy(state.details),
|
| 392 |
+
)
|
| 393 |
+
self._state.response = copy.copy(state.response)
|
| 394 |
+
self._state.debug_error_string = copy.copy(state.debug_error_string)
|
| 395 |
+
|
| 396 |
+
def initial_metadata(self) -> Optional[MetadataType]:
|
| 397 |
+
return self._state.initial_metadata
|
| 398 |
+
|
| 399 |
+
def trailing_metadata(self) -> Optional[MetadataType]:
|
| 400 |
+
return self._state.trailing_metadata
|
| 401 |
+
|
| 402 |
+
def code(self) -> Optional[grpc.StatusCode]:
|
| 403 |
+
return self._state.code
|
| 404 |
+
|
| 405 |
+
def details(self) -> Optional[str]:
|
| 406 |
+
return _common.decode(self._state.details)
|
| 407 |
+
|
| 408 |
+
def debug_error_string(self) -> Optional[str]:
|
| 409 |
+
return _common.decode(self._state.debug_error_string)
|
| 410 |
+
|
| 411 |
+
def _repr(self) -> str:
|
| 412 |
+
return _rpc_state_string(self.__class__.__name__, self._state)
|
| 413 |
+
|
| 414 |
+
def __repr__(self) -> str:
|
| 415 |
+
return self._repr()
|
| 416 |
+
|
| 417 |
+
def __str__(self) -> str:
|
| 418 |
+
return self._repr()
|
| 419 |
+
|
| 420 |
+
def cancel(self) -> bool:
|
| 421 |
+
"""See grpc.Future.cancel."""
|
| 422 |
+
return False
|
| 423 |
+
|
| 424 |
+
def cancelled(self) -> bool:
|
| 425 |
+
"""See grpc.Future.cancelled."""
|
| 426 |
+
return False
|
| 427 |
+
|
| 428 |
+
def running(self) -> bool:
|
| 429 |
+
"""See grpc.Future.running."""
|
| 430 |
+
return False
|
| 431 |
+
|
| 432 |
+
def done(self) -> bool:
|
| 433 |
+
"""See grpc.Future.done."""
|
| 434 |
+
return True
|
| 435 |
+
|
| 436 |
+
def result(
|
| 437 |
+
self, timeout: Optional[float] = None
|
| 438 |
+
) -> Any: # pylint: disable=unused-argument
|
| 439 |
+
"""See grpc.Future.result."""
|
| 440 |
+
raise self
|
| 441 |
+
|
| 442 |
+
def exception(
|
| 443 |
+
self, timeout: Optional[float] = None # pylint: disable=unused-argument
|
| 444 |
+
) -> Optional[Exception]:
|
| 445 |
+
"""See grpc.Future.exception."""
|
| 446 |
+
return self
|
| 447 |
+
|
| 448 |
+
def traceback(
|
| 449 |
+
self, timeout: Optional[float] = None # pylint: disable=unused-argument
|
| 450 |
+
) -> Optional[types.TracebackType]:
|
| 451 |
+
"""See grpc.Future.traceback."""
|
| 452 |
+
try:
|
| 453 |
+
raise self
|
| 454 |
+
except grpc.RpcError:
|
| 455 |
+
return sys.exc_info()[2]
|
| 456 |
+
|
| 457 |
+
def add_done_callback(
|
| 458 |
+
self,
|
| 459 |
+
fn: Callable[[grpc.Future], None],
|
| 460 |
+
timeout: Optional[float] = None, # pylint: disable=unused-argument
|
| 461 |
+
) -> None:
|
| 462 |
+
"""See grpc.Future.add_done_callback."""
|
| 463 |
+
fn(self)
|
| 464 |
+
|
| 465 |
+
|
| 466 |
+
class _Rendezvous(grpc.RpcError, grpc.RpcContext):
|
| 467 |
+
"""An RPC iterator.
|
| 468 |
+
|
| 469 |
+
Attributes:
|
| 470 |
+
_state: An instance of _RPCState.
|
| 471 |
+
_call: An instance of SegregatedCall or IntegratedCall.
|
| 472 |
+
In either case, the _call object is expected to have operate, cancel,
|
| 473 |
+
and next_event methods.
|
| 474 |
+
_response_deserializer: A callable taking bytes and return a Python
|
| 475 |
+
object.
|
| 476 |
+
_deadline: A float representing the deadline of the RPC in seconds. Or
|
| 477 |
+
possibly None, to represent an RPC with no deadline at all.
|
| 478 |
+
"""
|
| 479 |
+
|
| 480 |
+
_state: _RPCState
|
| 481 |
+
_call: Union[cygrpc.SegregatedCall, cygrpc.IntegratedCall]
|
| 482 |
+
_response_deserializer: Optional[DeserializingFunction]
|
| 483 |
+
_deadline: Optional[float]
|
| 484 |
+
|
| 485 |
+
def __init__(
|
| 486 |
+
self,
|
| 487 |
+
state: _RPCState,
|
| 488 |
+
call: Union[cygrpc.SegregatedCall, cygrpc.IntegratedCall],
|
| 489 |
+
response_deserializer: Optional[DeserializingFunction],
|
| 490 |
+
deadline: Optional[float],
|
| 491 |
+
):
|
| 492 |
+
super(_Rendezvous, self).__init__()
|
| 493 |
+
self._state = state
|
| 494 |
+
self._call = call
|
| 495 |
+
self._response_deserializer = response_deserializer
|
| 496 |
+
self._deadline = deadline
|
| 497 |
+
|
| 498 |
+
def is_active(self) -> bool:
|
| 499 |
+
"""See grpc.RpcContext.is_active"""
|
| 500 |
+
with self._state.condition:
|
| 501 |
+
return self._state.code is None
|
| 502 |
+
|
| 503 |
+
def time_remaining(self) -> Optional[float]:
|
| 504 |
+
"""See grpc.RpcContext.time_remaining"""
|
| 505 |
+
with self._state.condition:
|
| 506 |
+
if self._deadline is None:
|
| 507 |
+
return None
|
| 508 |
+
else:
|
| 509 |
+
return max(self._deadline - time.time(), 0)
|
| 510 |
+
|
| 511 |
+
def cancel(self) -> bool:
|
| 512 |
+
"""See grpc.RpcContext.cancel"""
|
| 513 |
+
with self._state.condition:
|
| 514 |
+
if self._state.code is None:
|
| 515 |
+
code = grpc.StatusCode.CANCELLED
|
| 516 |
+
details = "Locally cancelled by application!"
|
| 517 |
+
self._call.cancel(
|
| 518 |
+
_common.STATUS_CODE_TO_CYGRPC_STATUS_CODE[code], details
|
| 519 |
+
)
|
| 520 |
+
self._state.cancelled = True
|
| 521 |
+
_abort(self._state, code, details)
|
| 522 |
+
self._state.condition.notify_all()
|
| 523 |
+
return True
|
| 524 |
+
else:
|
| 525 |
+
return False
|
| 526 |
+
|
| 527 |
+
def add_callback(self, callback: NullaryCallbackType) -> bool:
|
| 528 |
+
"""See grpc.RpcContext.add_callback"""
|
| 529 |
+
with self._state.condition:
|
| 530 |
+
if self._state.callbacks is None:
|
| 531 |
+
return False
|
| 532 |
+
else:
|
| 533 |
+
self._state.callbacks.append(callback)
|
| 534 |
+
return True
|
| 535 |
+
|
| 536 |
+
def __iter__(self):
|
| 537 |
+
return self
|
| 538 |
+
|
| 539 |
+
def next(self):
|
| 540 |
+
return self._next()
|
| 541 |
+
|
| 542 |
+
def __next__(self):
|
| 543 |
+
return self._next()
|
| 544 |
+
|
| 545 |
+
def _next(self):
|
| 546 |
+
raise NotImplementedError()
|
| 547 |
+
|
| 548 |
+
def debug_error_string(self) -> Optional[str]:
|
| 549 |
+
raise NotImplementedError()
|
| 550 |
+
|
| 551 |
+
def _repr(self) -> str:
|
| 552 |
+
return _rpc_state_string(self.__class__.__name__, self._state)
|
| 553 |
+
|
| 554 |
+
def __repr__(self) -> str:
|
| 555 |
+
return self._repr()
|
| 556 |
+
|
| 557 |
+
def __str__(self) -> str:
|
| 558 |
+
return self._repr()
|
| 559 |
+
|
| 560 |
+
def __del__(self) -> None:
|
| 561 |
+
with self._state.condition:
|
| 562 |
+
if self._state.code is None:
|
| 563 |
+
self._state.code = grpc.StatusCode.CANCELLED
|
| 564 |
+
self._state.details = "Cancelled upon garbage collection!"
|
| 565 |
+
self._state.cancelled = True
|
| 566 |
+
self._call.cancel(
|
| 567 |
+
_common.STATUS_CODE_TO_CYGRPC_STATUS_CODE[self._state.code],
|
| 568 |
+
self._state.details,
|
| 569 |
+
)
|
| 570 |
+
self._state.condition.notify_all()
|
| 571 |
+
|
| 572 |
+
|
| 573 |
+
class _SingleThreadedRendezvous(
|
| 574 |
+
_Rendezvous, grpc.Call, grpc.Future
|
| 575 |
+
): # pylint: disable=too-many-ancestors
|
| 576 |
+
"""An RPC iterator operating entirely on a single thread.
|
| 577 |
+
|
| 578 |
+
The __next__ method of _SingleThreadedRendezvous does not depend on the
|
| 579 |
+
existence of any other thread, including the "channel spin thread".
|
| 580 |
+
However, this means that its interface is entirely synchronous. So this
|
| 581 |
+
class cannot completely fulfill the grpc.Future interface. The result,
|
| 582 |
+
exception, and traceback methods will never block and will instead raise
|
| 583 |
+
an exception if calling the method would result in blocking.
|
| 584 |
+
|
| 585 |
+
This means that these methods are safe to call from add_done_callback
|
| 586 |
+
handlers.
|
| 587 |
+
"""
|
| 588 |
+
|
| 589 |
+
_state: _RPCState
|
| 590 |
+
|
| 591 |
+
def _is_complete(self) -> bool:
|
| 592 |
+
return self._state.code is not None
|
| 593 |
+
|
| 594 |
+
def cancelled(self) -> bool:
|
| 595 |
+
with self._state.condition:
|
| 596 |
+
return self._state.cancelled
|
| 597 |
+
|
| 598 |
+
def running(self) -> bool:
|
| 599 |
+
with self._state.condition:
|
| 600 |
+
return self._state.code is None
|
| 601 |
+
|
| 602 |
+
def done(self) -> bool:
|
| 603 |
+
with self._state.condition:
|
| 604 |
+
return self._state.code is not None
|
| 605 |
+
|
| 606 |
+
def result(self, timeout: Optional[float] = None) -> Any:
|
| 607 |
+
"""Returns the result of the computation or raises its exception.
|
| 608 |
+
|
| 609 |
+
This method will never block. Instead, it will raise an exception
|
| 610 |
+
if calling this method would otherwise result in blocking.
|
| 611 |
+
|
| 612 |
+
Since this method will never block, any `timeout` argument passed will
|
| 613 |
+
be ignored.
|
| 614 |
+
"""
|
| 615 |
+
del timeout
|
| 616 |
+
with self._state.condition:
|
| 617 |
+
if not self._is_complete():
|
| 618 |
+
raise grpc.experimental.UsageError(
|
| 619 |
+
"_SingleThreadedRendezvous only supports result() when the"
|
| 620 |
+
" RPC is complete."
|
| 621 |
+
)
|
| 622 |
+
if self._state.code is grpc.StatusCode.OK:
|
| 623 |
+
return self._state.response
|
| 624 |
+
elif self._state.cancelled:
|
| 625 |
+
raise grpc.FutureCancelledError()
|
| 626 |
+
else:
|
| 627 |
+
raise self
|
| 628 |
+
|
| 629 |
+
def exception(self, timeout: Optional[float] = None) -> Optional[Exception]:
|
| 630 |
+
"""Return the exception raised by the computation.
|
| 631 |
+
|
| 632 |
+
This method will never block. Instead, it will raise an exception
|
| 633 |
+
if calling this method would otherwise result in blocking.
|
| 634 |
+
|
| 635 |
+
Since this method will never block, any `timeout` argument passed will
|
| 636 |
+
be ignored.
|
| 637 |
+
"""
|
| 638 |
+
del timeout
|
| 639 |
+
with self._state.condition:
|
| 640 |
+
if not self._is_complete():
|
| 641 |
+
raise grpc.experimental.UsageError(
|
| 642 |
+
"_SingleThreadedRendezvous only supports exception() when"
|
| 643 |
+
" the RPC is complete."
|
| 644 |
+
)
|
| 645 |
+
if self._state.code is grpc.StatusCode.OK:
|
| 646 |
+
return None
|
| 647 |
+
elif self._state.cancelled:
|
| 648 |
+
raise grpc.FutureCancelledError()
|
| 649 |
+
else:
|
| 650 |
+
return self
|
| 651 |
+
|
| 652 |
+
def traceback(
|
| 653 |
+
self, timeout: Optional[float] = None
|
| 654 |
+
) -> Optional[types.TracebackType]:
|
| 655 |
+
"""Access the traceback of the exception raised by the computation.
|
| 656 |
+
|
| 657 |
+
This method will never block. Instead, it will raise an exception
|
| 658 |
+
if calling this method would otherwise result in blocking.
|
| 659 |
+
|
| 660 |
+
Since this method will never block, any `timeout` argument passed will
|
| 661 |
+
be ignored.
|
| 662 |
+
"""
|
| 663 |
+
del timeout
|
| 664 |
+
with self._state.condition:
|
| 665 |
+
if not self._is_complete():
|
| 666 |
+
raise grpc.experimental.UsageError(
|
| 667 |
+
"_SingleThreadedRendezvous only supports traceback() when"
|
| 668 |
+
" the RPC is complete."
|
| 669 |
+
)
|
| 670 |
+
if self._state.code is grpc.StatusCode.OK:
|
| 671 |
+
return None
|
| 672 |
+
elif self._state.cancelled:
|
| 673 |
+
raise grpc.FutureCancelledError()
|
| 674 |
+
else:
|
| 675 |
+
try:
|
| 676 |
+
raise self
|
| 677 |
+
except grpc.RpcError:
|
| 678 |
+
return sys.exc_info()[2]
|
| 679 |
+
|
| 680 |
+
def add_done_callback(self, fn: Callable[[grpc.Future], None]) -> None:
|
| 681 |
+
with self._state.condition:
|
| 682 |
+
if self._state.code is None:
|
| 683 |
+
self._state.callbacks.append(functools.partial(fn, self))
|
| 684 |
+
return
|
| 685 |
+
|
| 686 |
+
fn(self)
|
| 687 |
+
|
| 688 |
+
def initial_metadata(self) -> Optional[MetadataType]:
|
| 689 |
+
"""See grpc.Call.initial_metadata"""
|
| 690 |
+
with self._state.condition:
|
| 691 |
+
# NOTE(gnossen): Based on our initial call batch, we are guaranteed
|
| 692 |
+
# to receive initial metadata before any messages.
|
| 693 |
+
while self._state.initial_metadata is None:
|
| 694 |
+
self._consume_next_event()
|
| 695 |
+
return self._state.initial_metadata
|
| 696 |
+
|
| 697 |
+
def trailing_metadata(self) -> Optional[MetadataType]:
|
| 698 |
+
"""See grpc.Call.trailing_metadata"""
|
| 699 |
+
with self._state.condition:
|
| 700 |
+
if self._state.trailing_metadata is None:
|
| 701 |
+
raise grpc.experimental.UsageError(
|
| 702 |
+
"Cannot get trailing metadata until RPC is completed."
|
| 703 |
+
)
|
| 704 |
+
return self._state.trailing_metadata
|
| 705 |
+
|
| 706 |
+
def code(self) -> Optional[grpc.StatusCode]:
|
| 707 |
+
"""See grpc.Call.code"""
|
| 708 |
+
with self._state.condition:
|
| 709 |
+
if self._state.code is None:
|
| 710 |
+
raise grpc.experimental.UsageError(
|
| 711 |
+
"Cannot get code until RPC is completed."
|
| 712 |
+
)
|
| 713 |
+
return self._state.code
|
| 714 |
+
|
| 715 |
+
def details(self) -> Optional[str]:
|
| 716 |
+
"""See grpc.Call.details"""
|
| 717 |
+
with self._state.condition:
|
| 718 |
+
if self._state.details is None:
|
| 719 |
+
raise grpc.experimental.UsageError(
|
| 720 |
+
"Cannot get details until RPC is completed."
|
| 721 |
+
)
|
| 722 |
+
return _common.decode(self._state.details)
|
| 723 |
+
|
| 724 |
+
def _consume_next_event(self) -> Optional[cygrpc.BaseEvent]:
|
| 725 |
+
event = self._call.next_event()
|
| 726 |
+
with self._state.condition:
|
| 727 |
+
callbacks = _handle_event(
|
| 728 |
+
event, self._state, self._response_deserializer
|
| 729 |
+
)
|
| 730 |
+
for callback in callbacks:
|
| 731 |
+
# NOTE(gnossen): We intentionally allow exceptions to bubble up
|
| 732 |
+
# to the user when running on a single thread.
|
| 733 |
+
callback()
|
| 734 |
+
return event
|
| 735 |
+
|
| 736 |
+
def _next_response(self) -> Any:
|
| 737 |
+
while True:
|
| 738 |
+
self._consume_next_event()
|
| 739 |
+
with self._state.condition:
|
| 740 |
+
if self._state.response is not None:
|
| 741 |
+
response = self._state.response
|
| 742 |
+
self._state.response = None
|
| 743 |
+
return response
|
| 744 |
+
elif (
|
| 745 |
+
cygrpc.OperationType.receive_message not in self._state.due
|
| 746 |
+
):
|
| 747 |
+
if self._state.code is grpc.StatusCode.OK:
|
| 748 |
+
raise StopIteration()
|
| 749 |
+
elif self._state.code is not None:
|
| 750 |
+
raise self
|
| 751 |
+
|
| 752 |
+
def _next(self) -> Any:
|
| 753 |
+
with self._state.condition:
|
| 754 |
+
if self._state.code is None:
|
| 755 |
+
# We tentatively add the operation as expected and remove
|
| 756 |
+
# it if the enqueue operation fails. This allows us to guarantee that
|
| 757 |
+
# if an event has been submitted to the core completion queue,
|
| 758 |
+
# it is in `due`. If we waited until after a successful
|
| 759 |
+
# enqueue operation then a signal could interrupt this
|
| 760 |
+
# thread between the enqueue operation and the addition of the
|
| 761 |
+
# operation to `due`. This would cause an exception on the
|
| 762 |
+
# channel spin thread when the operation completes and no
|
| 763 |
+
# corresponding operation would be present in state.due.
|
| 764 |
+
# Note that, since `condition` is held through this block, there is
|
| 765 |
+
# no data race on `due`.
|
| 766 |
+
self._state.due.add(cygrpc.OperationType.receive_message)
|
| 767 |
+
operating = self._call.operate(
|
| 768 |
+
(cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),), None
|
| 769 |
+
)
|
| 770 |
+
if not operating:
|
| 771 |
+
self._state.due.remove(cygrpc.OperationType.receive_message)
|
| 772 |
+
elif self._state.code is grpc.StatusCode.OK:
|
| 773 |
+
raise StopIteration()
|
| 774 |
+
else:
|
| 775 |
+
raise self
|
| 776 |
+
return self._next_response()
|
| 777 |
+
|
| 778 |
+
def debug_error_string(self) -> Optional[str]:
|
| 779 |
+
with self._state.condition:
|
| 780 |
+
if self._state.debug_error_string is None:
|
| 781 |
+
raise grpc.experimental.UsageError(
|
| 782 |
+
"Cannot get debug error string until RPC is completed."
|
| 783 |
+
)
|
| 784 |
+
return _common.decode(self._state.debug_error_string)
|
| 785 |
+
|
| 786 |
+
|
| 787 |
+
class _MultiThreadedRendezvous(
|
| 788 |
+
_Rendezvous, grpc.Call, grpc.Future
|
| 789 |
+
): # pylint: disable=too-many-ancestors
|
| 790 |
+
"""An RPC iterator that depends on a channel spin thread.
|
| 791 |
+
|
| 792 |
+
This iterator relies upon a per-channel thread running in the background,
|
| 793 |
+
dequeueing events from the completion queue, and notifying threads waiting
|
| 794 |
+
on the threading.Condition object in the _RPCState object.
|
| 795 |
+
|
| 796 |
+
This extra thread allows _MultiThreadedRendezvous to fulfill the grpc.Future interface
|
| 797 |
+
and to mediate a bidirection streaming RPC.
|
| 798 |
+
"""
|
| 799 |
+
|
| 800 |
+
_state: _RPCState
|
| 801 |
+
|
| 802 |
+
def initial_metadata(self) -> Optional[MetadataType]:
|
| 803 |
+
"""See grpc.Call.initial_metadata"""
|
| 804 |
+
with self._state.condition:
|
| 805 |
+
|
| 806 |
+
def _done():
|
| 807 |
+
return self._state.initial_metadata is not None
|
| 808 |
+
|
| 809 |
+
_common.wait(self._state.condition.wait, _done)
|
| 810 |
+
return self._state.initial_metadata
|
| 811 |
+
|
| 812 |
+
def trailing_metadata(self) -> Optional[MetadataType]:
|
| 813 |
+
"""See grpc.Call.trailing_metadata"""
|
| 814 |
+
with self._state.condition:
|
| 815 |
+
|
| 816 |
+
def _done():
|
| 817 |
+
return self._state.trailing_metadata is not None
|
| 818 |
+
|
| 819 |
+
_common.wait(self._state.condition.wait, _done)
|
| 820 |
+
return self._state.trailing_metadata
|
| 821 |
+
|
| 822 |
+
def code(self) -> Optional[grpc.StatusCode]:
|
| 823 |
+
"""See grpc.Call.code"""
|
| 824 |
+
with self._state.condition:
|
| 825 |
+
|
| 826 |
+
def _done():
|
| 827 |
+
return self._state.code is not None
|
| 828 |
+
|
| 829 |
+
_common.wait(self._state.condition.wait, _done)
|
| 830 |
+
return self._state.code
|
| 831 |
+
|
| 832 |
+
def details(self) -> Optional[str]:
|
| 833 |
+
"""See grpc.Call.details"""
|
| 834 |
+
with self._state.condition:
|
| 835 |
+
|
| 836 |
+
def _done():
|
| 837 |
+
return self._state.details is not None
|
| 838 |
+
|
| 839 |
+
_common.wait(self._state.condition.wait, _done)
|
| 840 |
+
return _common.decode(self._state.details)
|
| 841 |
+
|
| 842 |
+
def debug_error_string(self) -> Optional[str]:
|
| 843 |
+
with self._state.condition:
|
| 844 |
+
|
| 845 |
+
def _done():
|
| 846 |
+
return self._state.debug_error_string is not None
|
| 847 |
+
|
| 848 |
+
_common.wait(self._state.condition.wait, _done)
|
| 849 |
+
return _common.decode(self._state.debug_error_string)
|
| 850 |
+
|
| 851 |
+
def cancelled(self) -> bool:
|
| 852 |
+
with self._state.condition:
|
| 853 |
+
return self._state.cancelled
|
| 854 |
+
|
| 855 |
+
def running(self) -> bool:
|
| 856 |
+
with self._state.condition:
|
| 857 |
+
return self._state.code is None
|
| 858 |
+
|
| 859 |
+
def done(self) -> bool:
|
| 860 |
+
with self._state.condition:
|
| 861 |
+
return self._state.code is not None
|
| 862 |
+
|
| 863 |
+
def _is_complete(self) -> bool:
|
| 864 |
+
return self._state.code is not None
|
| 865 |
+
|
| 866 |
+
def result(self, timeout: Optional[float] = None) -> Any:
|
| 867 |
+
"""Returns the result of the computation or raises its exception.
|
| 868 |
+
|
| 869 |
+
See grpc.Future.result for the full API contract.
|
| 870 |
+
"""
|
| 871 |
+
with self._state.condition:
|
| 872 |
+
timed_out = _common.wait(
|
| 873 |
+
self._state.condition.wait, self._is_complete, timeout=timeout
|
| 874 |
+
)
|
| 875 |
+
if timed_out:
|
| 876 |
+
raise grpc.FutureTimeoutError()
|
| 877 |
+
else:
|
| 878 |
+
if self._state.code is grpc.StatusCode.OK:
|
| 879 |
+
return self._state.response
|
| 880 |
+
elif self._state.cancelled:
|
| 881 |
+
raise grpc.FutureCancelledError()
|
| 882 |
+
else:
|
| 883 |
+
raise self
|
| 884 |
+
|
| 885 |
+
def exception(self, timeout: Optional[float] = None) -> Optional[Exception]:
|
| 886 |
+
"""Return the exception raised by the computation.
|
| 887 |
+
|
| 888 |
+
See grpc.Future.exception for the full API contract.
|
| 889 |
+
"""
|
| 890 |
+
with self._state.condition:
|
| 891 |
+
timed_out = _common.wait(
|
| 892 |
+
self._state.condition.wait, self._is_complete, timeout=timeout
|
| 893 |
+
)
|
| 894 |
+
if timed_out:
|
| 895 |
+
raise grpc.FutureTimeoutError()
|
| 896 |
+
else:
|
| 897 |
+
if self._state.code is grpc.StatusCode.OK:
|
| 898 |
+
return None
|
| 899 |
+
elif self._state.cancelled:
|
| 900 |
+
raise grpc.FutureCancelledError()
|
| 901 |
+
else:
|
| 902 |
+
return self
|
| 903 |
+
|
| 904 |
+
def traceback(
|
| 905 |
+
self, timeout: Optional[float] = None
|
| 906 |
+
) -> Optional[types.TracebackType]:
|
| 907 |
+
"""Access the traceback of the exception raised by the computation.
|
| 908 |
+
|
| 909 |
+
See grpc.future.traceback for the full API contract.
|
| 910 |
+
"""
|
| 911 |
+
with self._state.condition:
|
| 912 |
+
timed_out = _common.wait(
|
| 913 |
+
self._state.condition.wait, self._is_complete, timeout=timeout
|
| 914 |
+
)
|
| 915 |
+
if timed_out:
|
| 916 |
+
raise grpc.FutureTimeoutError()
|
| 917 |
+
else:
|
| 918 |
+
if self._state.code is grpc.StatusCode.OK:
|
| 919 |
+
return None
|
| 920 |
+
elif self._state.cancelled:
|
| 921 |
+
raise grpc.FutureCancelledError()
|
| 922 |
+
else:
|
| 923 |
+
try:
|
| 924 |
+
raise self
|
| 925 |
+
except grpc.RpcError:
|
| 926 |
+
return sys.exc_info()[2]
|
| 927 |
+
|
| 928 |
+
def add_done_callback(self, fn: Callable[[grpc.Future], None]) -> None:
|
| 929 |
+
with self._state.condition:
|
| 930 |
+
if self._state.code is None:
|
| 931 |
+
self._state.callbacks.append(functools.partial(fn, self))
|
| 932 |
+
return
|
| 933 |
+
|
| 934 |
+
fn(self)
|
| 935 |
+
|
| 936 |
+
def _next(self) -> Any:
|
| 937 |
+
with self._state.condition:
|
| 938 |
+
if self._state.code is None:
|
| 939 |
+
event_handler = _event_handler(
|
| 940 |
+
self._state, self._response_deserializer
|
| 941 |
+
)
|
| 942 |
+
self._state.due.add(cygrpc.OperationType.receive_message)
|
| 943 |
+
operating = self._call.operate(
|
| 944 |
+
(cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),),
|
| 945 |
+
event_handler,
|
| 946 |
+
)
|
| 947 |
+
if not operating:
|
| 948 |
+
self._state.due.remove(cygrpc.OperationType.receive_message)
|
| 949 |
+
elif self._state.code is grpc.StatusCode.OK:
|
| 950 |
+
raise StopIteration()
|
| 951 |
+
else:
|
| 952 |
+
raise self
|
| 953 |
+
|
| 954 |
+
def _response_ready():
|
| 955 |
+
return self._state.response is not None or (
|
| 956 |
+
cygrpc.OperationType.receive_message not in self._state.due
|
| 957 |
+
and self._state.code is not None
|
| 958 |
+
)
|
| 959 |
+
|
| 960 |
+
_common.wait(self._state.condition.wait, _response_ready)
|
| 961 |
+
if self._state.response is not None:
|
| 962 |
+
response = self._state.response
|
| 963 |
+
self._state.response = None
|
| 964 |
+
return response
|
| 965 |
+
elif cygrpc.OperationType.receive_message not in self._state.due:
|
| 966 |
+
if self._state.code is grpc.StatusCode.OK:
|
| 967 |
+
raise StopIteration()
|
| 968 |
+
elif self._state.code is not None:
|
| 969 |
+
raise self
|
| 970 |
+
|
| 971 |
+
|
| 972 |
+
def _start_unary_request(
|
| 973 |
+
request: Any,
|
| 974 |
+
timeout: Optional[float],
|
| 975 |
+
request_serializer: SerializingFunction,
|
| 976 |
+
) -> Tuple[Optional[float], Optional[bytes], Optional[grpc.RpcError]]:
|
| 977 |
+
deadline = _deadline(timeout)
|
| 978 |
+
serialized_request = _common.serialize(request, request_serializer)
|
| 979 |
+
if serialized_request is None:
|
| 980 |
+
state = _RPCState(
|
| 981 |
+
(),
|
| 982 |
+
(),
|
| 983 |
+
(),
|
| 984 |
+
grpc.StatusCode.INTERNAL,
|
| 985 |
+
"Exception serializing request!",
|
| 986 |
+
)
|
| 987 |
+
error = _InactiveRpcError(state)
|
| 988 |
+
return deadline, None, error
|
| 989 |
+
else:
|
| 990 |
+
return deadline, serialized_request, None
|
| 991 |
+
|
| 992 |
+
|
| 993 |
+
def _end_unary_response_blocking(
|
| 994 |
+
state: _RPCState,
|
| 995 |
+
call: cygrpc.SegregatedCall,
|
| 996 |
+
with_call: bool,
|
| 997 |
+
deadline: Optional[float],
|
| 998 |
+
) -> Union[ResponseType, Tuple[ResponseType, grpc.Call]]:
|
| 999 |
+
if state.code is grpc.StatusCode.OK:
|
| 1000 |
+
if with_call:
|
| 1001 |
+
rendezvous = _MultiThreadedRendezvous(state, call, None, deadline)
|
| 1002 |
+
return state.response, rendezvous
|
| 1003 |
+
else:
|
| 1004 |
+
return state.response
|
| 1005 |
+
else:
|
| 1006 |
+
raise _InactiveRpcError(state) # pytype: disable=not-instantiable
|
| 1007 |
+
|
| 1008 |
+
|
| 1009 |
+
def _stream_unary_invocation_operations(
|
| 1010 |
+
metadata: Optional[MetadataType], initial_metadata_flags: int
|
| 1011 |
+
) -> Sequence[Sequence[cygrpc.Operation]]:
|
| 1012 |
+
return (
|
| 1013 |
+
(
|
| 1014 |
+
cygrpc.SendInitialMetadataOperation(
|
| 1015 |
+
metadata, initial_metadata_flags
|
| 1016 |
+
),
|
| 1017 |
+
cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),
|
| 1018 |
+
cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
|
| 1019 |
+
),
|
| 1020 |
+
(cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
|
| 1021 |
+
)
|
| 1022 |
+
|
| 1023 |
+
|
| 1024 |
+
def _stream_unary_invocation_operations_and_tags(
|
| 1025 |
+
metadata: Optional[MetadataType], initial_metadata_flags: int
|
| 1026 |
+
) -> Sequence[Tuple[Sequence[cygrpc.Operation], Optional[UserTag]]]:
|
| 1027 |
+
return tuple(
|
| 1028 |
+
(
|
| 1029 |
+
operations,
|
| 1030 |
+
None,
|
| 1031 |
+
)
|
| 1032 |
+
for operations in _stream_unary_invocation_operations(
|
| 1033 |
+
metadata, initial_metadata_flags
|
| 1034 |
+
)
|
| 1035 |
+
)
|
| 1036 |
+
|
| 1037 |
+
|
| 1038 |
+
def _determine_deadline(user_deadline: Optional[float]) -> Optional[float]:
|
| 1039 |
+
parent_deadline = cygrpc.get_deadline_from_context()
|
| 1040 |
+
if parent_deadline is None and user_deadline is None:
|
| 1041 |
+
return None
|
| 1042 |
+
elif parent_deadline is not None and user_deadline is None:
|
| 1043 |
+
return parent_deadline
|
| 1044 |
+
elif user_deadline is not None and parent_deadline is None:
|
| 1045 |
+
return user_deadline
|
| 1046 |
+
else:
|
| 1047 |
+
return min(parent_deadline, user_deadline)
|
| 1048 |
+
|
| 1049 |
+
|
| 1050 |
+
class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
|
| 1051 |
+
_channel: cygrpc.Channel
|
| 1052 |
+
_managed_call: IntegratedCallFactory
|
| 1053 |
+
_method: bytes
|
| 1054 |
+
_target: bytes
|
| 1055 |
+
_request_serializer: Optional[SerializingFunction]
|
| 1056 |
+
_response_deserializer: Optional[DeserializingFunction]
|
| 1057 |
+
_context: Any
|
| 1058 |
+
_registered_call_handle: Optional[int]
|
| 1059 |
+
|
| 1060 |
+
__slots__ = [
|
| 1061 |
+
"_channel",
|
| 1062 |
+
"_managed_call",
|
| 1063 |
+
"_method",
|
| 1064 |
+
"_target",
|
| 1065 |
+
"_request_serializer",
|
| 1066 |
+
"_response_deserializer",
|
| 1067 |
+
"_context",
|
| 1068 |
+
]
|
| 1069 |
+
|
| 1070 |
+
# pylint: disable=too-many-arguments
|
| 1071 |
+
def __init__(
|
| 1072 |
+
self,
|
| 1073 |
+
channel: cygrpc.Channel,
|
| 1074 |
+
managed_call: IntegratedCallFactory,
|
| 1075 |
+
method: bytes,
|
| 1076 |
+
target: bytes,
|
| 1077 |
+
request_serializer: Optional[SerializingFunction],
|
| 1078 |
+
response_deserializer: Optional[DeserializingFunction],
|
| 1079 |
+
_registered_call_handle: Optional[int],
|
| 1080 |
+
):
|
| 1081 |
+
self._channel = channel
|
| 1082 |
+
self._managed_call = managed_call
|
| 1083 |
+
self._method = method
|
| 1084 |
+
self._target = target
|
| 1085 |
+
self._request_serializer = request_serializer
|
| 1086 |
+
self._response_deserializer = response_deserializer
|
| 1087 |
+
self._context = cygrpc.build_census_context()
|
| 1088 |
+
self._registered_call_handle = _registered_call_handle
|
| 1089 |
+
|
| 1090 |
+
def _prepare(
|
| 1091 |
+
self,
|
| 1092 |
+
request: Any,
|
| 1093 |
+
timeout: Optional[float],
|
| 1094 |
+
metadata: Optional[MetadataType],
|
| 1095 |
+
wait_for_ready: Optional[bool],
|
| 1096 |
+
compression: Optional[grpc.Compression],
|
| 1097 |
+
) -> Tuple[
|
| 1098 |
+
Optional[_RPCState],
|
| 1099 |
+
Optional[Sequence[cygrpc.Operation]],
|
| 1100 |
+
Optional[float],
|
| 1101 |
+
Optional[grpc.RpcError],
|
| 1102 |
+
]:
|
| 1103 |
+
deadline, serialized_request, rendezvous = _start_unary_request(
|
| 1104 |
+
request, timeout, self._request_serializer
|
| 1105 |
+
)
|
| 1106 |
+
initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
|
| 1107 |
+
wait_for_ready
|
| 1108 |
+
)
|
| 1109 |
+
augmented_metadata = _compression.augment_metadata(
|
| 1110 |
+
metadata, compression
|
| 1111 |
+
)
|
| 1112 |
+
if serialized_request is None:
|
| 1113 |
+
return None, None, None, rendezvous
|
| 1114 |
+
else:
|
| 1115 |
+
state = _RPCState(_UNARY_UNARY_INITIAL_DUE, None, None, None, None)
|
| 1116 |
+
operations = (
|
| 1117 |
+
cygrpc.SendInitialMetadataOperation(
|
| 1118 |
+
augmented_metadata, initial_metadata_flags
|
| 1119 |
+
),
|
| 1120 |
+
cygrpc.SendMessageOperation(serialized_request, _EMPTY_FLAGS),
|
| 1121 |
+
cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
|
| 1122 |
+
cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),
|
| 1123 |
+
cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),
|
| 1124 |
+
cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
|
| 1125 |
+
)
|
| 1126 |
+
return state, operations, deadline, None
|
| 1127 |
+
|
| 1128 |
+
def _blocking(
|
| 1129 |
+
self,
|
| 1130 |
+
request: Any,
|
| 1131 |
+
timeout: Optional[float] = None,
|
| 1132 |
+
metadata: Optional[MetadataType] = None,
|
| 1133 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 1134 |
+
wait_for_ready: Optional[bool] = None,
|
| 1135 |
+
compression: Optional[grpc.Compression] = None,
|
| 1136 |
+
) -> Tuple[_RPCState, cygrpc.SegregatedCall]:
|
| 1137 |
+
state, operations, deadline, rendezvous = self._prepare(
|
| 1138 |
+
request, timeout, metadata, wait_for_ready, compression
|
| 1139 |
+
)
|
| 1140 |
+
if state is None:
|
| 1141 |
+
raise rendezvous # pylint: disable-msg=raising-bad-type
|
| 1142 |
+
else:
|
| 1143 |
+
state.rpc_start_time = time.perf_counter()
|
| 1144 |
+
state.method = _common.decode(self._method)
|
| 1145 |
+
state.target = _common.decode(self._target)
|
| 1146 |
+
call = self._channel.segregated_call(
|
| 1147 |
+
cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
|
| 1148 |
+
self._method,
|
| 1149 |
+
None,
|
| 1150 |
+
_determine_deadline(deadline),
|
| 1151 |
+
metadata,
|
| 1152 |
+
None if credentials is None else credentials._credentials,
|
| 1153 |
+
(
|
| 1154 |
+
(
|
| 1155 |
+
operations,
|
| 1156 |
+
None,
|
| 1157 |
+
),
|
| 1158 |
+
),
|
| 1159 |
+
self._context,
|
| 1160 |
+
self._registered_call_handle,
|
| 1161 |
+
)
|
| 1162 |
+
event = call.next_event()
|
| 1163 |
+
_handle_event(event, state, self._response_deserializer)
|
| 1164 |
+
return state, call
|
| 1165 |
+
|
| 1166 |
+
def __call__(
|
| 1167 |
+
self,
|
| 1168 |
+
request: Any,
|
| 1169 |
+
timeout: Optional[float] = None,
|
| 1170 |
+
metadata: Optional[MetadataType] = None,
|
| 1171 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 1172 |
+
wait_for_ready: Optional[bool] = None,
|
| 1173 |
+
compression: Optional[grpc.Compression] = None,
|
| 1174 |
+
) -> Any:
|
| 1175 |
+
(
|
| 1176 |
+
state,
|
| 1177 |
+
call,
|
| 1178 |
+
) = self._blocking(
|
| 1179 |
+
request, timeout, metadata, credentials, wait_for_ready, compression
|
| 1180 |
+
)
|
| 1181 |
+
return _end_unary_response_blocking(state, call, False, None)
|
| 1182 |
+
|
| 1183 |
+
def with_call(
|
| 1184 |
+
self,
|
| 1185 |
+
request: Any,
|
| 1186 |
+
timeout: Optional[float] = None,
|
| 1187 |
+
metadata: Optional[MetadataType] = None,
|
| 1188 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 1189 |
+
wait_for_ready: Optional[bool] = None,
|
| 1190 |
+
compression: Optional[grpc.Compression] = None,
|
| 1191 |
+
) -> Tuple[Any, grpc.Call]:
|
| 1192 |
+
(
|
| 1193 |
+
state,
|
| 1194 |
+
call,
|
| 1195 |
+
) = self._blocking(
|
| 1196 |
+
request, timeout, metadata, credentials, wait_for_ready, compression
|
| 1197 |
+
)
|
| 1198 |
+
return _end_unary_response_blocking(state, call, True, None)
|
| 1199 |
+
|
| 1200 |
+
def future(
|
| 1201 |
+
self,
|
| 1202 |
+
request: Any,
|
| 1203 |
+
timeout: Optional[float] = None,
|
| 1204 |
+
metadata: Optional[MetadataType] = None,
|
| 1205 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 1206 |
+
wait_for_ready: Optional[bool] = None,
|
| 1207 |
+
compression: Optional[grpc.Compression] = None,
|
| 1208 |
+
) -> _MultiThreadedRendezvous:
|
| 1209 |
+
state, operations, deadline, rendezvous = self._prepare(
|
| 1210 |
+
request, timeout, metadata, wait_for_ready, compression
|
| 1211 |
+
)
|
| 1212 |
+
if state is None:
|
| 1213 |
+
raise rendezvous # pylint: disable-msg=raising-bad-type
|
| 1214 |
+
else:
|
| 1215 |
+
event_handler = _event_handler(state, self._response_deserializer)
|
| 1216 |
+
state.rpc_start_time = time.perf_counter()
|
| 1217 |
+
state.method = _common.decode(self._method)
|
| 1218 |
+
state.target = _common.decode(self._target)
|
| 1219 |
+
call = self._managed_call(
|
| 1220 |
+
cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
|
| 1221 |
+
self._method,
|
| 1222 |
+
None,
|
| 1223 |
+
deadline,
|
| 1224 |
+
metadata,
|
| 1225 |
+
None if credentials is None else credentials._credentials,
|
| 1226 |
+
(operations,),
|
| 1227 |
+
event_handler,
|
| 1228 |
+
self._context,
|
| 1229 |
+
self._registered_call_handle,
|
| 1230 |
+
)
|
| 1231 |
+
return _MultiThreadedRendezvous(
|
| 1232 |
+
state, call, self._response_deserializer, deadline
|
| 1233 |
+
)
|
| 1234 |
+
|
| 1235 |
+
|
| 1236 |
+
class _SingleThreadedUnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
|
| 1237 |
+
_channel: cygrpc.Channel
|
| 1238 |
+
_method: bytes
|
| 1239 |
+
_target: bytes
|
| 1240 |
+
_request_serializer: Optional[SerializingFunction]
|
| 1241 |
+
_response_deserializer: Optional[DeserializingFunction]
|
| 1242 |
+
_context: Any
|
| 1243 |
+
_registered_call_handle: Optional[int]
|
| 1244 |
+
|
| 1245 |
+
__slots__ = [
|
| 1246 |
+
"_channel",
|
| 1247 |
+
"_method",
|
| 1248 |
+
"_target",
|
| 1249 |
+
"_request_serializer",
|
| 1250 |
+
"_response_deserializer",
|
| 1251 |
+
"_context",
|
| 1252 |
+
]
|
| 1253 |
+
|
| 1254 |
+
# pylint: disable=too-many-arguments
|
| 1255 |
+
def __init__(
|
| 1256 |
+
self,
|
| 1257 |
+
channel: cygrpc.Channel,
|
| 1258 |
+
method: bytes,
|
| 1259 |
+
target: bytes,
|
| 1260 |
+
request_serializer: SerializingFunction,
|
| 1261 |
+
response_deserializer: DeserializingFunction,
|
| 1262 |
+
_registered_call_handle: Optional[int],
|
| 1263 |
+
):
|
| 1264 |
+
self._channel = channel
|
| 1265 |
+
self._method = method
|
| 1266 |
+
self._target = target
|
| 1267 |
+
self._request_serializer = request_serializer
|
| 1268 |
+
self._response_deserializer = response_deserializer
|
| 1269 |
+
self._context = cygrpc.build_census_context()
|
| 1270 |
+
self._registered_call_handle = _registered_call_handle
|
| 1271 |
+
|
| 1272 |
+
def __call__( # pylint: disable=too-many-locals
|
| 1273 |
+
self,
|
| 1274 |
+
request: Any,
|
| 1275 |
+
timeout: Optional[float] = None,
|
| 1276 |
+
metadata: Optional[MetadataType] = None,
|
| 1277 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 1278 |
+
wait_for_ready: Optional[bool] = None,
|
| 1279 |
+
compression: Optional[grpc.Compression] = None,
|
| 1280 |
+
) -> _SingleThreadedRendezvous:
|
| 1281 |
+
deadline = _deadline(timeout)
|
| 1282 |
+
serialized_request = _common.serialize(
|
| 1283 |
+
request, self._request_serializer
|
| 1284 |
+
)
|
| 1285 |
+
if serialized_request is None:
|
| 1286 |
+
state = _RPCState(
|
| 1287 |
+
(),
|
| 1288 |
+
(),
|
| 1289 |
+
(),
|
| 1290 |
+
grpc.StatusCode.INTERNAL,
|
| 1291 |
+
"Exception serializing request!",
|
| 1292 |
+
)
|
| 1293 |
+
raise _InactiveRpcError(state)
|
| 1294 |
+
|
| 1295 |
+
state = _RPCState(_UNARY_STREAM_INITIAL_DUE, None, None, None, None)
|
| 1296 |
+
call_credentials = (
|
| 1297 |
+
None if credentials is None else credentials._credentials
|
| 1298 |
+
)
|
| 1299 |
+
initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
|
| 1300 |
+
wait_for_ready
|
| 1301 |
+
)
|
| 1302 |
+
augmented_metadata = _compression.augment_metadata(
|
| 1303 |
+
metadata, compression
|
| 1304 |
+
)
|
| 1305 |
+
operations = (
|
| 1306 |
+
(
|
| 1307 |
+
cygrpc.SendInitialMetadataOperation(
|
| 1308 |
+
augmented_metadata, initial_metadata_flags
|
| 1309 |
+
),
|
| 1310 |
+
cygrpc.SendMessageOperation(serialized_request, _EMPTY_FLAGS),
|
| 1311 |
+
cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
|
| 1312 |
+
),
|
| 1313 |
+
(cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),),
|
| 1314 |
+
(cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
|
| 1315 |
+
)
|
| 1316 |
+
operations_and_tags = tuple((ops, None) for ops in operations)
|
| 1317 |
+
state.rpc_start_time = time.perf_counter()
|
| 1318 |
+
state.method = _common.decode(self._method)
|
| 1319 |
+
state.target = _common.decode(self._target)
|
| 1320 |
+
call = self._channel.segregated_call(
|
| 1321 |
+
cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
|
| 1322 |
+
self._method,
|
| 1323 |
+
None,
|
| 1324 |
+
_determine_deadline(deadline),
|
| 1325 |
+
metadata,
|
| 1326 |
+
call_credentials,
|
| 1327 |
+
operations_and_tags,
|
| 1328 |
+
self._context,
|
| 1329 |
+
self._registered_call_handle,
|
| 1330 |
+
)
|
| 1331 |
+
return _SingleThreadedRendezvous(
|
| 1332 |
+
state, call, self._response_deserializer, deadline
|
| 1333 |
+
)
|
| 1334 |
+
|
| 1335 |
+
|
| 1336 |
+
class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
|
| 1337 |
+
_channel: cygrpc.Channel
|
| 1338 |
+
_managed_call: IntegratedCallFactory
|
| 1339 |
+
_method: bytes
|
| 1340 |
+
_target: bytes
|
| 1341 |
+
_request_serializer: Optional[SerializingFunction]
|
| 1342 |
+
_response_deserializer: Optional[DeserializingFunction]
|
| 1343 |
+
_context: Any
|
| 1344 |
+
_registered_call_handle: Optional[int]
|
| 1345 |
+
|
| 1346 |
+
__slots__ = [
|
| 1347 |
+
"_channel",
|
| 1348 |
+
"_managed_call",
|
| 1349 |
+
"_method",
|
| 1350 |
+
"_target",
|
| 1351 |
+
"_request_serializer",
|
| 1352 |
+
"_response_deserializer",
|
| 1353 |
+
"_context",
|
| 1354 |
+
]
|
| 1355 |
+
|
| 1356 |
+
# pylint: disable=too-many-arguments
|
| 1357 |
+
def __init__(
|
| 1358 |
+
self,
|
| 1359 |
+
channel: cygrpc.Channel,
|
| 1360 |
+
managed_call: IntegratedCallFactory,
|
| 1361 |
+
method: bytes,
|
| 1362 |
+
target: bytes,
|
| 1363 |
+
request_serializer: SerializingFunction,
|
| 1364 |
+
response_deserializer: DeserializingFunction,
|
| 1365 |
+
_registered_call_handle: Optional[int],
|
| 1366 |
+
):
|
| 1367 |
+
self._channel = channel
|
| 1368 |
+
self._managed_call = managed_call
|
| 1369 |
+
self._method = method
|
| 1370 |
+
self._target = target
|
| 1371 |
+
self._request_serializer = request_serializer
|
| 1372 |
+
self._response_deserializer = response_deserializer
|
| 1373 |
+
self._context = cygrpc.build_census_context()
|
| 1374 |
+
self._registered_call_handle = _registered_call_handle
|
| 1375 |
+
|
| 1376 |
+
def __call__( # pylint: disable=too-many-locals
|
| 1377 |
+
self,
|
| 1378 |
+
request: Any,
|
| 1379 |
+
timeout: Optional[float] = None,
|
| 1380 |
+
metadata: Optional[MetadataType] = None,
|
| 1381 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 1382 |
+
wait_for_ready: Optional[bool] = None,
|
| 1383 |
+
compression: Optional[grpc.Compression] = None,
|
| 1384 |
+
) -> _MultiThreadedRendezvous:
|
| 1385 |
+
deadline, serialized_request, rendezvous = _start_unary_request(
|
| 1386 |
+
request, timeout, self._request_serializer
|
| 1387 |
+
)
|
| 1388 |
+
initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
|
| 1389 |
+
wait_for_ready
|
| 1390 |
+
)
|
| 1391 |
+
if serialized_request is None:
|
| 1392 |
+
raise rendezvous # pylint: disable-msg=raising-bad-type
|
| 1393 |
+
else:
|
| 1394 |
+
augmented_metadata = _compression.augment_metadata(
|
| 1395 |
+
metadata, compression
|
| 1396 |
+
)
|
| 1397 |
+
state = _RPCState(_UNARY_STREAM_INITIAL_DUE, None, None, None, None)
|
| 1398 |
+
operations = (
|
| 1399 |
+
(
|
| 1400 |
+
cygrpc.SendInitialMetadataOperation(
|
| 1401 |
+
augmented_metadata, initial_metadata_flags
|
| 1402 |
+
),
|
| 1403 |
+
cygrpc.SendMessageOperation(
|
| 1404 |
+
serialized_request, _EMPTY_FLAGS
|
| 1405 |
+
),
|
| 1406 |
+
cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
|
| 1407 |
+
cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
|
| 1408 |
+
),
|
| 1409 |
+
(cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
|
| 1410 |
+
)
|
| 1411 |
+
state.rpc_start_time = time.perf_counter()
|
| 1412 |
+
state.method = _common.decode(self._method)
|
| 1413 |
+
state.target = _common.decode(self._target)
|
| 1414 |
+
call = self._managed_call(
|
| 1415 |
+
cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
|
| 1416 |
+
self._method,
|
| 1417 |
+
None,
|
| 1418 |
+
_determine_deadline(deadline),
|
| 1419 |
+
metadata,
|
| 1420 |
+
None if credentials is None else credentials._credentials,
|
| 1421 |
+
operations,
|
| 1422 |
+
_event_handler(state, self._response_deserializer),
|
| 1423 |
+
self._context,
|
| 1424 |
+
self._registered_call_handle,
|
| 1425 |
+
)
|
| 1426 |
+
return _MultiThreadedRendezvous(
|
| 1427 |
+
state, call, self._response_deserializer, deadline
|
| 1428 |
+
)
|
| 1429 |
+
|
| 1430 |
+
|
| 1431 |
+
class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
|
| 1432 |
+
_channel: cygrpc.Channel
|
| 1433 |
+
_managed_call: IntegratedCallFactory
|
| 1434 |
+
_method: bytes
|
| 1435 |
+
_target: bytes
|
| 1436 |
+
_request_serializer: Optional[SerializingFunction]
|
| 1437 |
+
_response_deserializer: Optional[DeserializingFunction]
|
| 1438 |
+
_context: Any
|
| 1439 |
+
_registered_call_handle: Optional[int]
|
| 1440 |
+
|
| 1441 |
+
__slots__ = [
|
| 1442 |
+
"_channel",
|
| 1443 |
+
"_managed_call",
|
| 1444 |
+
"_method",
|
| 1445 |
+
"_target",
|
| 1446 |
+
"_request_serializer",
|
| 1447 |
+
"_response_deserializer",
|
| 1448 |
+
"_context",
|
| 1449 |
+
]
|
| 1450 |
+
|
| 1451 |
+
# pylint: disable=too-many-arguments
|
| 1452 |
+
def __init__(
|
| 1453 |
+
self,
|
| 1454 |
+
channel: cygrpc.Channel,
|
| 1455 |
+
managed_call: IntegratedCallFactory,
|
| 1456 |
+
method: bytes,
|
| 1457 |
+
target: bytes,
|
| 1458 |
+
request_serializer: Optional[SerializingFunction],
|
| 1459 |
+
response_deserializer: Optional[DeserializingFunction],
|
| 1460 |
+
_registered_call_handle: Optional[int],
|
| 1461 |
+
):
|
| 1462 |
+
self._channel = channel
|
| 1463 |
+
self._managed_call = managed_call
|
| 1464 |
+
self._method = method
|
| 1465 |
+
self._target = target
|
| 1466 |
+
self._request_serializer = request_serializer
|
| 1467 |
+
self._response_deserializer = response_deserializer
|
| 1468 |
+
self._context = cygrpc.build_census_context()
|
| 1469 |
+
self._registered_call_handle = _registered_call_handle
|
| 1470 |
+
|
| 1471 |
+
def _blocking(
|
| 1472 |
+
self,
|
| 1473 |
+
request_iterator: Iterator,
|
| 1474 |
+
timeout: Optional[float],
|
| 1475 |
+
metadata: Optional[MetadataType],
|
| 1476 |
+
credentials: Optional[grpc.CallCredentials],
|
| 1477 |
+
wait_for_ready: Optional[bool],
|
| 1478 |
+
compression: Optional[grpc.Compression],
|
| 1479 |
+
) -> Tuple[_RPCState, cygrpc.SegregatedCall]:
|
| 1480 |
+
deadline = _deadline(timeout)
|
| 1481 |
+
state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None)
|
| 1482 |
+
initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
|
| 1483 |
+
wait_for_ready
|
| 1484 |
+
)
|
| 1485 |
+
augmented_metadata = _compression.augment_metadata(
|
| 1486 |
+
metadata, compression
|
| 1487 |
+
)
|
| 1488 |
+
state.rpc_start_time = time.perf_counter()
|
| 1489 |
+
state.method = _common.decode(self._method)
|
| 1490 |
+
state.target = _common.decode(self._target)
|
| 1491 |
+
call = self._channel.segregated_call(
|
| 1492 |
+
cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
|
| 1493 |
+
self._method,
|
| 1494 |
+
None,
|
| 1495 |
+
_determine_deadline(deadline),
|
| 1496 |
+
augmented_metadata,
|
| 1497 |
+
None if credentials is None else credentials._credentials,
|
| 1498 |
+
_stream_unary_invocation_operations_and_tags(
|
| 1499 |
+
augmented_metadata, initial_metadata_flags
|
| 1500 |
+
),
|
| 1501 |
+
self._context,
|
| 1502 |
+
self._registered_call_handle,
|
| 1503 |
+
)
|
| 1504 |
+
_consume_request_iterator(
|
| 1505 |
+
request_iterator, state, call, self._request_serializer, None
|
| 1506 |
+
)
|
| 1507 |
+
while True:
|
| 1508 |
+
event = call.next_event()
|
| 1509 |
+
with state.condition:
|
| 1510 |
+
_handle_event(event, state, self._response_deserializer)
|
| 1511 |
+
state.condition.notify_all()
|
| 1512 |
+
if not state.due:
|
| 1513 |
+
break
|
| 1514 |
+
return state, call
|
| 1515 |
+
|
| 1516 |
+
def __call__(
|
| 1517 |
+
self,
|
| 1518 |
+
request_iterator: Iterator,
|
| 1519 |
+
timeout: Optional[float] = None,
|
| 1520 |
+
metadata: Optional[MetadataType] = None,
|
| 1521 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 1522 |
+
wait_for_ready: Optional[bool] = None,
|
| 1523 |
+
compression: Optional[grpc.Compression] = None,
|
| 1524 |
+
) -> Any:
|
| 1525 |
+
(
|
| 1526 |
+
state,
|
| 1527 |
+
call,
|
| 1528 |
+
) = self._blocking(
|
| 1529 |
+
request_iterator,
|
| 1530 |
+
timeout,
|
| 1531 |
+
metadata,
|
| 1532 |
+
credentials,
|
| 1533 |
+
wait_for_ready,
|
| 1534 |
+
compression,
|
| 1535 |
+
)
|
| 1536 |
+
return _end_unary_response_blocking(state, call, False, None)
|
| 1537 |
+
|
| 1538 |
+
def with_call(
|
| 1539 |
+
self,
|
| 1540 |
+
request_iterator: Iterator,
|
| 1541 |
+
timeout: Optional[float] = None,
|
| 1542 |
+
metadata: Optional[MetadataType] = None,
|
| 1543 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 1544 |
+
wait_for_ready: Optional[bool] = None,
|
| 1545 |
+
compression: Optional[grpc.Compression] = None,
|
| 1546 |
+
) -> Tuple[Any, grpc.Call]:
|
| 1547 |
+
(
|
| 1548 |
+
state,
|
| 1549 |
+
call,
|
| 1550 |
+
) = self._blocking(
|
| 1551 |
+
request_iterator,
|
| 1552 |
+
timeout,
|
| 1553 |
+
metadata,
|
| 1554 |
+
credentials,
|
| 1555 |
+
wait_for_ready,
|
| 1556 |
+
compression,
|
| 1557 |
+
)
|
| 1558 |
+
return _end_unary_response_blocking(state, call, True, None)
|
| 1559 |
+
|
| 1560 |
+
def future(
|
| 1561 |
+
self,
|
| 1562 |
+
request_iterator: Iterator,
|
| 1563 |
+
timeout: Optional[float] = None,
|
| 1564 |
+
metadata: Optional[MetadataType] = None,
|
| 1565 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 1566 |
+
wait_for_ready: Optional[bool] = None,
|
| 1567 |
+
compression: Optional[grpc.Compression] = None,
|
| 1568 |
+
) -> _MultiThreadedRendezvous:
|
| 1569 |
+
deadline = _deadline(timeout)
|
| 1570 |
+
state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None)
|
| 1571 |
+
event_handler = _event_handler(state, self._response_deserializer)
|
| 1572 |
+
initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
|
| 1573 |
+
wait_for_ready
|
| 1574 |
+
)
|
| 1575 |
+
augmented_metadata = _compression.augment_metadata(
|
| 1576 |
+
metadata, compression
|
| 1577 |
+
)
|
| 1578 |
+
state.rpc_start_time = time.perf_counter()
|
| 1579 |
+
state.method = _common.decode(self._method)
|
| 1580 |
+
state.target = _common.decode(self._target)
|
| 1581 |
+
call = self._managed_call(
|
| 1582 |
+
cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
|
| 1583 |
+
self._method,
|
| 1584 |
+
None,
|
| 1585 |
+
deadline,
|
| 1586 |
+
augmented_metadata,
|
| 1587 |
+
None if credentials is None else credentials._credentials,
|
| 1588 |
+
_stream_unary_invocation_operations(
|
| 1589 |
+
metadata, initial_metadata_flags
|
| 1590 |
+
),
|
| 1591 |
+
event_handler,
|
| 1592 |
+
self._context,
|
| 1593 |
+
self._registered_call_handle,
|
| 1594 |
+
)
|
| 1595 |
+
_consume_request_iterator(
|
| 1596 |
+
request_iterator,
|
| 1597 |
+
state,
|
| 1598 |
+
call,
|
| 1599 |
+
self._request_serializer,
|
| 1600 |
+
event_handler,
|
| 1601 |
+
)
|
| 1602 |
+
return _MultiThreadedRendezvous(
|
| 1603 |
+
state, call, self._response_deserializer, deadline
|
| 1604 |
+
)
|
| 1605 |
+
|
| 1606 |
+
|
| 1607 |
+
class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
|
| 1608 |
+
_channel: cygrpc.Channel
|
| 1609 |
+
_managed_call: IntegratedCallFactory
|
| 1610 |
+
_method: bytes
|
| 1611 |
+
_target: bytes
|
| 1612 |
+
_request_serializer: Optional[SerializingFunction]
|
| 1613 |
+
_response_deserializer: Optional[DeserializingFunction]
|
| 1614 |
+
_context: Any
|
| 1615 |
+
_registered_call_handle: Optional[int]
|
| 1616 |
+
|
| 1617 |
+
__slots__ = [
|
| 1618 |
+
"_channel",
|
| 1619 |
+
"_managed_call",
|
| 1620 |
+
"_method",
|
| 1621 |
+
"_target",
|
| 1622 |
+
"_request_serializer",
|
| 1623 |
+
"_response_deserializer",
|
| 1624 |
+
"_context",
|
| 1625 |
+
]
|
| 1626 |
+
|
| 1627 |
+
# pylint: disable=too-many-arguments
|
| 1628 |
+
def __init__(
|
| 1629 |
+
self,
|
| 1630 |
+
channel: cygrpc.Channel,
|
| 1631 |
+
managed_call: IntegratedCallFactory,
|
| 1632 |
+
method: bytes,
|
| 1633 |
+
target: bytes,
|
| 1634 |
+
request_serializer: Optional[SerializingFunction],
|
| 1635 |
+
response_deserializer: Optional[DeserializingFunction],
|
| 1636 |
+
_registered_call_handle: Optional[int],
|
| 1637 |
+
):
|
| 1638 |
+
self._channel = channel
|
| 1639 |
+
self._managed_call = managed_call
|
| 1640 |
+
self._method = method
|
| 1641 |
+
self._target = target
|
| 1642 |
+
self._request_serializer = request_serializer
|
| 1643 |
+
self._response_deserializer = response_deserializer
|
| 1644 |
+
self._context = cygrpc.build_census_context()
|
| 1645 |
+
self._registered_call_handle = _registered_call_handle
|
| 1646 |
+
|
| 1647 |
+
def __call__(
|
| 1648 |
+
self,
|
| 1649 |
+
request_iterator: Iterator,
|
| 1650 |
+
timeout: Optional[float] = None,
|
| 1651 |
+
metadata: Optional[MetadataType] = None,
|
| 1652 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 1653 |
+
wait_for_ready: Optional[bool] = None,
|
| 1654 |
+
compression: Optional[grpc.Compression] = None,
|
| 1655 |
+
) -> _MultiThreadedRendezvous:
|
| 1656 |
+
deadline = _deadline(timeout)
|
| 1657 |
+
state = _RPCState(_STREAM_STREAM_INITIAL_DUE, None, None, None, None)
|
| 1658 |
+
initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
|
| 1659 |
+
wait_for_ready
|
| 1660 |
+
)
|
| 1661 |
+
augmented_metadata = _compression.augment_metadata(
|
| 1662 |
+
metadata, compression
|
| 1663 |
+
)
|
| 1664 |
+
operations = (
|
| 1665 |
+
(
|
| 1666 |
+
cygrpc.SendInitialMetadataOperation(
|
| 1667 |
+
augmented_metadata, initial_metadata_flags
|
| 1668 |
+
),
|
| 1669 |
+
cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
|
| 1670 |
+
),
|
| 1671 |
+
(cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
|
| 1672 |
+
)
|
| 1673 |
+
event_handler = _event_handler(state, self._response_deserializer)
|
| 1674 |
+
state.rpc_start_time = time.perf_counter()
|
| 1675 |
+
state.method = _common.decode(self._method)
|
| 1676 |
+
state.target = _common.decode(self._target)
|
| 1677 |
+
call = self._managed_call(
|
| 1678 |
+
cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
|
| 1679 |
+
self._method,
|
| 1680 |
+
None,
|
| 1681 |
+
_determine_deadline(deadline),
|
| 1682 |
+
augmented_metadata,
|
| 1683 |
+
None if credentials is None else credentials._credentials,
|
| 1684 |
+
operations,
|
| 1685 |
+
event_handler,
|
| 1686 |
+
self._context,
|
| 1687 |
+
self._registered_call_handle,
|
| 1688 |
+
)
|
| 1689 |
+
_consume_request_iterator(
|
| 1690 |
+
request_iterator,
|
| 1691 |
+
state,
|
| 1692 |
+
call,
|
| 1693 |
+
self._request_serializer,
|
| 1694 |
+
event_handler,
|
| 1695 |
+
)
|
| 1696 |
+
return _MultiThreadedRendezvous(
|
| 1697 |
+
state, call, self._response_deserializer, deadline
|
| 1698 |
+
)
|
| 1699 |
+
|
| 1700 |
+
|
| 1701 |
+
class _InitialMetadataFlags(int):
|
| 1702 |
+
"""Stores immutable initial metadata flags"""
|
| 1703 |
+
|
| 1704 |
+
def __new__(cls, value: int = _EMPTY_FLAGS):
|
| 1705 |
+
value &= cygrpc.InitialMetadataFlags.used_mask
|
| 1706 |
+
return super(_InitialMetadataFlags, cls).__new__(cls, value)
|
| 1707 |
+
|
| 1708 |
+
def with_wait_for_ready(self, wait_for_ready: Optional[bool]) -> int:
|
| 1709 |
+
if wait_for_ready is not None:
|
| 1710 |
+
if wait_for_ready:
|
| 1711 |
+
return self.__class__(
|
| 1712 |
+
self
|
| 1713 |
+
| cygrpc.InitialMetadataFlags.wait_for_ready
|
| 1714 |
+
| cygrpc.InitialMetadataFlags.wait_for_ready_explicitly_set
|
| 1715 |
+
)
|
| 1716 |
+
elif not wait_for_ready:
|
| 1717 |
+
return self.__class__(
|
| 1718 |
+
self & ~cygrpc.InitialMetadataFlags.wait_for_ready
|
| 1719 |
+
| cygrpc.InitialMetadataFlags.wait_for_ready_explicitly_set
|
| 1720 |
+
)
|
| 1721 |
+
return self
|
| 1722 |
+
|
| 1723 |
+
|
| 1724 |
+
class _ChannelCallState(object):
|
| 1725 |
+
channel: cygrpc.Channel
|
| 1726 |
+
managed_calls: int
|
| 1727 |
+
threading: bool
|
| 1728 |
+
|
| 1729 |
+
def __init__(self, channel: cygrpc.Channel):
|
| 1730 |
+
self.lock = threading.Lock()
|
| 1731 |
+
self.channel = channel
|
| 1732 |
+
self.managed_calls = 0
|
| 1733 |
+
self.threading = False
|
| 1734 |
+
|
| 1735 |
+
def reset_postfork_child(self) -> None:
|
| 1736 |
+
self.managed_calls = 0
|
| 1737 |
+
|
| 1738 |
+
def __del__(self):
|
| 1739 |
+
try:
|
| 1740 |
+
self.channel.close(
|
| 1741 |
+
cygrpc.StatusCode.cancelled, "Channel deallocated!"
|
| 1742 |
+
)
|
| 1743 |
+
except (TypeError, AttributeError):
|
| 1744 |
+
pass
|
| 1745 |
+
|
| 1746 |
+
|
| 1747 |
+
def _run_channel_spin_thread(state: _ChannelCallState) -> None:
|
| 1748 |
+
def channel_spin():
|
| 1749 |
+
while True:
|
| 1750 |
+
cygrpc.block_if_fork_in_progress(state)
|
| 1751 |
+
event = state.channel.next_call_event()
|
| 1752 |
+
if event.completion_type == cygrpc.CompletionType.queue_timeout:
|
| 1753 |
+
continue
|
| 1754 |
+
call_completed = event.tag(event)
|
| 1755 |
+
if call_completed:
|
| 1756 |
+
with state.lock:
|
| 1757 |
+
state.managed_calls -= 1
|
| 1758 |
+
if state.managed_calls == 0:
|
| 1759 |
+
return
|
| 1760 |
+
|
| 1761 |
+
channel_spin_thread = cygrpc.ForkManagedThread(target=channel_spin)
|
| 1762 |
+
channel_spin_thread.setDaemon(True)
|
| 1763 |
+
channel_spin_thread.start()
|
| 1764 |
+
|
| 1765 |
+
|
| 1766 |
+
def _channel_managed_call_management(state: _ChannelCallState):
|
| 1767 |
+
# pylint: disable=too-many-arguments
|
| 1768 |
+
def create(
|
| 1769 |
+
flags: int,
|
| 1770 |
+
method: bytes,
|
| 1771 |
+
host: Optional[str],
|
| 1772 |
+
deadline: Optional[float],
|
| 1773 |
+
metadata: Optional[MetadataType],
|
| 1774 |
+
credentials: Optional[cygrpc.CallCredentials],
|
| 1775 |
+
operations: Sequence[Sequence[cygrpc.Operation]],
|
| 1776 |
+
event_handler: UserTag,
|
| 1777 |
+
context: Any,
|
| 1778 |
+
_registered_call_handle: Optional[int],
|
| 1779 |
+
) -> cygrpc.IntegratedCall:
|
| 1780 |
+
"""Creates a cygrpc.IntegratedCall.
|
| 1781 |
+
|
| 1782 |
+
Args:
|
| 1783 |
+
flags: An integer bitfield of call flags.
|
| 1784 |
+
method: The RPC method.
|
| 1785 |
+
host: A host string for the created call.
|
| 1786 |
+
deadline: A float to be the deadline of the created call or None if
|
| 1787 |
+
the call is to have an infinite deadline.
|
| 1788 |
+
metadata: The metadata for the call or None.
|
| 1789 |
+
credentials: A cygrpc.CallCredentials or None.
|
| 1790 |
+
operations: A sequence of sequences of cygrpc.Operations to be
|
| 1791 |
+
started on the call.
|
| 1792 |
+
event_handler: A behavior to call to handle the events resultant from
|
| 1793 |
+
the operations on the call.
|
| 1794 |
+
context: Context object for distributed tracing.
|
| 1795 |
+
_registered_call_handle: An int representing the call handle of the
|
| 1796 |
+
method, or None if the method is not registered.
|
| 1797 |
+
Returns:
|
| 1798 |
+
A cygrpc.IntegratedCall with which to conduct an RPC.
|
| 1799 |
+
"""
|
| 1800 |
+
operations_and_tags = tuple(
|
| 1801 |
+
(
|
| 1802 |
+
operation,
|
| 1803 |
+
event_handler,
|
| 1804 |
+
)
|
| 1805 |
+
for operation in operations
|
| 1806 |
+
)
|
| 1807 |
+
with state.lock:
|
| 1808 |
+
call = state.channel.integrated_call(
|
| 1809 |
+
flags,
|
| 1810 |
+
method,
|
| 1811 |
+
host,
|
| 1812 |
+
deadline,
|
| 1813 |
+
metadata,
|
| 1814 |
+
credentials,
|
| 1815 |
+
operations_and_tags,
|
| 1816 |
+
context,
|
| 1817 |
+
_registered_call_handle,
|
| 1818 |
+
)
|
| 1819 |
+
if state.managed_calls == 0:
|
| 1820 |
+
state.managed_calls = 1
|
| 1821 |
+
_run_channel_spin_thread(state)
|
| 1822 |
+
else:
|
| 1823 |
+
state.managed_calls += 1
|
| 1824 |
+
return call
|
| 1825 |
+
|
| 1826 |
+
return create
|
| 1827 |
+
|
| 1828 |
+
|
| 1829 |
+
class _ChannelConnectivityState(object):
|
| 1830 |
+
lock: threading.RLock
|
| 1831 |
+
channel: grpc.Channel
|
| 1832 |
+
polling: bool
|
| 1833 |
+
connectivity: grpc.ChannelConnectivity
|
| 1834 |
+
try_to_connect: bool
|
| 1835 |
+
# TODO(xuanwn): Refactor this: https://github.com/grpc/grpc/issues/31704
|
| 1836 |
+
callbacks_and_connectivities: List[
|
| 1837 |
+
Sequence[
|
| 1838 |
+
Union[
|
| 1839 |
+
Callable[[grpc.ChannelConnectivity], None],
|
| 1840 |
+
Optional[grpc.ChannelConnectivity],
|
| 1841 |
+
]
|
| 1842 |
+
]
|
| 1843 |
+
]
|
| 1844 |
+
delivering: bool
|
| 1845 |
+
|
| 1846 |
+
def __init__(self, channel: grpc.Channel):
|
| 1847 |
+
self.lock = threading.RLock()
|
| 1848 |
+
self.channel = channel
|
| 1849 |
+
self.polling = False
|
| 1850 |
+
self.connectivity = None
|
| 1851 |
+
self.try_to_connect = False
|
| 1852 |
+
self.callbacks_and_connectivities = []
|
| 1853 |
+
self.delivering = False
|
| 1854 |
+
|
| 1855 |
+
def reset_postfork_child(self) -> None:
|
| 1856 |
+
self.polling = False
|
| 1857 |
+
self.connectivity = None
|
| 1858 |
+
self.try_to_connect = False
|
| 1859 |
+
self.callbacks_and_connectivities = []
|
| 1860 |
+
self.delivering = False
|
| 1861 |
+
|
| 1862 |
+
|
| 1863 |
+
def _deliveries(
|
| 1864 |
+
state: _ChannelConnectivityState,
|
| 1865 |
+
) -> List[Callable[[grpc.ChannelConnectivity], None]]:
|
| 1866 |
+
callbacks_needing_update = []
|
| 1867 |
+
for callback_and_connectivity in state.callbacks_and_connectivities:
|
| 1868 |
+
(
|
| 1869 |
+
callback,
|
| 1870 |
+
callback_connectivity,
|
| 1871 |
+
) = callback_and_connectivity
|
| 1872 |
+
if callback_connectivity is not state.connectivity:
|
| 1873 |
+
callbacks_needing_update.append(callback)
|
| 1874 |
+
callback_and_connectivity[1] = state.connectivity
|
| 1875 |
+
return callbacks_needing_update
|
| 1876 |
+
|
| 1877 |
+
|
| 1878 |
+
def _deliver(
|
| 1879 |
+
state: _ChannelConnectivityState,
|
| 1880 |
+
initial_connectivity: grpc.ChannelConnectivity,
|
| 1881 |
+
initial_callbacks: Sequence[Callable[[grpc.ChannelConnectivity], None]],
|
| 1882 |
+
) -> None:
|
| 1883 |
+
connectivity = initial_connectivity
|
| 1884 |
+
callbacks = initial_callbacks
|
| 1885 |
+
while True:
|
| 1886 |
+
for callback in callbacks:
|
| 1887 |
+
cygrpc.block_if_fork_in_progress(state)
|
| 1888 |
+
try:
|
| 1889 |
+
callback(connectivity)
|
| 1890 |
+
except Exception: # pylint: disable=broad-except
|
| 1891 |
+
_LOGGER.exception(
|
| 1892 |
+
_CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE
|
| 1893 |
+
)
|
| 1894 |
+
with state.lock:
|
| 1895 |
+
callbacks = _deliveries(state)
|
| 1896 |
+
if callbacks:
|
| 1897 |
+
connectivity = state.connectivity
|
| 1898 |
+
else:
|
| 1899 |
+
state.delivering = False
|
| 1900 |
+
return
|
| 1901 |
+
|
| 1902 |
+
|
| 1903 |
+
def _spawn_delivery(
|
| 1904 |
+
state: _ChannelConnectivityState,
|
| 1905 |
+
callbacks: Sequence[Callable[[grpc.ChannelConnectivity], None]],
|
| 1906 |
+
) -> None:
|
| 1907 |
+
delivering_thread = cygrpc.ForkManagedThread(
|
| 1908 |
+
target=_deliver,
|
| 1909 |
+
args=(
|
| 1910 |
+
state,
|
| 1911 |
+
state.connectivity,
|
| 1912 |
+
callbacks,
|
| 1913 |
+
),
|
| 1914 |
+
)
|
| 1915 |
+
delivering_thread.setDaemon(True)
|
| 1916 |
+
delivering_thread.start()
|
| 1917 |
+
state.delivering = True
|
| 1918 |
+
|
| 1919 |
+
|
| 1920 |
+
# NOTE(https://github.com/grpc/grpc/issues/3064): We'd rather not poll.
|
| 1921 |
+
def _poll_connectivity(
|
| 1922 |
+
state: _ChannelConnectivityState,
|
| 1923 |
+
channel: grpc.Channel,
|
| 1924 |
+
initial_try_to_connect: bool,
|
| 1925 |
+
) -> None:
|
| 1926 |
+
try_to_connect = initial_try_to_connect
|
| 1927 |
+
connectivity = channel.check_connectivity_state(try_to_connect)
|
| 1928 |
+
with state.lock:
|
| 1929 |
+
state.connectivity = (
|
| 1930 |
+
_common.CYGRPC_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[
|
| 1931 |
+
connectivity
|
| 1932 |
+
]
|
| 1933 |
+
)
|
| 1934 |
+
callbacks = tuple(
|
| 1935 |
+
callback for callback, _ in state.callbacks_and_connectivities
|
| 1936 |
+
)
|
| 1937 |
+
for callback_and_connectivity in state.callbacks_and_connectivities:
|
| 1938 |
+
callback_and_connectivity[1] = state.connectivity
|
| 1939 |
+
if callbacks:
|
| 1940 |
+
_spawn_delivery(state, callbacks)
|
| 1941 |
+
while True:
|
| 1942 |
+
event = channel.watch_connectivity_state(
|
| 1943 |
+
connectivity, time.time() + 0.2
|
| 1944 |
+
)
|
| 1945 |
+
cygrpc.block_if_fork_in_progress(state)
|
| 1946 |
+
with state.lock:
|
| 1947 |
+
if (
|
| 1948 |
+
not state.callbacks_and_connectivities
|
| 1949 |
+
and not state.try_to_connect
|
| 1950 |
+
):
|
| 1951 |
+
state.polling = False
|
| 1952 |
+
state.connectivity = None
|
| 1953 |
+
break
|
| 1954 |
+
try_to_connect = state.try_to_connect
|
| 1955 |
+
state.try_to_connect = False
|
| 1956 |
+
if event.success or try_to_connect:
|
| 1957 |
+
connectivity = channel.check_connectivity_state(try_to_connect)
|
| 1958 |
+
with state.lock:
|
| 1959 |
+
state.connectivity = (
|
| 1960 |
+
_common.CYGRPC_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[
|
| 1961 |
+
connectivity
|
| 1962 |
+
]
|
| 1963 |
+
)
|
| 1964 |
+
if not state.delivering:
|
| 1965 |
+
callbacks = _deliveries(state)
|
| 1966 |
+
if callbacks:
|
| 1967 |
+
_spawn_delivery(state, callbacks)
|
| 1968 |
+
|
| 1969 |
+
|
| 1970 |
+
def _subscribe(
|
| 1971 |
+
state: _ChannelConnectivityState,
|
| 1972 |
+
callback: Callable[[grpc.ChannelConnectivity], None],
|
| 1973 |
+
try_to_connect: bool,
|
| 1974 |
+
) -> None:
|
| 1975 |
+
with state.lock:
|
| 1976 |
+
if not state.callbacks_and_connectivities and not state.polling:
|
| 1977 |
+
polling_thread = cygrpc.ForkManagedThread(
|
| 1978 |
+
target=_poll_connectivity,
|
| 1979 |
+
args=(state, state.channel, bool(try_to_connect)),
|
| 1980 |
+
)
|
| 1981 |
+
polling_thread.setDaemon(True)
|
| 1982 |
+
polling_thread.start()
|
| 1983 |
+
state.polling = True
|
| 1984 |
+
state.callbacks_and_connectivities.append([callback, None])
|
| 1985 |
+
elif not state.delivering and state.connectivity is not None:
|
| 1986 |
+
_spawn_delivery(state, (callback,))
|
| 1987 |
+
state.try_to_connect |= bool(try_to_connect)
|
| 1988 |
+
state.callbacks_and_connectivities.append(
|
| 1989 |
+
[callback, state.connectivity]
|
| 1990 |
+
)
|
| 1991 |
+
else:
|
| 1992 |
+
state.try_to_connect |= bool(try_to_connect)
|
| 1993 |
+
state.callbacks_and_connectivities.append([callback, None])
|
| 1994 |
+
|
| 1995 |
+
|
| 1996 |
+
def _unsubscribe(
|
| 1997 |
+
state: _ChannelConnectivityState,
|
| 1998 |
+
callback: Callable[[grpc.ChannelConnectivity], None],
|
| 1999 |
+
) -> None:
|
| 2000 |
+
with state.lock:
|
| 2001 |
+
for index, (subscribed_callback, unused_connectivity) in enumerate(
|
| 2002 |
+
state.callbacks_and_connectivities
|
| 2003 |
+
):
|
| 2004 |
+
if callback == subscribed_callback:
|
| 2005 |
+
state.callbacks_and_connectivities.pop(index)
|
| 2006 |
+
break
|
| 2007 |
+
|
| 2008 |
+
|
| 2009 |
+
def _augment_options(
|
| 2010 |
+
base_options: Sequence[ChannelArgumentType],
|
| 2011 |
+
compression: Optional[grpc.Compression],
|
| 2012 |
+
) -> Sequence[ChannelArgumentType]:
|
| 2013 |
+
compression_option = _compression.create_channel_option(compression)
|
| 2014 |
+
return (
|
| 2015 |
+
tuple(base_options)
|
| 2016 |
+
+ compression_option
|
| 2017 |
+
+ (
|
| 2018 |
+
(
|
| 2019 |
+
cygrpc.ChannelArgKey.primary_user_agent_string,
|
| 2020 |
+
_USER_AGENT,
|
| 2021 |
+
),
|
| 2022 |
+
)
|
| 2023 |
+
)
|
| 2024 |
+
|
| 2025 |
+
|
| 2026 |
+
def _separate_channel_options(
|
| 2027 |
+
options: Sequence[ChannelArgumentType],
|
| 2028 |
+
) -> Tuple[Sequence[ChannelArgumentType], Sequence[ChannelArgumentType]]:
|
| 2029 |
+
"""Separates core channel options from Python channel options."""
|
| 2030 |
+
core_options = []
|
| 2031 |
+
python_options = []
|
| 2032 |
+
for pair in options:
|
| 2033 |
+
if (
|
| 2034 |
+
pair[0]
|
| 2035 |
+
== grpc.experimental.ChannelOptions.SingleThreadedUnaryStream
|
| 2036 |
+
):
|
| 2037 |
+
python_options.append(pair)
|
| 2038 |
+
else:
|
| 2039 |
+
core_options.append(pair)
|
| 2040 |
+
return python_options, core_options
|
| 2041 |
+
|
| 2042 |
+
|
| 2043 |
+
class Channel(grpc.Channel):
|
| 2044 |
+
"""A cygrpc.Channel-backed implementation of grpc.Channel."""
|
| 2045 |
+
|
| 2046 |
+
_single_threaded_unary_stream: bool
|
| 2047 |
+
_channel: cygrpc.Channel
|
| 2048 |
+
_call_state: _ChannelCallState
|
| 2049 |
+
_connectivity_state: _ChannelConnectivityState
|
| 2050 |
+
_target: str
|
| 2051 |
+
_registered_call_handles: Dict[str, int]
|
| 2052 |
+
|
| 2053 |
+
def __init__(
|
| 2054 |
+
self,
|
| 2055 |
+
target: str,
|
| 2056 |
+
options: Sequence[ChannelArgumentType],
|
| 2057 |
+
credentials: Optional[grpc.ChannelCredentials],
|
| 2058 |
+
compression: Optional[grpc.Compression],
|
| 2059 |
+
):
|
| 2060 |
+
"""Constructor.
|
| 2061 |
+
|
| 2062 |
+
Args:
|
| 2063 |
+
target: The target to which to connect.
|
| 2064 |
+
options: Configuration options for the channel.
|
| 2065 |
+
credentials: A cygrpc.ChannelCredentials or None.
|
| 2066 |
+
compression: An optional value indicating the compression method to be
|
| 2067 |
+
used over the lifetime of the channel.
|
| 2068 |
+
"""
|
| 2069 |
+
python_options, core_options = _separate_channel_options(options)
|
| 2070 |
+
self._single_threaded_unary_stream = (
|
| 2071 |
+
_DEFAULT_SINGLE_THREADED_UNARY_STREAM
|
| 2072 |
+
)
|
| 2073 |
+
self._process_python_options(python_options)
|
| 2074 |
+
self._channel = cygrpc.Channel(
|
| 2075 |
+
_common.encode(target),
|
| 2076 |
+
_augment_options(core_options, compression),
|
| 2077 |
+
credentials,
|
| 2078 |
+
)
|
| 2079 |
+
self._target = target
|
| 2080 |
+
self._call_state = _ChannelCallState(self._channel)
|
| 2081 |
+
self._connectivity_state = _ChannelConnectivityState(self._channel)
|
| 2082 |
+
cygrpc.fork_register_channel(self)
|
| 2083 |
+
if cygrpc.g_gevent_activated:
|
| 2084 |
+
cygrpc.gevent_increment_channel_count()
|
| 2085 |
+
|
| 2086 |
+
def _get_registered_call_handle(self, method: str) -> int:
|
| 2087 |
+
"""
|
| 2088 |
+
Get the registered call handle for a method.
|
| 2089 |
+
|
| 2090 |
+
This is a semi-private method. It is intended for use only by gRPC generated code.
|
| 2091 |
+
|
| 2092 |
+
This method is not thread-safe.
|
| 2093 |
+
|
| 2094 |
+
Args:
|
| 2095 |
+
method: Required, the method name for the RPC.
|
| 2096 |
+
|
| 2097 |
+
Returns:
|
| 2098 |
+
The registered call handle pointer in the form of a Python Long.
|
| 2099 |
+
"""
|
| 2100 |
+
return self._channel.get_registered_call_handle(_common.encode(method))
|
| 2101 |
+
|
| 2102 |
+
def _process_python_options(
|
| 2103 |
+
self, python_options: Sequence[ChannelArgumentType]
|
| 2104 |
+
) -> None:
|
| 2105 |
+
"""Sets channel attributes according to python-only channel options."""
|
| 2106 |
+
for pair in python_options:
|
| 2107 |
+
if (
|
| 2108 |
+
pair[0]
|
| 2109 |
+
== grpc.experimental.ChannelOptions.SingleThreadedUnaryStream
|
| 2110 |
+
):
|
| 2111 |
+
self._single_threaded_unary_stream = True
|
| 2112 |
+
|
| 2113 |
+
def subscribe(
|
| 2114 |
+
self,
|
| 2115 |
+
callback: Callable[[grpc.ChannelConnectivity], None],
|
| 2116 |
+
try_to_connect: Optional[bool] = None,
|
| 2117 |
+
) -> None:
|
| 2118 |
+
_subscribe(self._connectivity_state, callback, try_to_connect)
|
| 2119 |
+
|
| 2120 |
+
def unsubscribe(
|
| 2121 |
+
self, callback: Callable[[grpc.ChannelConnectivity], None]
|
| 2122 |
+
) -> None:
|
| 2123 |
+
_unsubscribe(self._connectivity_state, callback)
|
| 2124 |
+
|
| 2125 |
+
# pylint: disable=arguments-differ
|
| 2126 |
+
def unary_unary(
|
| 2127 |
+
self,
|
| 2128 |
+
method: str,
|
| 2129 |
+
request_serializer: Optional[SerializingFunction] = None,
|
| 2130 |
+
response_deserializer: Optional[DeserializingFunction] = None,
|
| 2131 |
+
_registered_method: Optional[bool] = False,
|
| 2132 |
+
) -> grpc.UnaryUnaryMultiCallable:
|
| 2133 |
+
_registered_call_handle = None
|
| 2134 |
+
if _registered_method:
|
| 2135 |
+
_registered_call_handle = self._get_registered_call_handle(method)
|
| 2136 |
+
return _UnaryUnaryMultiCallable(
|
| 2137 |
+
self._channel,
|
| 2138 |
+
_channel_managed_call_management(self._call_state),
|
| 2139 |
+
_common.encode(method),
|
| 2140 |
+
_common.encode(self._target),
|
| 2141 |
+
request_serializer,
|
| 2142 |
+
response_deserializer,
|
| 2143 |
+
_registered_call_handle,
|
| 2144 |
+
)
|
| 2145 |
+
|
| 2146 |
+
# pylint: disable=arguments-differ
|
| 2147 |
+
def unary_stream(
|
| 2148 |
+
self,
|
| 2149 |
+
method: str,
|
| 2150 |
+
request_serializer: Optional[SerializingFunction] = None,
|
| 2151 |
+
response_deserializer: Optional[DeserializingFunction] = None,
|
| 2152 |
+
_registered_method: Optional[bool] = False,
|
| 2153 |
+
) -> grpc.UnaryStreamMultiCallable:
|
| 2154 |
+
_registered_call_handle = None
|
| 2155 |
+
if _registered_method:
|
| 2156 |
+
_registered_call_handle = self._get_registered_call_handle(method)
|
| 2157 |
+
# NOTE(rbellevi): Benchmarks have shown that running a unary-stream RPC
|
| 2158 |
+
# on a single Python thread results in an appreciable speed-up. However,
|
| 2159 |
+
# due to slight differences in capability, the multi-threaded variant
|
| 2160 |
+
# remains the default.
|
| 2161 |
+
if self._single_threaded_unary_stream:
|
| 2162 |
+
return _SingleThreadedUnaryStreamMultiCallable(
|
| 2163 |
+
self._channel,
|
| 2164 |
+
_common.encode(method),
|
| 2165 |
+
_common.encode(self._target),
|
| 2166 |
+
request_serializer,
|
| 2167 |
+
response_deserializer,
|
| 2168 |
+
_registered_call_handle,
|
| 2169 |
+
)
|
| 2170 |
+
else:
|
| 2171 |
+
return _UnaryStreamMultiCallable(
|
| 2172 |
+
self._channel,
|
| 2173 |
+
_channel_managed_call_management(self._call_state),
|
| 2174 |
+
_common.encode(method),
|
| 2175 |
+
_common.encode(self._target),
|
| 2176 |
+
request_serializer,
|
| 2177 |
+
response_deserializer,
|
| 2178 |
+
_registered_call_handle,
|
| 2179 |
+
)
|
| 2180 |
+
|
| 2181 |
+
# pylint: disable=arguments-differ
|
| 2182 |
+
def stream_unary(
|
| 2183 |
+
self,
|
| 2184 |
+
method: str,
|
| 2185 |
+
request_serializer: Optional[SerializingFunction] = None,
|
| 2186 |
+
response_deserializer: Optional[DeserializingFunction] = None,
|
| 2187 |
+
_registered_method: Optional[bool] = False,
|
| 2188 |
+
) -> grpc.StreamUnaryMultiCallable:
|
| 2189 |
+
_registered_call_handle = None
|
| 2190 |
+
if _registered_method:
|
| 2191 |
+
_registered_call_handle = self._get_registered_call_handle(method)
|
| 2192 |
+
return _StreamUnaryMultiCallable(
|
| 2193 |
+
self._channel,
|
| 2194 |
+
_channel_managed_call_management(self._call_state),
|
| 2195 |
+
_common.encode(method),
|
| 2196 |
+
_common.encode(self._target),
|
| 2197 |
+
request_serializer,
|
| 2198 |
+
response_deserializer,
|
| 2199 |
+
_registered_call_handle,
|
| 2200 |
+
)
|
| 2201 |
+
|
| 2202 |
+
# pylint: disable=arguments-differ
|
| 2203 |
+
def stream_stream(
|
| 2204 |
+
self,
|
| 2205 |
+
method: str,
|
| 2206 |
+
request_serializer: Optional[SerializingFunction] = None,
|
| 2207 |
+
response_deserializer: Optional[DeserializingFunction] = None,
|
| 2208 |
+
_registered_method: Optional[bool] = False,
|
| 2209 |
+
) -> grpc.StreamStreamMultiCallable:
|
| 2210 |
+
_registered_call_handle = None
|
| 2211 |
+
if _registered_method:
|
| 2212 |
+
_registered_call_handle = self._get_registered_call_handle(method)
|
| 2213 |
+
return _StreamStreamMultiCallable(
|
| 2214 |
+
self._channel,
|
| 2215 |
+
_channel_managed_call_management(self._call_state),
|
| 2216 |
+
_common.encode(method),
|
| 2217 |
+
_common.encode(self._target),
|
| 2218 |
+
request_serializer,
|
| 2219 |
+
response_deserializer,
|
| 2220 |
+
_registered_call_handle,
|
| 2221 |
+
)
|
| 2222 |
+
|
| 2223 |
+
def _unsubscribe_all(self) -> None:
|
| 2224 |
+
state = self._connectivity_state
|
| 2225 |
+
if state:
|
| 2226 |
+
with state.lock:
|
| 2227 |
+
del state.callbacks_and_connectivities[:]
|
| 2228 |
+
|
| 2229 |
+
def _close(self) -> None:
|
| 2230 |
+
self._unsubscribe_all()
|
| 2231 |
+
self._channel.close(cygrpc.StatusCode.cancelled, "Channel closed!")
|
| 2232 |
+
cygrpc.fork_unregister_channel(self)
|
| 2233 |
+
if cygrpc.g_gevent_activated:
|
| 2234 |
+
cygrpc.gevent_decrement_channel_count()
|
| 2235 |
+
|
| 2236 |
+
def _close_on_fork(self) -> None:
|
| 2237 |
+
self._unsubscribe_all()
|
| 2238 |
+
self._channel.close_on_fork(
|
| 2239 |
+
cygrpc.StatusCode.cancelled, "Channel closed due to fork"
|
| 2240 |
+
)
|
| 2241 |
+
|
| 2242 |
+
def __enter__(self):
|
| 2243 |
+
return self
|
| 2244 |
+
|
| 2245 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 2246 |
+
self._close()
|
| 2247 |
+
return False
|
| 2248 |
+
|
| 2249 |
+
def close(self) -> None:
|
| 2250 |
+
self._close()
|
| 2251 |
+
|
| 2252 |
+
def __del__(self):
|
| 2253 |
+
# TODO(https://github.com/grpc/grpc/issues/12531): Several releases
|
| 2254 |
+
# after 1.12 (1.16 or thereabouts?) add a "self._channel.close" call
|
| 2255 |
+
# here (or more likely, call self._close() here). We don't do this today
|
| 2256 |
+
# because many valid use cases today allow the channel to be deleted
|
| 2257 |
+
# immediately after stubs are created. After a sufficient period of time
|
| 2258 |
+
# has passed for all users to be trusted to freeze out to their channels
|
| 2259 |
+
# for as long as they are in use and to close them after using them,
|
| 2260 |
+
# then deletion of this grpc._channel.Channel instance can be made to
|
| 2261 |
+
# effect closure of the underlying cygrpc.Channel instance.
|
| 2262 |
+
try:
|
| 2263 |
+
self._unsubscribe_all()
|
| 2264 |
+
except: # pylint: disable=bare-except
|
| 2265 |
+
# Exceptions in __del__ are ignored by Python anyway, but they can
|
| 2266 |
+
# keep spamming logs. Just silence them.
|
| 2267 |
+
pass
|
.venv/lib/python3.11/site-packages/grpc/_common.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Shared implementation."""
|
| 15 |
+
|
| 16 |
+
import logging
|
| 17 |
+
import time
|
| 18 |
+
from typing import Any, AnyStr, Callable, Optional, Union
|
| 19 |
+
|
| 20 |
+
import grpc
|
| 21 |
+
from grpc._cython import cygrpc
|
| 22 |
+
from grpc._typing import DeserializingFunction
|
| 23 |
+
from grpc._typing import SerializingFunction
|
| 24 |
+
|
| 25 |
+
_LOGGER = logging.getLogger(__name__)
|
| 26 |
+
|
| 27 |
+
CYGRPC_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY = {
|
| 28 |
+
cygrpc.ConnectivityState.idle: grpc.ChannelConnectivity.IDLE,
|
| 29 |
+
cygrpc.ConnectivityState.connecting: grpc.ChannelConnectivity.CONNECTING,
|
| 30 |
+
cygrpc.ConnectivityState.ready: grpc.ChannelConnectivity.READY,
|
| 31 |
+
cygrpc.ConnectivityState.transient_failure: grpc.ChannelConnectivity.TRANSIENT_FAILURE,
|
| 32 |
+
cygrpc.ConnectivityState.shutdown: grpc.ChannelConnectivity.SHUTDOWN,
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
CYGRPC_STATUS_CODE_TO_STATUS_CODE = {
|
| 36 |
+
cygrpc.StatusCode.ok: grpc.StatusCode.OK,
|
| 37 |
+
cygrpc.StatusCode.cancelled: grpc.StatusCode.CANCELLED,
|
| 38 |
+
cygrpc.StatusCode.unknown: grpc.StatusCode.UNKNOWN,
|
| 39 |
+
cygrpc.StatusCode.invalid_argument: grpc.StatusCode.INVALID_ARGUMENT,
|
| 40 |
+
cygrpc.StatusCode.deadline_exceeded: grpc.StatusCode.DEADLINE_EXCEEDED,
|
| 41 |
+
cygrpc.StatusCode.not_found: grpc.StatusCode.NOT_FOUND,
|
| 42 |
+
cygrpc.StatusCode.already_exists: grpc.StatusCode.ALREADY_EXISTS,
|
| 43 |
+
cygrpc.StatusCode.permission_denied: grpc.StatusCode.PERMISSION_DENIED,
|
| 44 |
+
cygrpc.StatusCode.unauthenticated: grpc.StatusCode.UNAUTHENTICATED,
|
| 45 |
+
cygrpc.StatusCode.resource_exhausted: grpc.StatusCode.RESOURCE_EXHAUSTED,
|
| 46 |
+
cygrpc.StatusCode.failed_precondition: grpc.StatusCode.FAILED_PRECONDITION,
|
| 47 |
+
cygrpc.StatusCode.aborted: grpc.StatusCode.ABORTED,
|
| 48 |
+
cygrpc.StatusCode.out_of_range: grpc.StatusCode.OUT_OF_RANGE,
|
| 49 |
+
cygrpc.StatusCode.unimplemented: grpc.StatusCode.UNIMPLEMENTED,
|
| 50 |
+
cygrpc.StatusCode.internal: grpc.StatusCode.INTERNAL,
|
| 51 |
+
cygrpc.StatusCode.unavailable: grpc.StatusCode.UNAVAILABLE,
|
| 52 |
+
cygrpc.StatusCode.data_loss: grpc.StatusCode.DATA_LOSS,
|
| 53 |
+
}
|
| 54 |
+
STATUS_CODE_TO_CYGRPC_STATUS_CODE = {
|
| 55 |
+
grpc_code: cygrpc_code
|
| 56 |
+
for cygrpc_code, grpc_code in CYGRPC_STATUS_CODE_TO_STATUS_CODE.items()
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
MAXIMUM_WAIT_TIMEOUT = 0.1
|
| 60 |
+
|
| 61 |
+
_ERROR_MESSAGE_PORT_BINDING_FAILED = (
|
| 62 |
+
"Failed to bind to address %s; set "
|
| 63 |
+
"GRPC_VERBOSITY=debug environment variable to see detailed error message."
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def encode(s: AnyStr) -> bytes:
|
| 68 |
+
if isinstance(s, bytes):
|
| 69 |
+
return s
|
| 70 |
+
else:
|
| 71 |
+
return s.encode("utf8")
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def decode(b: AnyStr) -> str:
|
| 75 |
+
if isinstance(b, bytes):
|
| 76 |
+
return b.decode("utf-8", "replace")
|
| 77 |
+
return b
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def _transform(
|
| 81 |
+
message: Any,
|
| 82 |
+
transformer: Union[SerializingFunction, DeserializingFunction, None],
|
| 83 |
+
exception_message: str,
|
| 84 |
+
) -> Any:
|
| 85 |
+
if transformer is None:
|
| 86 |
+
return message
|
| 87 |
+
else:
|
| 88 |
+
try:
|
| 89 |
+
return transformer(message)
|
| 90 |
+
except Exception: # pylint: disable=broad-except
|
| 91 |
+
_LOGGER.exception(exception_message)
|
| 92 |
+
return None
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def serialize(message: Any, serializer: Optional[SerializingFunction]) -> bytes:
|
| 96 |
+
return _transform(message, serializer, "Exception serializing message!")
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def deserialize(
|
| 100 |
+
serialized_message: bytes, deserializer: Optional[DeserializingFunction]
|
| 101 |
+
) -> Any:
|
| 102 |
+
return _transform(
|
| 103 |
+
serialized_message, deserializer, "Exception deserializing message!"
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def fully_qualified_method(group: str, method: str) -> str:
|
| 108 |
+
return "/{}/{}".format(group, method)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def _wait_once(
|
| 112 |
+
wait_fn: Callable[..., bool],
|
| 113 |
+
timeout: float,
|
| 114 |
+
spin_cb: Optional[Callable[[], None]],
|
| 115 |
+
):
|
| 116 |
+
wait_fn(timeout=timeout)
|
| 117 |
+
if spin_cb is not None:
|
| 118 |
+
spin_cb()
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def wait(
|
| 122 |
+
wait_fn: Callable[..., bool],
|
| 123 |
+
wait_complete_fn: Callable[[], bool],
|
| 124 |
+
timeout: Optional[float] = None,
|
| 125 |
+
spin_cb: Optional[Callable[[], None]] = None,
|
| 126 |
+
) -> bool:
|
| 127 |
+
"""Blocks waiting for an event without blocking the thread indefinitely.
|
| 128 |
+
|
| 129 |
+
See https://github.com/grpc/grpc/issues/19464 for full context. CPython's
|
| 130 |
+
`threading.Event.wait` and `threading.Condition.wait` methods, if invoked
|
| 131 |
+
without a timeout kwarg, may block the calling thread indefinitely. If the
|
| 132 |
+
call is made from the main thread, this means that signal handlers may not
|
| 133 |
+
run for an arbitrarily long period of time.
|
| 134 |
+
|
| 135 |
+
This wrapper calls the supplied wait function with an arbitrary short
|
| 136 |
+
timeout to ensure that no signal handler has to wait longer than
|
| 137 |
+
MAXIMUM_WAIT_TIMEOUT before executing.
|
| 138 |
+
|
| 139 |
+
Args:
|
| 140 |
+
wait_fn: A callable acceptable a single float-valued kwarg named
|
| 141 |
+
`timeout`. This function is expected to be one of `threading.Event.wait`
|
| 142 |
+
or `threading.Condition.wait`.
|
| 143 |
+
wait_complete_fn: A callable taking no arguments and returning a bool.
|
| 144 |
+
When this function returns true, it indicates that waiting should cease.
|
| 145 |
+
timeout: An optional float-valued number of seconds after which the wait
|
| 146 |
+
should cease.
|
| 147 |
+
spin_cb: An optional Callable taking no arguments and returning nothing.
|
| 148 |
+
This callback will be called on each iteration of the spin. This may be
|
| 149 |
+
used for, e.g. work related to forking.
|
| 150 |
+
|
| 151 |
+
Returns:
|
| 152 |
+
True if a timeout was supplied and it was reached. False otherwise.
|
| 153 |
+
"""
|
| 154 |
+
if timeout is None:
|
| 155 |
+
while not wait_complete_fn():
|
| 156 |
+
_wait_once(wait_fn, MAXIMUM_WAIT_TIMEOUT, spin_cb)
|
| 157 |
+
else:
|
| 158 |
+
end = time.time() + timeout
|
| 159 |
+
while not wait_complete_fn():
|
| 160 |
+
remaining = min(end - time.time(), MAXIMUM_WAIT_TIMEOUT)
|
| 161 |
+
if remaining < 0:
|
| 162 |
+
return True
|
| 163 |
+
_wait_once(wait_fn, remaining, spin_cb)
|
| 164 |
+
return False
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
def validate_port_binding_result(address: str, port: int) -> int:
|
| 168 |
+
"""Validates if the port binding succeed.
|
| 169 |
+
|
| 170 |
+
If the port returned by Core is 0, the binding is failed. However, in that
|
| 171 |
+
case, the Core API doesn't return a detailed failing reason. The best we
|
| 172 |
+
can do is raising an exception to prevent further confusion.
|
| 173 |
+
|
| 174 |
+
Args:
|
| 175 |
+
address: The address string to be bound.
|
| 176 |
+
port: An int returned by core
|
| 177 |
+
"""
|
| 178 |
+
if port == 0:
|
| 179 |
+
# The Core API doesn't return a failure message. The best we can do
|
| 180 |
+
# is raising an exception to prevent further confusion.
|
| 181 |
+
raise RuntimeError(_ERROR_MESSAGE_PORT_BINDING_FAILED % address)
|
| 182 |
+
else:
|
| 183 |
+
return port
|
.venv/lib/python3.11/site-packages/grpc/_compression.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2019 The gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from __future__ import annotations
|
| 16 |
+
|
| 17 |
+
from typing import Optional
|
| 18 |
+
|
| 19 |
+
import grpc
|
| 20 |
+
from grpc._cython import cygrpc
|
| 21 |
+
from grpc._typing import MetadataType
|
| 22 |
+
|
| 23 |
+
NoCompression = cygrpc.CompressionAlgorithm.none
|
| 24 |
+
Deflate = cygrpc.CompressionAlgorithm.deflate
|
| 25 |
+
Gzip = cygrpc.CompressionAlgorithm.gzip
|
| 26 |
+
|
| 27 |
+
_METADATA_STRING_MAPPING = {
|
| 28 |
+
NoCompression: "identity",
|
| 29 |
+
Deflate: "deflate",
|
| 30 |
+
Gzip: "gzip",
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _compression_algorithm_to_metadata_value(
|
| 35 |
+
compression: grpc.Compression,
|
| 36 |
+
) -> str:
|
| 37 |
+
return _METADATA_STRING_MAPPING[compression]
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def compression_algorithm_to_metadata(compression: grpc.Compression):
|
| 41 |
+
return (
|
| 42 |
+
cygrpc.GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY,
|
| 43 |
+
_compression_algorithm_to_metadata_value(compression),
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def create_channel_option(compression: Optional[grpc.Compression]):
|
| 48 |
+
return (
|
| 49 |
+
((cygrpc.GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, int(compression)),)
|
| 50 |
+
if compression
|
| 51 |
+
else ()
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def augment_metadata(
|
| 56 |
+
metadata: Optional[MetadataType], compression: Optional[grpc.Compression]
|
| 57 |
+
):
|
| 58 |
+
if not metadata and not compression:
|
| 59 |
+
return None
|
| 60 |
+
base_metadata = tuple(metadata) if metadata else ()
|
| 61 |
+
compression_metadata = (
|
| 62 |
+
(compression_algorithm_to_metadata(compression),) if compression else ()
|
| 63 |
+
)
|
| 64 |
+
return base_metadata + compression_metadata
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
__all__ = (
|
| 68 |
+
"NoCompression",
|
| 69 |
+
"Deflate",
|
| 70 |
+
"Gzip",
|
| 71 |
+
)
|
.venv/lib/python3.11/site-packages/grpc/_grpcio_metadata.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
__version__ = """1.70.0"""
|
.venv/lib/python3.11/site-packages/grpc/_interceptor.py
ADDED
|
@@ -0,0 +1,813 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Implementation of gRPC Python interceptors."""
|
| 15 |
+
|
| 16 |
+
import collections
|
| 17 |
+
import sys
|
| 18 |
+
import types
|
| 19 |
+
from typing import Any, Callable, Optional, Sequence, Tuple, Union
|
| 20 |
+
|
| 21 |
+
import grpc
|
| 22 |
+
|
| 23 |
+
from ._typing import DeserializingFunction
|
| 24 |
+
from ._typing import DoneCallbackType
|
| 25 |
+
from ._typing import MetadataType
|
| 26 |
+
from ._typing import RequestIterableType
|
| 27 |
+
from ._typing import SerializingFunction
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class _ServicePipeline(object):
|
| 31 |
+
interceptors: Tuple[grpc.ServerInterceptor]
|
| 32 |
+
|
| 33 |
+
def __init__(self, interceptors: Sequence[grpc.ServerInterceptor]):
|
| 34 |
+
self.interceptors = tuple(interceptors)
|
| 35 |
+
|
| 36 |
+
def _continuation(self, thunk: Callable, index: int) -> Callable:
|
| 37 |
+
return lambda context: self._intercept_at(thunk, index, context)
|
| 38 |
+
|
| 39 |
+
def _intercept_at(
|
| 40 |
+
self, thunk: Callable, index: int, context: grpc.HandlerCallDetails
|
| 41 |
+
) -> grpc.RpcMethodHandler:
|
| 42 |
+
if index < len(self.interceptors):
|
| 43 |
+
interceptor = self.interceptors[index]
|
| 44 |
+
thunk = self._continuation(thunk, index + 1)
|
| 45 |
+
return interceptor.intercept_service(thunk, context)
|
| 46 |
+
else:
|
| 47 |
+
return thunk(context)
|
| 48 |
+
|
| 49 |
+
def execute(
|
| 50 |
+
self, thunk: Callable, context: grpc.HandlerCallDetails
|
| 51 |
+
) -> grpc.RpcMethodHandler:
|
| 52 |
+
return self._intercept_at(thunk, 0, context)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def service_pipeline(
|
| 56 |
+
interceptors: Optional[Sequence[grpc.ServerInterceptor]],
|
| 57 |
+
) -> Optional[_ServicePipeline]:
|
| 58 |
+
return _ServicePipeline(interceptors) if interceptors else None
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class _ClientCallDetails(
|
| 62 |
+
collections.namedtuple(
|
| 63 |
+
"_ClientCallDetails",
|
| 64 |
+
(
|
| 65 |
+
"method",
|
| 66 |
+
"timeout",
|
| 67 |
+
"metadata",
|
| 68 |
+
"credentials",
|
| 69 |
+
"wait_for_ready",
|
| 70 |
+
"compression",
|
| 71 |
+
),
|
| 72 |
+
),
|
| 73 |
+
grpc.ClientCallDetails,
|
| 74 |
+
):
|
| 75 |
+
pass
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def _unwrap_client_call_details(
|
| 79 |
+
call_details: grpc.ClientCallDetails,
|
| 80 |
+
default_details: grpc.ClientCallDetails,
|
| 81 |
+
) -> Tuple[
|
| 82 |
+
str, float, MetadataType, grpc.CallCredentials, bool, grpc.Compression
|
| 83 |
+
]:
|
| 84 |
+
try:
|
| 85 |
+
method = call_details.method # pytype: disable=attribute-error
|
| 86 |
+
except AttributeError:
|
| 87 |
+
method = default_details.method # pytype: disable=attribute-error
|
| 88 |
+
|
| 89 |
+
try:
|
| 90 |
+
timeout = call_details.timeout # pytype: disable=attribute-error
|
| 91 |
+
except AttributeError:
|
| 92 |
+
timeout = default_details.timeout # pytype: disable=attribute-error
|
| 93 |
+
|
| 94 |
+
try:
|
| 95 |
+
metadata = call_details.metadata # pytype: disable=attribute-error
|
| 96 |
+
except AttributeError:
|
| 97 |
+
metadata = default_details.metadata # pytype: disable=attribute-error
|
| 98 |
+
|
| 99 |
+
try:
|
| 100 |
+
credentials = (
|
| 101 |
+
call_details.credentials
|
| 102 |
+
) # pytype: disable=attribute-error
|
| 103 |
+
except AttributeError:
|
| 104 |
+
credentials = (
|
| 105 |
+
default_details.credentials
|
| 106 |
+
) # pytype: disable=attribute-error
|
| 107 |
+
|
| 108 |
+
try:
|
| 109 |
+
wait_for_ready = (
|
| 110 |
+
call_details.wait_for_ready
|
| 111 |
+
) # pytype: disable=attribute-error
|
| 112 |
+
except AttributeError:
|
| 113 |
+
wait_for_ready = (
|
| 114 |
+
default_details.wait_for_ready
|
| 115 |
+
) # pytype: disable=attribute-error
|
| 116 |
+
|
| 117 |
+
try:
|
| 118 |
+
compression = (
|
| 119 |
+
call_details.compression
|
| 120 |
+
) # pytype: disable=attribute-error
|
| 121 |
+
except AttributeError:
|
| 122 |
+
compression = (
|
| 123 |
+
default_details.compression
|
| 124 |
+
) # pytype: disable=attribute-error
|
| 125 |
+
|
| 126 |
+
return method, timeout, metadata, credentials, wait_for_ready, compression
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class _FailureOutcome(
|
| 130 |
+
grpc.RpcError, grpc.Future, grpc.Call
|
| 131 |
+
): # pylint: disable=too-many-ancestors
|
| 132 |
+
_exception: Exception
|
| 133 |
+
_traceback: types.TracebackType
|
| 134 |
+
|
| 135 |
+
def __init__(self, exception: Exception, traceback: types.TracebackType):
|
| 136 |
+
super(_FailureOutcome, self).__init__()
|
| 137 |
+
self._exception = exception
|
| 138 |
+
self._traceback = traceback
|
| 139 |
+
|
| 140 |
+
def initial_metadata(self) -> Optional[MetadataType]:
|
| 141 |
+
return None
|
| 142 |
+
|
| 143 |
+
def trailing_metadata(self) -> Optional[MetadataType]:
|
| 144 |
+
return None
|
| 145 |
+
|
| 146 |
+
def code(self) -> Optional[grpc.StatusCode]:
|
| 147 |
+
return grpc.StatusCode.INTERNAL
|
| 148 |
+
|
| 149 |
+
def details(self) -> Optional[str]:
|
| 150 |
+
return "Exception raised while intercepting the RPC"
|
| 151 |
+
|
| 152 |
+
def cancel(self) -> bool:
|
| 153 |
+
return False
|
| 154 |
+
|
| 155 |
+
def cancelled(self) -> bool:
|
| 156 |
+
return False
|
| 157 |
+
|
| 158 |
+
def is_active(self) -> bool:
|
| 159 |
+
return False
|
| 160 |
+
|
| 161 |
+
def time_remaining(self) -> Optional[float]:
|
| 162 |
+
return None
|
| 163 |
+
|
| 164 |
+
def running(self) -> bool:
|
| 165 |
+
return False
|
| 166 |
+
|
| 167 |
+
def done(self) -> bool:
|
| 168 |
+
return True
|
| 169 |
+
|
| 170 |
+
def result(self, ignored_timeout: Optional[float] = None):
|
| 171 |
+
raise self._exception
|
| 172 |
+
|
| 173 |
+
def exception(
|
| 174 |
+
self, ignored_timeout: Optional[float] = None
|
| 175 |
+
) -> Optional[Exception]:
|
| 176 |
+
return self._exception
|
| 177 |
+
|
| 178 |
+
def traceback(
|
| 179 |
+
self, ignored_timeout: Optional[float] = None
|
| 180 |
+
) -> Optional[types.TracebackType]:
|
| 181 |
+
return self._traceback
|
| 182 |
+
|
| 183 |
+
def add_callback(self, unused_callback) -> bool:
|
| 184 |
+
return False
|
| 185 |
+
|
| 186 |
+
def add_done_callback(self, fn: DoneCallbackType) -> None:
|
| 187 |
+
fn(self)
|
| 188 |
+
|
| 189 |
+
def __iter__(self):
|
| 190 |
+
return self
|
| 191 |
+
|
| 192 |
+
def __next__(self):
|
| 193 |
+
raise self._exception
|
| 194 |
+
|
| 195 |
+
def next(self):
|
| 196 |
+
return self.__next__()
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
class _UnaryOutcome(grpc.Call, grpc.Future):
|
| 200 |
+
_response: Any
|
| 201 |
+
_call: grpc.Call
|
| 202 |
+
|
| 203 |
+
def __init__(self, response: Any, call: grpc.Call):
|
| 204 |
+
self._response = response
|
| 205 |
+
self._call = call
|
| 206 |
+
|
| 207 |
+
def initial_metadata(self) -> Optional[MetadataType]:
|
| 208 |
+
return self._call.initial_metadata()
|
| 209 |
+
|
| 210 |
+
def trailing_metadata(self) -> Optional[MetadataType]:
|
| 211 |
+
return self._call.trailing_metadata()
|
| 212 |
+
|
| 213 |
+
def code(self) -> Optional[grpc.StatusCode]:
|
| 214 |
+
return self._call.code()
|
| 215 |
+
|
| 216 |
+
def details(self) -> Optional[str]:
|
| 217 |
+
return self._call.details()
|
| 218 |
+
|
| 219 |
+
def is_active(self) -> bool:
|
| 220 |
+
return self._call.is_active()
|
| 221 |
+
|
| 222 |
+
def time_remaining(self) -> Optional[float]:
|
| 223 |
+
return self._call.time_remaining()
|
| 224 |
+
|
| 225 |
+
def cancel(self) -> bool:
|
| 226 |
+
return self._call.cancel()
|
| 227 |
+
|
| 228 |
+
def add_callback(self, callback) -> bool:
|
| 229 |
+
return self._call.add_callback(callback)
|
| 230 |
+
|
| 231 |
+
def cancelled(self) -> bool:
|
| 232 |
+
return False
|
| 233 |
+
|
| 234 |
+
def running(self) -> bool:
|
| 235 |
+
return False
|
| 236 |
+
|
| 237 |
+
def done(self) -> bool:
|
| 238 |
+
return True
|
| 239 |
+
|
| 240 |
+
def result(self, ignored_timeout: Optional[float] = None):
|
| 241 |
+
return self._response
|
| 242 |
+
|
| 243 |
+
def exception(self, ignored_timeout: Optional[float] = None):
|
| 244 |
+
return None
|
| 245 |
+
|
| 246 |
+
def traceback(self, ignored_timeout: Optional[float] = None):
|
| 247 |
+
return None
|
| 248 |
+
|
| 249 |
+
def add_done_callback(self, fn: DoneCallbackType) -> None:
|
| 250 |
+
fn(self)
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
|
| 254 |
+
_thunk: Callable
|
| 255 |
+
_method: str
|
| 256 |
+
_interceptor: grpc.UnaryUnaryClientInterceptor
|
| 257 |
+
|
| 258 |
+
def __init__(
|
| 259 |
+
self,
|
| 260 |
+
thunk: Callable,
|
| 261 |
+
method: str,
|
| 262 |
+
interceptor: grpc.UnaryUnaryClientInterceptor,
|
| 263 |
+
):
|
| 264 |
+
self._thunk = thunk
|
| 265 |
+
self._method = method
|
| 266 |
+
self._interceptor = interceptor
|
| 267 |
+
|
| 268 |
+
def __call__(
|
| 269 |
+
self,
|
| 270 |
+
request: Any,
|
| 271 |
+
timeout: Optional[float] = None,
|
| 272 |
+
metadata: Optional[MetadataType] = None,
|
| 273 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 274 |
+
wait_for_ready: Optional[bool] = None,
|
| 275 |
+
compression: Optional[grpc.Compression] = None,
|
| 276 |
+
) -> Any:
|
| 277 |
+
response, ignored_call = self._with_call(
|
| 278 |
+
request,
|
| 279 |
+
timeout=timeout,
|
| 280 |
+
metadata=metadata,
|
| 281 |
+
credentials=credentials,
|
| 282 |
+
wait_for_ready=wait_for_ready,
|
| 283 |
+
compression=compression,
|
| 284 |
+
)
|
| 285 |
+
return response
|
| 286 |
+
|
| 287 |
+
def _with_call(
|
| 288 |
+
self,
|
| 289 |
+
request: Any,
|
| 290 |
+
timeout: Optional[float] = None,
|
| 291 |
+
metadata: Optional[MetadataType] = None,
|
| 292 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 293 |
+
wait_for_ready: Optional[bool] = None,
|
| 294 |
+
compression: Optional[grpc.Compression] = None,
|
| 295 |
+
) -> Tuple[Any, grpc.Call]:
|
| 296 |
+
client_call_details = _ClientCallDetails(
|
| 297 |
+
self._method,
|
| 298 |
+
timeout,
|
| 299 |
+
metadata,
|
| 300 |
+
credentials,
|
| 301 |
+
wait_for_ready,
|
| 302 |
+
compression,
|
| 303 |
+
)
|
| 304 |
+
|
| 305 |
+
def continuation(new_details, request):
|
| 306 |
+
(
|
| 307 |
+
new_method,
|
| 308 |
+
new_timeout,
|
| 309 |
+
new_metadata,
|
| 310 |
+
new_credentials,
|
| 311 |
+
new_wait_for_ready,
|
| 312 |
+
new_compression,
|
| 313 |
+
) = _unwrap_client_call_details(new_details, client_call_details)
|
| 314 |
+
try:
|
| 315 |
+
response, call = self._thunk(new_method).with_call(
|
| 316 |
+
request,
|
| 317 |
+
timeout=new_timeout,
|
| 318 |
+
metadata=new_metadata,
|
| 319 |
+
credentials=new_credentials,
|
| 320 |
+
wait_for_ready=new_wait_for_ready,
|
| 321 |
+
compression=new_compression,
|
| 322 |
+
)
|
| 323 |
+
return _UnaryOutcome(response, call)
|
| 324 |
+
except grpc.RpcError as rpc_error:
|
| 325 |
+
return rpc_error
|
| 326 |
+
except Exception as exception: # pylint:disable=broad-except
|
| 327 |
+
return _FailureOutcome(exception, sys.exc_info()[2])
|
| 328 |
+
|
| 329 |
+
call = self._interceptor.intercept_unary_unary(
|
| 330 |
+
continuation, client_call_details, request
|
| 331 |
+
)
|
| 332 |
+
return call.result(), call
|
| 333 |
+
|
| 334 |
+
def with_call(
|
| 335 |
+
self,
|
| 336 |
+
request: Any,
|
| 337 |
+
timeout: Optional[float] = None,
|
| 338 |
+
metadata: Optional[MetadataType] = None,
|
| 339 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 340 |
+
wait_for_ready: Optional[bool] = None,
|
| 341 |
+
compression: Optional[grpc.Compression] = None,
|
| 342 |
+
) -> Tuple[Any, grpc.Call]:
|
| 343 |
+
return self._with_call(
|
| 344 |
+
request,
|
| 345 |
+
timeout=timeout,
|
| 346 |
+
metadata=metadata,
|
| 347 |
+
credentials=credentials,
|
| 348 |
+
wait_for_ready=wait_for_ready,
|
| 349 |
+
compression=compression,
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
def future(
|
| 353 |
+
self,
|
| 354 |
+
request: Any,
|
| 355 |
+
timeout: Optional[float] = None,
|
| 356 |
+
metadata: Optional[MetadataType] = None,
|
| 357 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 358 |
+
wait_for_ready: Optional[bool] = None,
|
| 359 |
+
compression: Optional[grpc.Compression] = None,
|
| 360 |
+
) -> Any:
|
| 361 |
+
client_call_details = _ClientCallDetails(
|
| 362 |
+
self._method,
|
| 363 |
+
timeout,
|
| 364 |
+
metadata,
|
| 365 |
+
credentials,
|
| 366 |
+
wait_for_ready,
|
| 367 |
+
compression,
|
| 368 |
+
)
|
| 369 |
+
|
| 370 |
+
def continuation(new_details, request):
|
| 371 |
+
(
|
| 372 |
+
new_method,
|
| 373 |
+
new_timeout,
|
| 374 |
+
new_metadata,
|
| 375 |
+
new_credentials,
|
| 376 |
+
new_wait_for_ready,
|
| 377 |
+
new_compression,
|
| 378 |
+
) = _unwrap_client_call_details(new_details, client_call_details)
|
| 379 |
+
return self._thunk(new_method).future(
|
| 380 |
+
request,
|
| 381 |
+
timeout=new_timeout,
|
| 382 |
+
metadata=new_metadata,
|
| 383 |
+
credentials=new_credentials,
|
| 384 |
+
wait_for_ready=new_wait_for_ready,
|
| 385 |
+
compression=new_compression,
|
| 386 |
+
)
|
| 387 |
+
|
| 388 |
+
try:
|
| 389 |
+
return self._interceptor.intercept_unary_unary(
|
| 390 |
+
continuation, client_call_details, request
|
| 391 |
+
)
|
| 392 |
+
except Exception as exception: # pylint:disable=broad-except
|
| 393 |
+
return _FailureOutcome(exception, sys.exc_info()[2])
|
| 394 |
+
|
| 395 |
+
|
| 396 |
+
class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
|
| 397 |
+
_thunk: Callable
|
| 398 |
+
_method: str
|
| 399 |
+
_interceptor: grpc.UnaryStreamClientInterceptor
|
| 400 |
+
|
| 401 |
+
def __init__(
|
| 402 |
+
self,
|
| 403 |
+
thunk: Callable,
|
| 404 |
+
method: str,
|
| 405 |
+
interceptor: grpc.UnaryStreamClientInterceptor,
|
| 406 |
+
):
|
| 407 |
+
self._thunk = thunk
|
| 408 |
+
self._method = method
|
| 409 |
+
self._interceptor = interceptor
|
| 410 |
+
|
| 411 |
+
def __call__(
|
| 412 |
+
self,
|
| 413 |
+
request: Any,
|
| 414 |
+
timeout: Optional[float] = None,
|
| 415 |
+
metadata: Optional[MetadataType] = None,
|
| 416 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 417 |
+
wait_for_ready: Optional[bool] = None,
|
| 418 |
+
compression: Optional[grpc.Compression] = None,
|
| 419 |
+
):
|
| 420 |
+
client_call_details = _ClientCallDetails(
|
| 421 |
+
self._method,
|
| 422 |
+
timeout,
|
| 423 |
+
metadata,
|
| 424 |
+
credentials,
|
| 425 |
+
wait_for_ready,
|
| 426 |
+
compression,
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
def continuation(new_details, request):
|
| 430 |
+
(
|
| 431 |
+
new_method,
|
| 432 |
+
new_timeout,
|
| 433 |
+
new_metadata,
|
| 434 |
+
new_credentials,
|
| 435 |
+
new_wait_for_ready,
|
| 436 |
+
new_compression,
|
| 437 |
+
) = _unwrap_client_call_details(new_details, client_call_details)
|
| 438 |
+
return self._thunk(new_method)(
|
| 439 |
+
request,
|
| 440 |
+
timeout=new_timeout,
|
| 441 |
+
metadata=new_metadata,
|
| 442 |
+
credentials=new_credentials,
|
| 443 |
+
wait_for_ready=new_wait_for_ready,
|
| 444 |
+
compression=new_compression,
|
| 445 |
+
)
|
| 446 |
+
|
| 447 |
+
try:
|
| 448 |
+
return self._interceptor.intercept_unary_stream(
|
| 449 |
+
continuation, client_call_details, request
|
| 450 |
+
)
|
| 451 |
+
except Exception as exception: # pylint:disable=broad-except
|
| 452 |
+
return _FailureOutcome(exception, sys.exc_info()[2])
|
| 453 |
+
|
| 454 |
+
|
| 455 |
+
class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
|
| 456 |
+
_thunk: Callable
|
| 457 |
+
_method: str
|
| 458 |
+
_interceptor: grpc.StreamUnaryClientInterceptor
|
| 459 |
+
|
| 460 |
+
def __init__(
|
| 461 |
+
self,
|
| 462 |
+
thunk: Callable,
|
| 463 |
+
method: str,
|
| 464 |
+
interceptor: grpc.StreamUnaryClientInterceptor,
|
| 465 |
+
):
|
| 466 |
+
self._thunk = thunk
|
| 467 |
+
self._method = method
|
| 468 |
+
self._interceptor = interceptor
|
| 469 |
+
|
| 470 |
+
def __call__(
|
| 471 |
+
self,
|
| 472 |
+
request_iterator: RequestIterableType,
|
| 473 |
+
timeout: Optional[float] = None,
|
| 474 |
+
metadata: Optional[MetadataType] = None,
|
| 475 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 476 |
+
wait_for_ready: Optional[bool] = None,
|
| 477 |
+
compression: Optional[grpc.Compression] = None,
|
| 478 |
+
) -> Any:
|
| 479 |
+
response, ignored_call = self._with_call(
|
| 480 |
+
request_iterator,
|
| 481 |
+
timeout=timeout,
|
| 482 |
+
metadata=metadata,
|
| 483 |
+
credentials=credentials,
|
| 484 |
+
wait_for_ready=wait_for_ready,
|
| 485 |
+
compression=compression,
|
| 486 |
+
)
|
| 487 |
+
return response
|
| 488 |
+
|
| 489 |
+
def _with_call(
|
| 490 |
+
self,
|
| 491 |
+
request_iterator: RequestIterableType,
|
| 492 |
+
timeout: Optional[float] = None,
|
| 493 |
+
metadata: Optional[MetadataType] = None,
|
| 494 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 495 |
+
wait_for_ready: Optional[bool] = None,
|
| 496 |
+
compression: Optional[grpc.Compression] = None,
|
| 497 |
+
) -> Tuple[Any, grpc.Call]:
|
| 498 |
+
client_call_details = _ClientCallDetails(
|
| 499 |
+
self._method,
|
| 500 |
+
timeout,
|
| 501 |
+
metadata,
|
| 502 |
+
credentials,
|
| 503 |
+
wait_for_ready,
|
| 504 |
+
compression,
|
| 505 |
+
)
|
| 506 |
+
|
| 507 |
+
def continuation(new_details, request_iterator):
|
| 508 |
+
(
|
| 509 |
+
new_method,
|
| 510 |
+
new_timeout,
|
| 511 |
+
new_metadata,
|
| 512 |
+
new_credentials,
|
| 513 |
+
new_wait_for_ready,
|
| 514 |
+
new_compression,
|
| 515 |
+
) = _unwrap_client_call_details(new_details, client_call_details)
|
| 516 |
+
try:
|
| 517 |
+
response, call = self._thunk(new_method).with_call(
|
| 518 |
+
request_iterator,
|
| 519 |
+
timeout=new_timeout,
|
| 520 |
+
metadata=new_metadata,
|
| 521 |
+
credentials=new_credentials,
|
| 522 |
+
wait_for_ready=new_wait_for_ready,
|
| 523 |
+
compression=new_compression,
|
| 524 |
+
)
|
| 525 |
+
return _UnaryOutcome(response, call)
|
| 526 |
+
except grpc.RpcError as rpc_error:
|
| 527 |
+
return rpc_error
|
| 528 |
+
except Exception as exception: # pylint:disable=broad-except
|
| 529 |
+
return _FailureOutcome(exception, sys.exc_info()[2])
|
| 530 |
+
|
| 531 |
+
call = self._interceptor.intercept_stream_unary(
|
| 532 |
+
continuation, client_call_details, request_iterator
|
| 533 |
+
)
|
| 534 |
+
return call.result(), call
|
| 535 |
+
|
| 536 |
+
def with_call(
|
| 537 |
+
self,
|
| 538 |
+
request_iterator: RequestIterableType,
|
| 539 |
+
timeout: Optional[float] = None,
|
| 540 |
+
metadata: Optional[MetadataType] = None,
|
| 541 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 542 |
+
wait_for_ready: Optional[bool] = None,
|
| 543 |
+
compression: Optional[grpc.Compression] = None,
|
| 544 |
+
) -> Tuple[Any, grpc.Call]:
|
| 545 |
+
return self._with_call(
|
| 546 |
+
request_iterator,
|
| 547 |
+
timeout=timeout,
|
| 548 |
+
metadata=metadata,
|
| 549 |
+
credentials=credentials,
|
| 550 |
+
wait_for_ready=wait_for_ready,
|
| 551 |
+
compression=compression,
|
| 552 |
+
)
|
| 553 |
+
|
| 554 |
+
def future(
|
| 555 |
+
self,
|
| 556 |
+
request_iterator: RequestIterableType,
|
| 557 |
+
timeout: Optional[float] = None,
|
| 558 |
+
metadata: Optional[MetadataType] = None,
|
| 559 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 560 |
+
wait_for_ready: Optional[bool] = None,
|
| 561 |
+
compression: Optional[grpc.Compression] = None,
|
| 562 |
+
) -> Any:
|
| 563 |
+
client_call_details = _ClientCallDetails(
|
| 564 |
+
self._method,
|
| 565 |
+
timeout,
|
| 566 |
+
metadata,
|
| 567 |
+
credentials,
|
| 568 |
+
wait_for_ready,
|
| 569 |
+
compression,
|
| 570 |
+
)
|
| 571 |
+
|
| 572 |
+
def continuation(new_details, request_iterator):
|
| 573 |
+
(
|
| 574 |
+
new_method,
|
| 575 |
+
new_timeout,
|
| 576 |
+
new_metadata,
|
| 577 |
+
new_credentials,
|
| 578 |
+
new_wait_for_ready,
|
| 579 |
+
new_compression,
|
| 580 |
+
) = _unwrap_client_call_details(new_details, client_call_details)
|
| 581 |
+
return self._thunk(new_method).future(
|
| 582 |
+
request_iterator,
|
| 583 |
+
timeout=new_timeout,
|
| 584 |
+
metadata=new_metadata,
|
| 585 |
+
credentials=new_credentials,
|
| 586 |
+
wait_for_ready=new_wait_for_ready,
|
| 587 |
+
compression=new_compression,
|
| 588 |
+
)
|
| 589 |
+
|
| 590 |
+
try:
|
| 591 |
+
return self._interceptor.intercept_stream_unary(
|
| 592 |
+
continuation, client_call_details, request_iterator
|
| 593 |
+
)
|
| 594 |
+
except Exception as exception: # pylint:disable=broad-except
|
| 595 |
+
return _FailureOutcome(exception, sys.exc_info()[2])
|
| 596 |
+
|
| 597 |
+
|
| 598 |
+
class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
|
| 599 |
+
_thunk: Callable
|
| 600 |
+
_method: str
|
| 601 |
+
_interceptor: grpc.StreamStreamClientInterceptor
|
| 602 |
+
|
| 603 |
+
def __init__(
|
| 604 |
+
self,
|
| 605 |
+
thunk: Callable,
|
| 606 |
+
method: str,
|
| 607 |
+
interceptor: grpc.StreamStreamClientInterceptor,
|
| 608 |
+
):
|
| 609 |
+
self._thunk = thunk
|
| 610 |
+
self._method = method
|
| 611 |
+
self._interceptor = interceptor
|
| 612 |
+
|
| 613 |
+
def __call__(
|
| 614 |
+
self,
|
| 615 |
+
request_iterator: RequestIterableType,
|
| 616 |
+
timeout: Optional[float] = None,
|
| 617 |
+
metadata: Optional[MetadataType] = None,
|
| 618 |
+
credentials: Optional[grpc.CallCredentials] = None,
|
| 619 |
+
wait_for_ready: Optional[bool] = None,
|
| 620 |
+
compression: Optional[grpc.Compression] = None,
|
| 621 |
+
):
|
| 622 |
+
client_call_details = _ClientCallDetails(
|
| 623 |
+
self._method,
|
| 624 |
+
timeout,
|
| 625 |
+
metadata,
|
| 626 |
+
credentials,
|
| 627 |
+
wait_for_ready,
|
| 628 |
+
compression,
|
| 629 |
+
)
|
| 630 |
+
|
| 631 |
+
def continuation(new_details, request_iterator):
|
| 632 |
+
(
|
| 633 |
+
new_method,
|
| 634 |
+
new_timeout,
|
| 635 |
+
new_metadata,
|
| 636 |
+
new_credentials,
|
| 637 |
+
new_wait_for_ready,
|
| 638 |
+
new_compression,
|
| 639 |
+
) = _unwrap_client_call_details(new_details, client_call_details)
|
| 640 |
+
return self._thunk(new_method)(
|
| 641 |
+
request_iterator,
|
| 642 |
+
timeout=new_timeout,
|
| 643 |
+
metadata=new_metadata,
|
| 644 |
+
credentials=new_credentials,
|
| 645 |
+
wait_for_ready=new_wait_for_ready,
|
| 646 |
+
compression=new_compression,
|
| 647 |
+
)
|
| 648 |
+
|
| 649 |
+
try:
|
| 650 |
+
return self._interceptor.intercept_stream_stream(
|
| 651 |
+
continuation, client_call_details, request_iterator
|
| 652 |
+
)
|
| 653 |
+
except Exception as exception: # pylint:disable=broad-except
|
| 654 |
+
return _FailureOutcome(exception, sys.exc_info()[2])
|
| 655 |
+
|
| 656 |
+
|
| 657 |
+
class _Channel(grpc.Channel):
|
| 658 |
+
_channel: grpc.Channel
|
| 659 |
+
_interceptor: Union[
|
| 660 |
+
grpc.UnaryUnaryClientInterceptor,
|
| 661 |
+
grpc.UnaryStreamClientInterceptor,
|
| 662 |
+
grpc.StreamStreamClientInterceptor,
|
| 663 |
+
grpc.StreamUnaryClientInterceptor,
|
| 664 |
+
]
|
| 665 |
+
|
| 666 |
+
def __init__(
|
| 667 |
+
self,
|
| 668 |
+
channel: grpc.Channel,
|
| 669 |
+
interceptor: Union[
|
| 670 |
+
grpc.UnaryUnaryClientInterceptor,
|
| 671 |
+
grpc.UnaryStreamClientInterceptor,
|
| 672 |
+
grpc.StreamStreamClientInterceptor,
|
| 673 |
+
grpc.StreamUnaryClientInterceptor,
|
| 674 |
+
],
|
| 675 |
+
):
|
| 676 |
+
self._channel = channel
|
| 677 |
+
self._interceptor = interceptor
|
| 678 |
+
|
| 679 |
+
def subscribe(
|
| 680 |
+
self, callback: Callable, try_to_connect: Optional[bool] = False
|
| 681 |
+
):
|
| 682 |
+
self._channel.subscribe(callback, try_to_connect=try_to_connect)
|
| 683 |
+
|
| 684 |
+
def unsubscribe(self, callback: Callable):
|
| 685 |
+
self._channel.unsubscribe(callback)
|
| 686 |
+
|
| 687 |
+
# pylint: disable=arguments-differ
|
| 688 |
+
def unary_unary(
|
| 689 |
+
self,
|
| 690 |
+
method: str,
|
| 691 |
+
request_serializer: Optional[SerializingFunction] = None,
|
| 692 |
+
response_deserializer: Optional[DeserializingFunction] = None,
|
| 693 |
+
_registered_method: Optional[bool] = False,
|
| 694 |
+
) -> grpc.UnaryUnaryMultiCallable:
|
| 695 |
+
# pytype: disable=wrong-arg-count
|
| 696 |
+
thunk = lambda m: self._channel.unary_unary(
|
| 697 |
+
m,
|
| 698 |
+
request_serializer,
|
| 699 |
+
response_deserializer,
|
| 700 |
+
_registered_method,
|
| 701 |
+
)
|
| 702 |
+
# pytype: enable=wrong-arg-count
|
| 703 |
+
if isinstance(self._interceptor, grpc.UnaryUnaryClientInterceptor):
|
| 704 |
+
return _UnaryUnaryMultiCallable(thunk, method, self._interceptor)
|
| 705 |
+
else:
|
| 706 |
+
return thunk(method)
|
| 707 |
+
|
| 708 |
+
# pylint: disable=arguments-differ
|
| 709 |
+
def unary_stream(
|
| 710 |
+
self,
|
| 711 |
+
method: str,
|
| 712 |
+
request_serializer: Optional[SerializingFunction] = None,
|
| 713 |
+
response_deserializer: Optional[DeserializingFunction] = None,
|
| 714 |
+
_registered_method: Optional[bool] = False,
|
| 715 |
+
) -> grpc.UnaryStreamMultiCallable:
|
| 716 |
+
# pytype: disable=wrong-arg-count
|
| 717 |
+
thunk = lambda m: self._channel.unary_stream(
|
| 718 |
+
m,
|
| 719 |
+
request_serializer,
|
| 720 |
+
response_deserializer,
|
| 721 |
+
_registered_method,
|
| 722 |
+
)
|
| 723 |
+
# pytype: enable=wrong-arg-count
|
| 724 |
+
if isinstance(self._interceptor, grpc.UnaryStreamClientInterceptor):
|
| 725 |
+
return _UnaryStreamMultiCallable(thunk, method, self._interceptor)
|
| 726 |
+
else:
|
| 727 |
+
return thunk(method)
|
| 728 |
+
|
| 729 |
+
# pylint: disable=arguments-differ
|
| 730 |
+
def stream_unary(
|
| 731 |
+
self,
|
| 732 |
+
method: str,
|
| 733 |
+
request_serializer: Optional[SerializingFunction] = None,
|
| 734 |
+
response_deserializer: Optional[DeserializingFunction] = None,
|
| 735 |
+
_registered_method: Optional[bool] = False,
|
| 736 |
+
) -> grpc.StreamUnaryMultiCallable:
|
| 737 |
+
# pytype: disable=wrong-arg-count
|
| 738 |
+
thunk = lambda m: self._channel.stream_unary(
|
| 739 |
+
m,
|
| 740 |
+
request_serializer,
|
| 741 |
+
response_deserializer,
|
| 742 |
+
_registered_method,
|
| 743 |
+
)
|
| 744 |
+
# pytype: enable=wrong-arg-count
|
| 745 |
+
if isinstance(self._interceptor, grpc.StreamUnaryClientInterceptor):
|
| 746 |
+
return _StreamUnaryMultiCallable(thunk, method, self._interceptor)
|
| 747 |
+
else:
|
| 748 |
+
return thunk(method)
|
| 749 |
+
|
| 750 |
+
# pylint: disable=arguments-differ
|
| 751 |
+
def stream_stream(
|
| 752 |
+
self,
|
| 753 |
+
method: str,
|
| 754 |
+
request_serializer: Optional[SerializingFunction] = None,
|
| 755 |
+
response_deserializer: Optional[DeserializingFunction] = None,
|
| 756 |
+
_registered_method: Optional[bool] = False,
|
| 757 |
+
) -> grpc.StreamStreamMultiCallable:
|
| 758 |
+
# pytype: disable=wrong-arg-count
|
| 759 |
+
thunk = lambda m: self._channel.stream_stream(
|
| 760 |
+
m,
|
| 761 |
+
request_serializer,
|
| 762 |
+
response_deserializer,
|
| 763 |
+
_registered_method,
|
| 764 |
+
)
|
| 765 |
+
# pytype: enable=wrong-arg-count
|
| 766 |
+
if isinstance(self._interceptor, grpc.StreamStreamClientInterceptor):
|
| 767 |
+
return _StreamStreamMultiCallable(thunk, method, self._interceptor)
|
| 768 |
+
else:
|
| 769 |
+
return thunk(method)
|
| 770 |
+
|
| 771 |
+
def _close(self):
|
| 772 |
+
self._channel.close()
|
| 773 |
+
|
| 774 |
+
def __enter__(self):
|
| 775 |
+
return self
|
| 776 |
+
|
| 777 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 778 |
+
self._close()
|
| 779 |
+
return False
|
| 780 |
+
|
| 781 |
+
def close(self):
|
| 782 |
+
self._channel.close()
|
| 783 |
+
|
| 784 |
+
|
| 785 |
+
def intercept_channel(
|
| 786 |
+
channel: grpc.Channel,
|
| 787 |
+
*interceptors: Optional[
|
| 788 |
+
Sequence[
|
| 789 |
+
Union[
|
| 790 |
+
grpc.UnaryUnaryClientInterceptor,
|
| 791 |
+
grpc.UnaryStreamClientInterceptor,
|
| 792 |
+
grpc.StreamStreamClientInterceptor,
|
| 793 |
+
grpc.StreamUnaryClientInterceptor,
|
| 794 |
+
]
|
| 795 |
+
]
|
| 796 |
+
],
|
| 797 |
+
) -> grpc.Channel:
|
| 798 |
+
for interceptor in reversed(list(interceptors)):
|
| 799 |
+
if (
|
| 800 |
+
not isinstance(interceptor, grpc.UnaryUnaryClientInterceptor)
|
| 801 |
+
and not isinstance(interceptor, grpc.UnaryStreamClientInterceptor)
|
| 802 |
+
and not isinstance(interceptor, grpc.StreamUnaryClientInterceptor)
|
| 803 |
+
and not isinstance(interceptor, grpc.StreamStreamClientInterceptor)
|
| 804 |
+
):
|
| 805 |
+
raise TypeError(
|
| 806 |
+
"interceptor must be "
|
| 807 |
+
"grpc.UnaryUnaryClientInterceptor or "
|
| 808 |
+
"grpc.UnaryStreamClientInterceptor or "
|
| 809 |
+
"grpc.StreamUnaryClientInterceptor or "
|
| 810 |
+
"grpc.StreamStreamClientInterceptor or "
|
| 811 |
+
)
|
| 812 |
+
channel = _Channel(channel, interceptor)
|
| 813 |
+
return channel
|
.venv/lib/python3.11/site-packages/grpc/_observability.py
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023 The gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from __future__ import annotations
|
| 16 |
+
|
| 17 |
+
import abc
|
| 18 |
+
import contextlib
|
| 19 |
+
import logging
|
| 20 |
+
import threading
|
| 21 |
+
from typing import Any, Generator, Generic, List, Optional, TypeVar
|
| 22 |
+
|
| 23 |
+
from grpc._cython import cygrpc as _cygrpc
|
| 24 |
+
from grpc._typing import ChannelArgumentType
|
| 25 |
+
|
| 26 |
+
_LOGGER = logging.getLogger(__name__)
|
| 27 |
+
|
| 28 |
+
_channel = Any # _channel.py imports this module.
|
| 29 |
+
ClientCallTracerCapsule = TypeVar("ClientCallTracerCapsule")
|
| 30 |
+
ServerCallTracerFactoryCapsule = TypeVar("ServerCallTracerFactoryCapsule")
|
| 31 |
+
|
| 32 |
+
_plugin_lock: threading.RLock = threading.RLock()
|
| 33 |
+
_OBSERVABILITY_PLUGIN: Optional["ObservabilityPlugin"] = None
|
| 34 |
+
_SERVICES_TO_EXCLUDE: List[bytes] = [
|
| 35 |
+
b"google.monitoring.v3.MetricService",
|
| 36 |
+
b"google.devtools.cloudtrace.v2.TraceService",
|
| 37 |
+
]
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class ServerCallTracerFactory:
|
| 41 |
+
"""An encapsulation of a ServerCallTracerFactory.
|
| 42 |
+
|
| 43 |
+
Instances of this class can be passed to a Channel as values for the
|
| 44 |
+
grpc.experimental.server_call_tracer_factory option
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
def __init__(self, address):
|
| 48 |
+
self._address = address
|
| 49 |
+
|
| 50 |
+
def __int__(self):
|
| 51 |
+
return self._address
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class ObservabilityPlugin(
|
| 55 |
+
Generic[ClientCallTracerCapsule, ServerCallTracerFactoryCapsule],
|
| 56 |
+
metaclass=abc.ABCMeta,
|
| 57 |
+
):
|
| 58 |
+
"""Abstract base class for observability plugin.
|
| 59 |
+
|
| 60 |
+
*This is a semi-private class that was intended for the exclusive use of
|
| 61 |
+
the gRPC team.*
|
| 62 |
+
|
| 63 |
+
The ClientCallTracerCapsule and ClientCallTracerCapsule created by this
|
| 64 |
+
plugin should be injected to gRPC core using observability_init at the
|
| 65 |
+
start of a program, before any channels/servers are built.
|
| 66 |
+
|
| 67 |
+
Any future methods added to this interface cannot have the
|
| 68 |
+
@abc.abstractmethod annotation.
|
| 69 |
+
|
| 70 |
+
Attributes:
|
| 71 |
+
_stats_enabled: A bool indicates whether tracing is enabled.
|
| 72 |
+
_tracing_enabled: A bool indicates whether stats(metrics) is enabled.
|
| 73 |
+
_registered_methods: A set which stores the registered method names in
|
| 74 |
+
bytes.
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
_tracing_enabled: bool = False
|
| 78 |
+
_stats_enabled: bool = False
|
| 79 |
+
|
| 80 |
+
@abc.abstractmethod
|
| 81 |
+
def create_client_call_tracer(
|
| 82 |
+
self, method_name: bytes, target: bytes
|
| 83 |
+
) -> ClientCallTracerCapsule:
|
| 84 |
+
"""Creates a ClientCallTracerCapsule.
|
| 85 |
+
|
| 86 |
+
After register the plugin, if tracing or stats is enabled, this method
|
| 87 |
+
will be called after a call was created, the ClientCallTracer created
|
| 88 |
+
by this method will be saved to call context.
|
| 89 |
+
|
| 90 |
+
The ClientCallTracer is an object which implements `grpc_core::ClientCallTracer`
|
| 91 |
+
interface and wrapped in a PyCapsule using `client_call_tracer` as name.
|
| 92 |
+
|
| 93 |
+
Args:
|
| 94 |
+
method_name: The method name of the call in byte format.
|
| 95 |
+
target: The channel target of the call in byte format.
|
| 96 |
+
registered_method: Whether this method is pre-registered.
|
| 97 |
+
|
| 98 |
+
Returns:
|
| 99 |
+
A PyCapsule which stores a ClientCallTracer object.
|
| 100 |
+
"""
|
| 101 |
+
raise NotImplementedError()
|
| 102 |
+
|
| 103 |
+
@abc.abstractmethod
|
| 104 |
+
def save_trace_context(
|
| 105 |
+
self, trace_id: str, span_id: str, is_sampled: bool
|
| 106 |
+
) -> None:
|
| 107 |
+
"""Saves the trace_id and span_id related to the current span.
|
| 108 |
+
|
| 109 |
+
After register the plugin, if tracing is enabled, this method will be
|
| 110 |
+
called after the server finished sending response.
|
| 111 |
+
|
| 112 |
+
This method can be used to propagate census context.
|
| 113 |
+
|
| 114 |
+
Args:
|
| 115 |
+
trace_id: The identifier for the trace associated with the span as a
|
| 116 |
+
32-character hexadecimal encoded string,
|
| 117 |
+
e.g. 26ed0036f2eff2b7317bccce3e28d01f
|
| 118 |
+
span_id: The identifier for the span as a 16-character hexadecimal encoded
|
| 119 |
+
string. e.g. 113ec879e62583bc
|
| 120 |
+
is_sampled: A bool indicates whether the span is sampled.
|
| 121 |
+
"""
|
| 122 |
+
raise NotImplementedError()
|
| 123 |
+
|
| 124 |
+
@abc.abstractmethod
|
| 125 |
+
def create_server_call_tracer_factory(
|
| 126 |
+
self,
|
| 127 |
+
*,
|
| 128 |
+
xds: bool = False,
|
| 129 |
+
) -> Optional[ServerCallTracerFactoryCapsule]:
|
| 130 |
+
"""Creates a ServerCallTracerFactoryCapsule.
|
| 131 |
+
|
| 132 |
+
This method will be called at server initialization time to create a
|
| 133 |
+
ServerCallTracerFactory, which will be registered to gRPC core.
|
| 134 |
+
|
| 135 |
+
The ServerCallTracerFactory is an object which implements
|
| 136 |
+
`grpc_core::ServerCallTracerFactory` interface and wrapped in a PyCapsule
|
| 137 |
+
using `server_call_tracer_factory` as name.
|
| 138 |
+
|
| 139 |
+
Args:
|
| 140 |
+
xds: Whether the server is xds server.
|
| 141 |
+
Returns:
|
| 142 |
+
A PyCapsule which stores a ServerCallTracerFactory object. Or None if
|
| 143 |
+
plugin decides not to create ServerCallTracerFactory.
|
| 144 |
+
"""
|
| 145 |
+
raise NotImplementedError()
|
| 146 |
+
|
| 147 |
+
@abc.abstractmethod
|
| 148 |
+
def record_rpc_latency(
|
| 149 |
+
self, method: str, target: str, rpc_latency: float, status_code: Any
|
| 150 |
+
) -> None:
|
| 151 |
+
"""Record the latency of the RPC.
|
| 152 |
+
|
| 153 |
+
After register the plugin, if stats is enabled, this method will be
|
| 154 |
+
called at the end of each RPC.
|
| 155 |
+
|
| 156 |
+
Args:
|
| 157 |
+
method: The fully-qualified name of the RPC method being invoked.
|
| 158 |
+
target: The target name of the RPC method being invoked.
|
| 159 |
+
rpc_latency: The latency for the RPC in seconds, equals to the time between
|
| 160 |
+
when the client invokes the RPC and when the client receives the status.
|
| 161 |
+
status_code: An element of grpc.StatusCode in string format representing the
|
| 162 |
+
final status for the RPC.
|
| 163 |
+
"""
|
| 164 |
+
raise NotImplementedError()
|
| 165 |
+
|
| 166 |
+
def set_tracing(self, enable: bool) -> None:
|
| 167 |
+
"""Enable or disable tracing.
|
| 168 |
+
|
| 169 |
+
Args:
|
| 170 |
+
enable: A bool indicates whether tracing should be enabled.
|
| 171 |
+
"""
|
| 172 |
+
self._tracing_enabled = enable
|
| 173 |
+
|
| 174 |
+
def set_stats(self, enable: bool) -> None:
|
| 175 |
+
"""Enable or disable stats(metrics).
|
| 176 |
+
|
| 177 |
+
Args:
|
| 178 |
+
enable: A bool indicates whether stats should be enabled.
|
| 179 |
+
"""
|
| 180 |
+
self._stats_enabled = enable
|
| 181 |
+
|
| 182 |
+
def save_registered_method(self, method_name: bytes) -> None:
|
| 183 |
+
"""Saves the method name to registered_method list.
|
| 184 |
+
|
| 185 |
+
When exporting metrics, method name for unregistered methods will be replaced
|
| 186 |
+
with 'other' by default.
|
| 187 |
+
|
| 188 |
+
Args:
|
| 189 |
+
method_name: The method name in bytes.
|
| 190 |
+
"""
|
| 191 |
+
raise NotImplementedError()
|
| 192 |
+
|
| 193 |
+
@property
|
| 194 |
+
def tracing_enabled(self) -> bool:
|
| 195 |
+
return self._tracing_enabled
|
| 196 |
+
|
| 197 |
+
@property
|
| 198 |
+
def stats_enabled(self) -> bool:
|
| 199 |
+
return self._stats_enabled
|
| 200 |
+
|
| 201 |
+
@property
|
| 202 |
+
def observability_enabled(self) -> bool:
|
| 203 |
+
return self.tracing_enabled or self.stats_enabled
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
@contextlib.contextmanager
|
| 207 |
+
def get_plugin() -> Generator[Optional[ObservabilityPlugin], None, None]:
|
| 208 |
+
"""Get the ObservabilityPlugin in _observability module.
|
| 209 |
+
|
| 210 |
+
Returns:
|
| 211 |
+
The ObservabilityPlugin currently registered with the _observability
|
| 212 |
+
module. Or None if no plugin exists at the time of calling this method.
|
| 213 |
+
"""
|
| 214 |
+
with _plugin_lock:
|
| 215 |
+
yield _OBSERVABILITY_PLUGIN
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def set_plugin(observability_plugin: Optional[ObservabilityPlugin]) -> None:
|
| 219 |
+
"""Save ObservabilityPlugin to _observability module.
|
| 220 |
+
|
| 221 |
+
Args:
|
| 222 |
+
observability_plugin: The ObservabilityPlugin to save.
|
| 223 |
+
|
| 224 |
+
Raises:
|
| 225 |
+
ValueError: If an ObservabilityPlugin was already registered at the
|
| 226 |
+
time of calling this method.
|
| 227 |
+
"""
|
| 228 |
+
global _OBSERVABILITY_PLUGIN # pylint: disable=global-statement
|
| 229 |
+
with _plugin_lock:
|
| 230 |
+
if observability_plugin and _OBSERVABILITY_PLUGIN:
|
| 231 |
+
raise ValueError("observability_plugin was already set!")
|
| 232 |
+
_OBSERVABILITY_PLUGIN = observability_plugin
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
def observability_init(observability_plugin: ObservabilityPlugin) -> None:
|
| 236 |
+
"""Initialize observability with provided ObservabilityPlugin.
|
| 237 |
+
|
| 238 |
+
This method have to be called at the start of a program, before any
|
| 239 |
+
channels/servers are built.
|
| 240 |
+
|
| 241 |
+
Args:
|
| 242 |
+
observability_plugin: The ObservabilityPlugin to use.
|
| 243 |
+
|
| 244 |
+
Raises:
|
| 245 |
+
ValueError: If an ObservabilityPlugin was already registered at the
|
| 246 |
+
time of calling this method.
|
| 247 |
+
"""
|
| 248 |
+
set_plugin(observability_plugin)
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
def observability_deinit() -> None:
|
| 252 |
+
"""Clear the observability context, including ObservabilityPlugin and
|
| 253 |
+
ServerCallTracerFactory
|
| 254 |
+
|
| 255 |
+
This method have to be called after exit observability context so that
|
| 256 |
+
it's possible to re-initialize again.
|
| 257 |
+
"""
|
| 258 |
+
set_plugin(None)
|
| 259 |
+
_cygrpc.clear_server_call_tracer_factory()
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
def maybe_record_rpc_latency(state: "_channel._RPCState") -> None:
|
| 263 |
+
"""Record the latency of the RPC, if the plugin is registered and stats is enabled.
|
| 264 |
+
|
| 265 |
+
This method will be called at the end of each RPC.
|
| 266 |
+
|
| 267 |
+
Args:
|
| 268 |
+
state: a grpc._channel._RPCState object which contains the stats related to the
|
| 269 |
+
RPC.
|
| 270 |
+
"""
|
| 271 |
+
# TODO(xuanwn): use channel args to exclude those metrics.
|
| 272 |
+
for exclude_prefix in _SERVICES_TO_EXCLUDE:
|
| 273 |
+
if exclude_prefix in state.method.encode("utf8"):
|
| 274 |
+
return
|
| 275 |
+
with get_plugin() as plugin:
|
| 276 |
+
if plugin and plugin.stats_enabled:
|
| 277 |
+
rpc_latency_s = state.rpc_end_time - state.rpc_start_time
|
| 278 |
+
rpc_latency_ms = rpc_latency_s * 1000
|
| 279 |
+
plugin.record_rpc_latency(
|
| 280 |
+
state.method, state.target, rpc_latency_ms, state.code
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
def create_server_call_tracer_factory_option(xds: bool) -> ChannelArgumentType:
|
| 285 |
+
with get_plugin() as plugin:
|
| 286 |
+
if plugin and plugin.stats_enabled:
|
| 287 |
+
server_call_tracer_factory_address = (
|
| 288 |
+
_cygrpc.get_server_call_tracer_factory_address(plugin, xds)
|
| 289 |
+
)
|
| 290 |
+
if server_call_tracer_factory_address:
|
| 291 |
+
return (
|
| 292 |
+
(
|
| 293 |
+
"grpc.experimental.server_call_tracer_factory",
|
| 294 |
+
ServerCallTracerFactory(
|
| 295 |
+
server_call_tracer_factory_address
|
| 296 |
+
),
|
| 297 |
+
),
|
| 298 |
+
)
|
| 299 |
+
return ()
|
.venv/lib/python3.11/site-packages/grpc/_plugin_wrapping.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import collections
|
| 16 |
+
import logging
|
| 17 |
+
import threading
|
| 18 |
+
from typing import Callable, Optional, Type
|
| 19 |
+
|
| 20 |
+
import grpc
|
| 21 |
+
from grpc import _common
|
| 22 |
+
from grpc._cython import cygrpc
|
| 23 |
+
from grpc._typing import MetadataType
|
| 24 |
+
|
| 25 |
+
_LOGGER = logging.getLogger(__name__)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class _AuthMetadataContext(
|
| 29 |
+
collections.namedtuple(
|
| 30 |
+
"AuthMetadataContext",
|
| 31 |
+
(
|
| 32 |
+
"service_url",
|
| 33 |
+
"method_name",
|
| 34 |
+
),
|
| 35 |
+
),
|
| 36 |
+
grpc.AuthMetadataContext,
|
| 37 |
+
):
|
| 38 |
+
pass
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class _CallbackState(object):
|
| 42 |
+
def __init__(self):
|
| 43 |
+
self.lock = threading.Lock()
|
| 44 |
+
self.called = False
|
| 45 |
+
self.exception = None
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class _AuthMetadataPluginCallback(grpc.AuthMetadataPluginCallback):
|
| 49 |
+
_state: _CallbackState
|
| 50 |
+
_callback: Callable
|
| 51 |
+
|
| 52 |
+
def __init__(self, state: _CallbackState, callback: Callable):
|
| 53 |
+
self._state = state
|
| 54 |
+
self._callback = callback
|
| 55 |
+
|
| 56 |
+
def __call__(
|
| 57 |
+
self, metadata: MetadataType, error: Optional[Type[BaseException]]
|
| 58 |
+
):
|
| 59 |
+
with self._state.lock:
|
| 60 |
+
if self._state.exception is None:
|
| 61 |
+
if self._state.called:
|
| 62 |
+
raise RuntimeError(
|
| 63 |
+
"AuthMetadataPluginCallback invoked more than once!"
|
| 64 |
+
)
|
| 65 |
+
else:
|
| 66 |
+
self._state.called = True
|
| 67 |
+
else:
|
| 68 |
+
raise RuntimeError(
|
| 69 |
+
'AuthMetadataPluginCallback raised exception "{}"!'.format(
|
| 70 |
+
self._state.exception
|
| 71 |
+
)
|
| 72 |
+
)
|
| 73 |
+
if error is None:
|
| 74 |
+
self._callback(metadata, cygrpc.StatusCode.ok, None)
|
| 75 |
+
else:
|
| 76 |
+
self._callback(
|
| 77 |
+
None, cygrpc.StatusCode.internal, _common.encode(str(error))
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class _Plugin(object):
|
| 82 |
+
_metadata_plugin: grpc.AuthMetadataPlugin
|
| 83 |
+
|
| 84 |
+
def __init__(self, metadata_plugin: grpc.AuthMetadataPlugin):
|
| 85 |
+
self._metadata_plugin = metadata_plugin
|
| 86 |
+
self._stored_ctx = None
|
| 87 |
+
|
| 88 |
+
try:
|
| 89 |
+
import contextvars # pylint: disable=wrong-import-position
|
| 90 |
+
|
| 91 |
+
# The plugin may be invoked on a thread created by Core, which will not
|
| 92 |
+
# have the context propagated. This context is stored and installed in
|
| 93 |
+
# the thread invoking the plugin.
|
| 94 |
+
self._stored_ctx = contextvars.copy_context()
|
| 95 |
+
except ImportError:
|
| 96 |
+
# Support versions predating contextvars.
|
| 97 |
+
pass
|
| 98 |
+
|
| 99 |
+
def __call__(self, service_url: str, method_name: str, callback: Callable):
|
| 100 |
+
context = _AuthMetadataContext(
|
| 101 |
+
_common.decode(service_url), _common.decode(method_name)
|
| 102 |
+
)
|
| 103 |
+
callback_state = _CallbackState()
|
| 104 |
+
try:
|
| 105 |
+
self._metadata_plugin(
|
| 106 |
+
context, _AuthMetadataPluginCallback(callback_state, callback)
|
| 107 |
+
)
|
| 108 |
+
except Exception as exception: # pylint: disable=broad-except
|
| 109 |
+
_LOGGER.exception(
|
| 110 |
+
'AuthMetadataPluginCallback "%s" raised exception!',
|
| 111 |
+
self._metadata_plugin,
|
| 112 |
+
)
|
| 113 |
+
with callback_state.lock:
|
| 114 |
+
callback_state.exception = exception
|
| 115 |
+
if callback_state.called:
|
| 116 |
+
return
|
| 117 |
+
callback(
|
| 118 |
+
None, cygrpc.StatusCode.internal, _common.encode(str(exception))
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def metadata_plugin_call_credentials(
|
| 123 |
+
metadata_plugin: grpc.AuthMetadataPlugin, name: Optional[str]
|
| 124 |
+
) -> grpc.CallCredentials:
|
| 125 |
+
if name is None:
|
| 126 |
+
try:
|
| 127 |
+
effective_name = metadata_plugin.__name__
|
| 128 |
+
except AttributeError:
|
| 129 |
+
effective_name = metadata_plugin.__class__.__name__
|
| 130 |
+
else:
|
| 131 |
+
effective_name = name
|
| 132 |
+
return grpc.CallCredentials(
|
| 133 |
+
cygrpc.MetadataPluginCallCredentials(
|
| 134 |
+
_Plugin(metadata_plugin), _common.encode(effective_name)
|
| 135 |
+
)
|
| 136 |
+
)
|
.venv/lib/python3.11/site-packages/grpc/_runtime_protos.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import sys
|
| 16 |
+
import types
|
| 17 |
+
from typing import Tuple, Union
|
| 18 |
+
|
| 19 |
+
_REQUIRED_SYMBOLS = ("_protos", "_services", "_protos_and_services")
|
| 20 |
+
_MINIMUM_VERSION = (3, 5, 0)
|
| 21 |
+
|
| 22 |
+
_UNINSTALLED_TEMPLATE = (
|
| 23 |
+
"Install the grpcio-tools package (1.32.0+) to use the {} function."
|
| 24 |
+
)
|
| 25 |
+
_VERSION_ERROR_TEMPLATE = (
|
| 26 |
+
"The {} function is only on available on Python 3.X interpreters."
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def _has_runtime_proto_symbols(mod: types.ModuleType) -> bool:
|
| 31 |
+
return all(hasattr(mod, sym) for sym in _REQUIRED_SYMBOLS)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _is_grpc_tools_importable() -> bool:
|
| 35 |
+
try:
|
| 36 |
+
import grpc_tools # pylint: disable=unused-import # pytype: disable=import-error
|
| 37 |
+
|
| 38 |
+
return True
|
| 39 |
+
except ImportError as e:
|
| 40 |
+
# NOTE: It's possible that we're encountering a transitive ImportError, so
|
| 41 |
+
# we check for that and re-raise if so.
|
| 42 |
+
if "grpc_tools" not in e.args[0]:
|
| 43 |
+
raise
|
| 44 |
+
return False
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def _call_with_lazy_import(
|
| 48 |
+
fn_name: str, protobuf_path: str
|
| 49 |
+
) -> Union[types.ModuleType, Tuple[types.ModuleType, types.ModuleType]]:
|
| 50 |
+
"""Calls one of the three functions, lazily importing grpc_tools.
|
| 51 |
+
|
| 52 |
+
Args:
|
| 53 |
+
fn_name: The name of the function to import from grpc_tools.protoc.
|
| 54 |
+
protobuf_path: The path to import.
|
| 55 |
+
|
| 56 |
+
Returns:
|
| 57 |
+
The appropriate module object.
|
| 58 |
+
"""
|
| 59 |
+
if sys.version_info < _MINIMUM_VERSION:
|
| 60 |
+
raise NotImplementedError(_VERSION_ERROR_TEMPLATE.format(fn_name))
|
| 61 |
+
else:
|
| 62 |
+
if not _is_grpc_tools_importable():
|
| 63 |
+
raise NotImplementedError(_UNINSTALLED_TEMPLATE.format(fn_name))
|
| 64 |
+
import grpc_tools.protoc # pytype: disable=import-error
|
| 65 |
+
|
| 66 |
+
if _has_runtime_proto_symbols(grpc_tools.protoc):
|
| 67 |
+
fn = getattr(grpc_tools.protoc, "_" + fn_name)
|
| 68 |
+
return fn(protobuf_path)
|
| 69 |
+
else:
|
| 70 |
+
raise NotImplementedError(_UNINSTALLED_TEMPLATE.format(fn_name))
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def protos(protobuf_path): # pylint: disable=unused-argument
|
| 74 |
+
"""Returns a module generated by the indicated .proto file.
|
| 75 |
+
|
| 76 |
+
THIS IS AN EXPERIMENTAL API.
|
| 77 |
+
|
| 78 |
+
Use this function to retrieve classes corresponding to message
|
| 79 |
+
definitions in the .proto file.
|
| 80 |
+
|
| 81 |
+
To inspect the contents of the returned module, use the dir function.
|
| 82 |
+
For example:
|
| 83 |
+
|
| 84 |
+
```
|
| 85 |
+
protos = grpc.protos("foo.proto")
|
| 86 |
+
print(dir(protos))
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
The returned module object corresponds to the _pb2.py file generated
|
| 90 |
+
by protoc. The path is expected to be relative to an entry on sys.path
|
| 91 |
+
and all transitive dependencies of the file should also be resolvable
|
| 92 |
+
from an entry on sys.path.
|
| 93 |
+
|
| 94 |
+
To completely disable the machinery behind this function, set the
|
| 95 |
+
GRPC_PYTHON_DISABLE_DYNAMIC_STUBS environment variable to "true".
|
| 96 |
+
|
| 97 |
+
Args:
|
| 98 |
+
protobuf_path: The path to the .proto file on the filesystem. This path
|
| 99 |
+
must be resolvable from an entry on sys.path and so must all of its
|
| 100 |
+
transitive dependencies.
|
| 101 |
+
|
| 102 |
+
Returns:
|
| 103 |
+
A module object corresponding to the message code for the indicated
|
| 104 |
+
.proto file. Equivalent to a generated _pb2.py file.
|
| 105 |
+
"""
|
| 106 |
+
return _call_with_lazy_import("protos", protobuf_path)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def services(protobuf_path): # pylint: disable=unused-argument
|
| 110 |
+
"""Returns a module generated by the indicated .proto file.
|
| 111 |
+
|
| 112 |
+
THIS IS AN EXPERIMENTAL API.
|
| 113 |
+
|
| 114 |
+
Use this function to retrieve classes and functions corresponding to
|
| 115 |
+
service definitions in the .proto file, including both stub and servicer
|
| 116 |
+
definitions.
|
| 117 |
+
|
| 118 |
+
To inspect the contents of the returned module, use the dir function.
|
| 119 |
+
For example:
|
| 120 |
+
|
| 121 |
+
```
|
| 122 |
+
services = grpc.services("foo.proto")
|
| 123 |
+
print(dir(services))
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
The returned module object corresponds to the _pb2_grpc.py file generated
|
| 127 |
+
by protoc. The path is expected to be relative to an entry on sys.path
|
| 128 |
+
and all transitive dependencies of the file should also be resolvable
|
| 129 |
+
from an entry on sys.path.
|
| 130 |
+
|
| 131 |
+
To completely disable the machinery behind this function, set the
|
| 132 |
+
GRPC_PYTHON_DISABLE_DYNAMIC_STUBS environment variable to "true".
|
| 133 |
+
|
| 134 |
+
Args:
|
| 135 |
+
protobuf_path: The path to the .proto file on the filesystem. This path
|
| 136 |
+
must be resolvable from an entry on sys.path and so must all of its
|
| 137 |
+
transitive dependencies.
|
| 138 |
+
|
| 139 |
+
Returns:
|
| 140 |
+
A module object corresponding to the stub/service code for the indicated
|
| 141 |
+
.proto file. Equivalent to a generated _pb2_grpc.py file.
|
| 142 |
+
"""
|
| 143 |
+
return _call_with_lazy_import("services", protobuf_path)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def protos_and_services(protobuf_path): # pylint: disable=unused-argument
|
| 147 |
+
"""Returns a 2-tuple of modules corresponding to protos and services.
|
| 148 |
+
|
| 149 |
+
THIS IS AN EXPERIMENTAL API.
|
| 150 |
+
|
| 151 |
+
The return value of this function is equivalent to a call to protos and a
|
| 152 |
+
call to services.
|
| 153 |
+
|
| 154 |
+
To completely disable the machinery behind this function, set the
|
| 155 |
+
GRPC_PYTHON_DISABLE_DYNAMIC_STUBS environment variable to "true".
|
| 156 |
+
|
| 157 |
+
Args:
|
| 158 |
+
protobuf_path: The path to the .proto file on the filesystem. This path
|
| 159 |
+
must be resolvable from an entry on sys.path and so must all of its
|
| 160 |
+
transitive dependencies.
|
| 161 |
+
|
| 162 |
+
Returns:
|
| 163 |
+
A 2-tuple of module objects corresponding to (protos(path), services(path)).
|
| 164 |
+
"""
|
| 165 |
+
return _call_with_lazy_import("protos_and_services", protobuf_path)
|
.venv/lib/python3.11/site-packages/grpc/_server.py
ADDED
|
@@ -0,0 +1,1528 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Service-side implementation of gRPC Python."""
|
| 15 |
+
|
| 16 |
+
from __future__ import annotations
|
| 17 |
+
|
| 18 |
+
import abc
|
| 19 |
+
import collections
|
| 20 |
+
from concurrent import futures
|
| 21 |
+
import contextvars
|
| 22 |
+
import enum
|
| 23 |
+
import logging
|
| 24 |
+
import threading
|
| 25 |
+
import time
|
| 26 |
+
import traceback
|
| 27 |
+
from typing import (
|
| 28 |
+
Any,
|
| 29 |
+
Callable,
|
| 30 |
+
Dict,
|
| 31 |
+
Iterable,
|
| 32 |
+
Iterator,
|
| 33 |
+
List,
|
| 34 |
+
Mapping,
|
| 35 |
+
Optional,
|
| 36 |
+
Sequence,
|
| 37 |
+
Set,
|
| 38 |
+
Tuple,
|
| 39 |
+
Union,
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
import grpc # pytype: disable=pyi-error
|
| 43 |
+
from grpc import _common # pytype: disable=pyi-error
|
| 44 |
+
from grpc import _compression # pytype: disable=pyi-error
|
| 45 |
+
from grpc import _interceptor # pytype: disable=pyi-error
|
| 46 |
+
from grpc import _observability # pytype: disable=pyi-error
|
| 47 |
+
from grpc._cython import cygrpc
|
| 48 |
+
from grpc._typing import ArityAgnosticMethodHandler
|
| 49 |
+
from grpc._typing import ChannelArgumentType
|
| 50 |
+
from grpc._typing import DeserializingFunction
|
| 51 |
+
from grpc._typing import MetadataType
|
| 52 |
+
from grpc._typing import NullaryCallbackType
|
| 53 |
+
from grpc._typing import ResponseType
|
| 54 |
+
from grpc._typing import SerializingFunction
|
| 55 |
+
from grpc._typing import ServerCallbackTag
|
| 56 |
+
from grpc._typing import ServerTagCallbackType
|
| 57 |
+
|
| 58 |
+
_LOGGER = logging.getLogger(__name__)
|
| 59 |
+
|
| 60 |
+
_SHUTDOWN_TAG = "shutdown"
|
| 61 |
+
_REQUEST_CALL_TAG = "request_call"
|
| 62 |
+
|
| 63 |
+
_RECEIVE_CLOSE_ON_SERVER_TOKEN = "receive_close_on_server"
|
| 64 |
+
_SEND_INITIAL_METADATA_TOKEN = "send_initial_metadata"
|
| 65 |
+
_RECEIVE_MESSAGE_TOKEN = "receive_message"
|
| 66 |
+
_SEND_MESSAGE_TOKEN = "send_message"
|
| 67 |
+
_SEND_INITIAL_METADATA_AND_SEND_MESSAGE_TOKEN = (
|
| 68 |
+
"send_initial_metadata * send_message"
|
| 69 |
+
)
|
| 70 |
+
_SEND_STATUS_FROM_SERVER_TOKEN = "send_status_from_server"
|
| 71 |
+
_SEND_INITIAL_METADATA_AND_SEND_STATUS_FROM_SERVER_TOKEN = (
|
| 72 |
+
"send_initial_metadata * send_status_from_server"
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
_OPEN = "open"
|
| 76 |
+
_CLOSED = "closed"
|
| 77 |
+
_CANCELLED = "cancelled"
|
| 78 |
+
|
| 79 |
+
_EMPTY_FLAGS = 0
|
| 80 |
+
|
| 81 |
+
_DEALLOCATED_SERVER_CHECK_PERIOD_S = 1.0
|
| 82 |
+
_INF_TIMEOUT = 1e9
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def _serialized_request(request_event: cygrpc.BaseEvent) -> bytes:
|
| 86 |
+
return request_event.batch_operations[0].message()
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def _application_code(code: grpc.StatusCode) -> cygrpc.StatusCode:
|
| 90 |
+
cygrpc_code = _common.STATUS_CODE_TO_CYGRPC_STATUS_CODE.get(code)
|
| 91 |
+
return cygrpc.StatusCode.unknown if cygrpc_code is None else cygrpc_code
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def _completion_code(state: _RPCState) -> cygrpc.StatusCode:
|
| 95 |
+
if state.code is None:
|
| 96 |
+
return cygrpc.StatusCode.ok
|
| 97 |
+
else:
|
| 98 |
+
return _application_code(state.code)
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def _abortion_code(
|
| 102 |
+
state: _RPCState, code: cygrpc.StatusCode
|
| 103 |
+
) -> cygrpc.StatusCode:
|
| 104 |
+
if state.code is None:
|
| 105 |
+
return code
|
| 106 |
+
else:
|
| 107 |
+
return _application_code(state.code)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def _details(state: _RPCState) -> bytes:
|
| 111 |
+
return b"" if state.details is None else state.details
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
class _HandlerCallDetails(
|
| 115 |
+
collections.namedtuple(
|
| 116 |
+
"_HandlerCallDetails",
|
| 117 |
+
(
|
| 118 |
+
"method",
|
| 119 |
+
"invocation_metadata",
|
| 120 |
+
),
|
| 121 |
+
),
|
| 122 |
+
grpc.HandlerCallDetails,
|
| 123 |
+
):
|
| 124 |
+
pass
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
class _Method(abc.ABC):
|
| 128 |
+
@abc.abstractmethod
|
| 129 |
+
def name(self) -> Optional[str]:
|
| 130 |
+
raise NotImplementedError()
|
| 131 |
+
|
| 132 |
+
@abc.abstractmethod
|
| 133 |
+
def handler(
|
| 134 |
+
self, handler_call_details: _HandlerCallDetails
|
| 135 |
+
) -> Optional[grpc.RpcMethodHandler]:
|
| 136 |
+
raise NotImplementedError()
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
class _RegisteredMethod(_Method):
|
| 140 |
+
def __init__(
|
| 141 |
+
self,
|
| 142 |
+
name: str,
|
| 143 |
+
registered_handler: Optional[grpc.RpcMethodHandler],
|
| 144 |
+
):
|
| 145 |
+
self._name = name
|
| 146 |
+
self._registered_handler = registered_handler
|
| 147 |
+
|
| 148 |
+
def name(self) -> Optional[str]:
|
| 149 |
+
return self._name
|
| 150 |
+
|
| 151 |
+
def handler(
|
| 152 |
+
self, handler_call_details: _HandlerCallDetails
|
| 153 |
+
) -> Optional[grpc.RpcMethodHandler]:
|
| 154 |
+
return self._registered_handler
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
class _GenericMethod(_Method):
|
| 158 |
+
def __init__(
|
| 159 |
+
self,
|
| 160 |
+
generic_handlers: List[grpc.GenericRpcHandler],
|
| 161 |
+
):
|
| 162 |
+
self._generic_handlers = generic_handlers
|
| 163 |
+
|
| 164 |
+
def name(self) -> Optional[str]:
|
| 165 |
+
return None
|
| 166 |
+
|
| 167 |
+
def handler(
|
| 168 |
+
self, handler_call_details: _HandlerCallDetails
|
| 169 |
+
) -> Optional[grpc.RpcMethodHandler]:
|
| 170 |
+
# If the same method have both generic and registered handler,
|
| 171 |
+
# registered handler will take precedence.
|
| 172 |
+
for generic_handler in self._generic_handlers:
|
| 173 |
+
method_handler = generic_handler.service(handler_call_details)
|
| 174 |
+
if method_handler is not None:
|
| 175 |
+
return method_handler
|
| 176 |
+
return None
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
class _RPCState(object):
|
| 180 |
+
context: contextvars.Context
|
| 181 |
+
condition: threading.Condition
|
| 182 |
+
due = Set[str]
|
| 183 |
+
request: Any
|
| 184 |
+
client: str
|
| 185 |
+
initial_metadata_allowed: bool
|
| 186 |
+
compression_algorithm: Optional[grpc.Compression]
|
| 187 |
+
disable_next_compression: bool
|
| 188 |
+
trailing_metadata: Optional[MetadataType]
|
| 189 |
+
code: Optional[grpc.StatusCode]
|
| 190 |
+
details: Optional[bytes]
|
| 191 |
+
statused: bool
|
| 192 |
+
rpc_errors: List[Exception]
|
| 193 |
+
callbacks: Optional[List[NullaryCallbackType]]
|
| 194 |
+
aborted: bool
|
| 195 |
+
|
| 196 |
+
def __init__(self):
|
| 197 |
+
self.context = contextvars.Context()
|
| 198 |
+
self.condition = threading.Condition()
|
| 199 |
+
self.due = set()
|
| 200 |
+
self.request = None
|
| 201 |
+
self.client = _OPEN
|
| 202 |
+
self.initial_metadata_allowed = True
|
| 203 |
+
self.compression_algorithm = None
|
| 204 |
+
self.disable_next_compression = False
|
| 205 |
+
self.trailing_metadata = None
|
| 206 |
+
self.code = None
|
| 207 |
+
self.details = None
|
| 208 |
+
self.statused = False
|
| 209 |
+
self.rpc_errors = []
|
| 210 |
+
self.callbacks = []
|
| 211 |
+
self.aborted = False
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
def _raise_rpc_error(state: _RPCState) -> None:
|
| 215 |
+
rpc_error = grpc.RpcError()
|
| 216 |
+
state.rpc_errors.append(rpc_error)
|
| 217 |
+
raise rpc_error
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def _possibly_finish_call(
|
| 221 |
+
state: _RPCState, token: str
|
| 222 |
+
) -> ServerTagCallbackType:
|
| 223 |
+
state.due.remove(token)
|
| 224 |
+
if not _is_rpc_state_active(state) and not state.due:
|
| 225 |
+
callbacks = state.callbacks
|
| 226 |
+
state.callbacks = None
|
| 227 |
+
return state, callbacks
|
| 228 |
+
else:
|
| 229 |
+
return None, ()
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
def _send_status_from_server(state: _RPCState, token: str) -> ServerCallbackTag:
|
| 233 |
+
def send_status_from_server(unused_send_status_from_server_event):
|
| 234 |
+
with state.condition:
|
| 235 |
+
return _possibly_finish_call(state, token)
|
| 236 |
+
|
| 237 |
+
return send_status_from_server
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
def _get_initial_metadata(
|
| 241 |
+
state: _RPCState, metadata: Optional[MetadataType]
|
| 242 |
+
) -> Optional[MetadataType]:
|
| 243 |
+
with state.condition:
|
| 244 |
+
if state.compression_algorithm:
|
| 245 |
+
compression_metadata = (
|
| 246 |
+
_compression.compression_algorithm_to_metadata(
|
| 247 |
+
state.compression_algorithm
|
| 248 |
+
),
|
| 249 |
+
)
|
| 250 |
+
if metadata is None:
|
| 251 |
+
return compression_metadata
|
| 252 |
+
else:
|
| 253 |
+
return compression_metadata + tuple(metadata)
|
| 254 |
+
else:
|
| 255 |
+
return metadata
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
def _get_initial_metadata_operation(
|
| 259 |
+
state: _RPCState, metadata: Optional[MetadataType]
|
| 260 |
+
) -> cygrpc.Operation:
|
| 261 |
+
operation = cygrpc.SendInitialMetadataOperation(
|
| 262 |
+
_get_initial_metadata(state, metadata), _EMPTY_FLAGS
|
| 263 |
+
)
|
| 264 |
+
return operation
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
def _abort(
|
| 268 |
+
state: _RPCState, call: cygrpc.Call, code: cygrpc.StatusCode, details: bytes
|
| 269 |
+
) -> None:
|
| 270 |
+
if state.client is not _CANCELLED:
|
| 271 |
+
effective_code = _abortion_code(state, code)
|
| 272 |
+
effective_details = details if state.details is None else state.details
|
| 273 |
+
if state.initial_metadata_allowed:
|
| 274 |
+
operations = (
|
| 275 |
+
_get_initial_metadata_operation(state, None),
|
| 276 |
+
cygrpc.SendStatusFromServerOperation(
|
| 277 |
+
state.trailing_metadata,
|
| 278 |
+
effective_code,
|
| 279 |
+
effective_details,
|
| 280 |
+
_EMPTY_FLAGS,
|
| 281 |
+
),
|
| 282 |
+
)
|
| 283 |
+
token = _SEND_INITIAL_METADATA_AND_SEND_STATUS_FROM_SERVER_TOKEN
|
| 284 |
+
else:
|
| 285 |
+
operations = (
|
| 286 |
+
cygrpc.SendStatusFromServerOperation(
|
| 287 |
+
state.trailing_metadata,
|
| 288 |
+
effective_code,
|
| 289 |
+
effective_details,
|
| 290 |
+
_EMPTY_FLAGS,
|
| 291 |
+
),
|
| 292 |
+
)
|
| 293 |
+
token = _SEND_STATUS_FROM_SERVER_TOKEN
|
| 294 |
+
call.start_server_batch(
|
| 295 |
+
operations, _send_status_from_server(state, token)
|
| 296 |
+
)
|
| 297 |
+
state.statused = True
|
| 298 |
+
state.due.add(token)
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def _receive_close_on_server(state: _RPCState) -> ServerCallbackTag:
|
| 302 |
+
def receive_close_on_server(receive_close_on_server_event):
|
| 303 |
+
with state.condition:
|
| 304 |
+
if receive_close_on_server_event.batch_operations[0].cancelled():
|
| 305 |
+
state.client = _CANCELLED
|
| 306 |
+
elif state.client is _OPEN:
|
| 307 |
+
state.client = _CLOSED
|
| 308 |
+
state.condition.notify_all()
|
| 309 |
+
return _possibly_finish_call(state, _RECEIVE_CLOSE_ON_SERVER_TOKEN)
|
| 310 |
+
|
| 311 |
+
return receive_close_on_server
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def _receive_message(
|
| 315 |
+
state: _RPCState,
|
| 316 |
+
call: cygrpc.Call,
|
| 317 |
+
request_deserializer: Optional[DeserializingFunction],
|
| 318 |
+
) -> ServerCallbackTag:
|
| 319 |
+
def receive_message(receive_message_event):
|
| 320 |
+
serialized_request = _serialized_request(receive_message_event)
|
| 321 |
+
if serialized_request is None:
|
| 322 |
+
with state.condition:
|
| 323 |
+
if state.client is _OPEN:
|
| 324 |
+
state.client = _CLOSED
|
| 325 |
+
state.condition.notify_all()
|
| 326 |
+
return _possibly_finish_call(state, _RECEIVE_MESSAGE_TOKEN)
|
| 327 |
+
else:
|
| 328 |
+
request = _common.deserialize(
|
| 329 |
+
serialized_request, request_deserializer
|
| 330 |
+
)
|
| 331 |
+
with state.condition:
|
| 332 |
+
if request is None:
|
| 333 |
+
_abort(
|
| 334 |
+
state,
|
| 335 |
+
call,
|
| 336 |
+
cygrpc.StatusCode.internal,
|
| 337 |
+
b"Exception deserializing request!",
|
| 338 |
+
)
|
| 339 |
+
else:
|
| 340 |
+
state.request = request
|
| 341 |
+
state.condition.notify_all()
|
| 342 |
+
return _possibly_finish_call(state, _RECEIVE_MESSAGE_TOKEN)
|
| 343 |
+
|
| 344 |
+
return receive_message
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
def _send_initial_metadata(state: _RPCState) -> ServerCallbackTag:
|
| 348 |
+
def send_initial_metadata(unused_send_initial_metadata_event):
|
| 349 |
+
with state.condition:
|
| 350 |
+
return _possibly_finish_call(state, _SEND_INITIAL_METADATA_TOKEN)
|
| 351 |
+
|
| 352 |
+
return send_initial_metadata
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
def _send_message(state: _RPCState, token: str) -> ServerCallbackTag:
|
| 356 |
+
def send_message(unused_send_message_event):
|
| 357 |
+
with state.condition:
|
| 358 |
+
state.condition.notify_all()
|
| 359 |
+
return _possibly_finish_call(state, token)
|
| 360 |
+
|
| 361 |
+
return send_message
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
class _Context(grpc.ServicerContext):
|
| 365 |
+
_rpc_event: cygrpc.BaseEvent
|
| 366 |
+
_state: _RPCState
|
| 367 |
+
request_deserializer: Optional[DeserializingFunction]
|
| 368 |
+
|
| 369 |
+
def __init__(
|
| 370 |
+
self,
|
| 371 |
+
rpc_event: cygrpc.BaseEvent,
|
| 372 |
+
state: _RPCState,
|
| 373 |
+
request_deserializer: Optional[DeserializingFunction],
|
| 374 |
+
):
|
| 375 |
+
self._rpc_event = rpc_event
|
| 376 |
+
self._state = state
|
| 377 |
+
self._request_deserializer = request_deserializer
|
| 378 |
+
|
| 379 |
+
def is_active(self) -> bool:
|
| 380 |
+
with self._state.condition:
|
| 381 |
+
return _is_rpc_state_active(self._state)
|
| 382 |
+
|
| 383 |
+
def time_remaining(self) -> float:
|
| 384 |
+
return max(self._rpc_event.call_details.deadline - time.time(), 0)
|
| 385 |
+
|
| 386 |
+
def cancel(self) -> None:
|
| 387 |
+
self._rpc_event.call.cancel()
|
| 388 |
+
|
| 389 |
+
def add_callback(self, callback: NullaryCallbackType) -> bool:
|
| 390 |
+
with self._state.condition:
|
| 391 |
+
if self._state.callbacks is None:
|
| 392 |
+
return False
|
| 393 |
+
else:
|
| 394 |
+
self._state.callbacks.append(callback)
|
| 395 |
+
return True
|
| 396 |
+
|
| 397 |
+
def disable_next_message_compression(self) -> None:
|
| 398 |
+
with self._state.condition:
|
| 399 |
+
self._state.disable_next_compression = True
|
| 400 |
+
|
| 401 |
+
def invocation_metadata(self) -> Optional[MetadataType]:
|
| 402 |
+
return self._rpc_event.invocation_metadata
|
| 403 |
+
|
| 404 |
+
def peer(self) -> str:
|
| 405 |
+
return _common.decode(self._rpc_event.call.peer())
|
| 406 |
+
|
| 407 |
+
def peer_identities(self) -> Optional[Sequence[bytes]]:
|
| 408 |
+
return cygrpc.peer_identities(self._rpc_event.call)
|
| 409 |
+
|
| 410 |
+
def peer_identity_key(self) -> Optional[str]:
|
| 411 |
+
id_key = cygrpc.peer_identity_key(self._rpc_event.call)
|
| 412 |
+
return id_key if id_key is None else _common.decode(id_key)
|
| 413 |
+
|
| 414 |
+
def auth_context(self) -> Mapping[str, Sequence[bytes]]:
|
| 415 |
+
auth_context = cygrpc.auth_context(self._rpc_event.call)
|
| 416 |
+
auth_context_dict = {} if auth_context is None else auth_context
|
| 417 |
+
return {
|
| 418 |
+
_common.decode(key): value
|
| 419 |
+
for key, value in auth_context_dict.items()
|
| 420 |
+
}
|
| 421 |
+
|
| 422 |
+
def set_compression(self, compression: grpc.Compression) -> None:
|
| 423 |
+
with self._state.condition:
|
| 424 |
+
self._state.compression_algorithm = compression
|
| 425 |
+
|
| 426 |
+
def send_initial_metadata(self, initial_metadata: MetadataType) -> None:
|
| 427 |
+
with self._state.condition:
|
| 428 |
+
if self._state.client is _CANCELLED:
|
| 429 |
+
_raise_rpc_error(self._state)
|
| 430 |
+
else:
|
| 431 |
+
if self._state.initial_metadata_allowed:
|
| 432 |
+
operation = _get_initial_metadata_operation(
|
| 433 |
+
self._state, initial_metadata
|
| 434 |
+
)
|
| 435 |
+
self._rpc_event.call.start_server_batch(
|
| 436 |
+
(operation,), _send_initial_metadata(self._state)
|
| 437 |
+
)
|
| 438 |
+
self._state.initial_metadata_allowed = False
|
| 439 |
+
self._state.due.add(_SEND_INITIAL_METADATA_TOKEN)
|
| 440 |
+
else:
|
| 441 |
+
raise ValueError("Initial metadata no longer allowed!")
|
| 442 |
+
|
| 443 |
+
def set_trailing_metadata(self, trailing_metadata: MetadataType) -> None:
|
| 444 |
+
with self._state.condition:
|
| 445 |
+
self._state.trailing_metadata = trailing_metadata
|
| 446 |
+
|
| 447 |
+
def trailing_metadata(self) -> Optional[MetadataType]:
|
| 448 |
+
return self._state.trailing_metadata
|
| 449 |
+
|
| 450 |
+
def abort(self, code: grpc.StatusCode, details: str) -> None:
|
| 451 |
+
# treat OK like other invalid arguments: fail the RPC
|
| 452 |
+
if code == grpc.StatusCode.OK:
|
| 453 |
+
_LOGGER.error(
|
| 454 |
+
"abort() called with StatusCode.OK; returning UNKNOWN"
|
| 455 |
+
)
|
| 456 |
+
code = grpc.StatusCode.UNKNOWN
|
| 457 |
+
details = ""
|
| 458 |
+
with self._state.condition:
|
| 459 |
+
self._state.code = code
|
| 460 |
+
self._state.details = _common.encode(details)
|
| 461 |
+
self._state.aborted = True
|
| 462 |
+
raise Exception()
|
| 463 |
+
|
| 464 |
+
def abort_with_status(self, status: grpc.Status) -> None:
|
| 465 |
+
self._state.trailing_metadata = status.trailing_metadata
|
| 466 |
+
self.abort(status.code, status.details)
|
| 467 |
+
|
| 468 |
+
def set_code(self, code: grpc.StatusCode) -> None:
|
| 469 |
+
with self._state.condition:
|
| 470 |
+
self._state.code = code
|
| 471 |
+
|
| 472 |
+
def code(self) -> grpc.StatusCode:
|
| 473 |
+
return self._state.code
|
| 474 |
+
|
| 475 |
+
def set_details(self, details: str) -> None:
|
| 476 |
+
with self._state.condition:
|
| 477 |
+
self._state.details = _common.encode(details)
|
| 478 |
+
|
| 479 |
+
def details(self) -> bytes:
|
| 480 |
+
return self._state.details
|
| 481 |
+
|
| 482 |
+
def _finalize_state(self) -> None:
|
| 483 |
+
pass
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
class _RequestIterator(object):
|
| 487 |
+
_state: _RPCState
|
| 488 |
+
_call: cygrpc.Call
|
| 489 |
+
_request_deserializer: Optional[DeserializingFunction]
|
| 490 |
+
|
| 491 |
+
def __init__(
|
| 492 |
+
self,
|
| 493 |
+
state: _RPCState,
|
| 494 |
+
call: cygrpc.Call,
|
| 495 |
+
request_deserializer: Optional[DeserializingFunction],
|
| 496 |
+
):
|
| 497 |
+
self._state = state
|
| 498 |
+
self._call = call
|
| 499 |
+
self._request_deserializer = request_deserializer
|
| 500 |
+
|
| 501 |
+
def _raise_or_start_receive_message(self) -> None:
|
| 502 |
+
if self._state.client is _CANCELLED:
|
| 503 |
+
_raise_rpc_error(self._state)
|
| 504 |
+
elif not _is_rpc_state_active(self._state):
|
| 505 |
+
raise StopIteration()
|
| 506 |
+
else:
|
| 507 |
+
self._call.start_server_batch(
|
| 508 |
+
(cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),),
|
| 509 |
+
_receive_message(
|
| 510 |
+
self._state, self._call, self._request_deserializer
|
| 511 |
+
),
|
| 512 |
+
)
|
| 513 |
+
self._state.due.add(_RECEIVE_MESSAGE_TOKEN)
|
| 514 |
+
|
| 515 |
+
def _look_for_request(self) -> Any:
|
| 516 |
+
if self._state.client is _CANCELLED:
|
| 517 |
+
_raise_rpc_error(self._state)
|
| 518 |
+
elif (
|
| 519 |
+
self._state.request is None
|
| 520 |
+
and _RECEIVE_MESSAGE_TOKEN not in self._state.due
|
| 521 |
+
):
|
| 522 |
+
raise StopIteration()
|
| 523 |
+
else:
|
| 524 |
+
request = self._state.request
|
| 525 |
+
self._state.request = None
|
| 526 |
+
return request
|
| 527 |
+
|
| 528 |
+
raise AssertionError() # should never run
|
| 529 |
+
|
| 530 |
+
def _next(self) -> Any:
|
| 531 |
+
with self._state.condition:
|
| 532 |
+
self._raise_or_start_receive_message()
|
| 533 |
+
while True:
|
| 534 |
+
self._state.condition.wait()
|
| 535 |
+
request = self._look_for_request()
|
| 536 |
+
if request is not None:
|
| 537 |
+
return request
|
| 538 |
+
|
| 539 |
+
def __iter__(self) -> _RequestIterator:
|
| 540 |
+
return self
|
| 541 |
+
|
| 542 |
+
def __next__(self) -> Any:
|
| 543 |
+
return self._next()
|
| 544 |
+
|
| 545 |
+
def next(self) -> Any:
|
| 546 |
+
return self._next()
|
| 547 |
+
|
| 548 |
+
|
| 549 |
+
def _unary_request(
|
| 550 |
+
rpc_event: cygrpc.BaseEvent,
|
| 551 |
+
state: _RPCState,
|
| 552 |
+
request_deserializer: Optional[DeserializingFunction],
|
| 553 |
+
) -> Callable[[], Any]:
|
| 554 |
+
def unary_request():
|
| 555 |
+
with state.condition:
|
| 556 |
+
if not _is_rpc_state_active(state):
|
| 557 |
+
return None
|
| 558 |
+
else:
|
| 559 |
+
rpc_event.call.start_server_batch(
|
| 560 |
+
(cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),),
|
| 561 |
+
_receive_message(
|
| 562 |
+
state, rpc_event.call, request_deserializer
|
| 563 |
+
),
|
| 564 |
+
)
|
| 565 |
+
state.due.add(_RECEIVE_MESSAGE_TOKEN)
|
| 566 |
+
while True:
|
| 567 |
+
state.condition.wait()
|
| 568 |
+
if state.request is None:
|
| 569 |
+
if state.client is _CLOSED:
|
| 570 |
+
details = '"{}" requires exactly one request message.'.format(
|
| 571 |
+
rpc_event.call_details.method
|
| 572 |
+
)
|
| 573 |
+
_abort(
|
| 574 |
+
state,
|
| 575 |
+
rpc_event.call,
|
| 576 |
+
cygrpc.StatusCode.unimplemented,
|
| 577 |
+
_common.encode(details),
|
| 578 |
+
)
|
| 579 |
+
return None
|
| 580 |
+
elif state.client is _CANCELLED:
|
| 581 |
+
return None
|
| 582 |
+
else:
|
| 583 |
+
request = state.request
|
| 584 |
+
state.request = None
|
| 585 |
+
return request
|
| 586 |
+
|
| 587 |
+
return unary_request
|
| 588 |
+
|
| 589 |
+
|
| 590 |
+
def _call_behavior(
|
| 591 |
+
rpc_event: cygrpc.BaseEvent,
|
| 592 |
+
state: _RPCState,
|
| 593 |
+
behavior: ArityAgnosticMethodHandler,
|
| 594 |
+
argument: Any,
|
| 595 |
+
request_deserializer: Optional[DeserializingFunction],
|
| 596 |
+
send_response_callback: Optional[Callable[[ResponseType], None]] = None,
|
| 597 |
+
) -> Tuple[Union[ResponseType, Iterator[ResponseType]], bool]:
|
| 598 |
+
from grpc import _create_servicer_context # pytype: disable=pyi-error
|
| 599 |
+
|
| 600 |
+
with _create_servicer_context(
|
| 601 |
+
rpc_event, state, request_deserializer
|
| 602 |
+
) as context:
|
| 603 |
+
try:
|
| 604 |
+
response_or_iterator = None
|
| 605 |
+
if send_response_callback is not None:
|
| 606 |
+
response_or_iterator = behavior(
|
| 607 |
+
argument, context, send_response_callback
|
| 608 |
+
)
|
| 609 |
+
else:
|
| 610 |
+
response_or_iterator = behavior(argument, context)
|
| 611 |
+
return response_or_iterator, True
|
| 612 |
+
except Exception as exception: # pylint: disable=broad-except
|
| 613 |
+
with state.condition:
|
| 614 |
+
if state.aborted:
|
| 615 |
+
_abort(
|
| 616 |
+
state,
|
| 617 |
+
rpc_event.call,
|
| 618 |
+
cygrpc.StatusCode.unknown,
|
| 619 |
+
b"RPC Aborted",
|
| 620 |
+
)
|
| 621 |
+
elif exception not in state.rpc_errors:
|
| 622 |
+
try:
|
| 623 |
+
details = "Exception calling application: {}".format(
|
| 624 |
+
exception
|
| 625 |
+
)
|
| 626 |
+
except Exception: # pylint: disable=broad-except
|
| 627 |
+
details = (
|
| 628 |
+
"Calling application raised unprintable Exception!"
|
| 629 |
+
)
|
| 630 |
+
_LOGGER.exception(
|
| 631 |
+
traceback.format_exception(
|
| 632 |
+
type(exception),
|
| 633 |
+
exception,
|
| 634 |
+
exception.__traceback__,
|
| 635 |
+
)
|
| 636 |
+
)
|
| 637 |
+
traceback.print_exc()
|
| 638 |
+
_LOGGER.exception(details)
|
| 639 |
+
_abort(
|
| 640 |
+
state,
|
| 641 |
+
rpc_event.call,
|
| 642 |
+
cygrpc.StatusCode.unknown,
|
| 643 |
+
_common.encode(details),
|
| 644 |
+
)
|
| 645 |
+
return None, False
|
| 646 |
+
|
| 647 |
+
|
| 648 |
+
def _take_response_from_response_iterator(
|
| 649 |
+
rpc_event: cygrpc.BaseEvent,
|
| 650 |
+
state: _RPCState,
|
| 651 |
+
response_iterator: Iterator[ResponseType],
|
| 652 |
+
) -> Tuple[ResponseType, bool]:
|
| 653 |
+
try:
|
| 654 |
+
return next(response_iterator), True
|
| 655 |
+
except StopIteration:
|
| 656 |
+
return None, True
|
| 657 |
+
except Exception as exception: # pylint: disable=broad-except
|
| 658 |
+
with state.condition:
|
| 659 |
+
if state.aborted:
|
| 660 |
+
_abort(
|
| 661 |
+
state,
|
| 662 |
+
rpc_event.call,
|
| 663 |
+
cygrpc.StatusCode.unknown,
|
| 664 |
+
b"RPC Aborted",
|
| 665 |
+
)
|
| 666 |
+
elif exception not in state.rpc_errors:
|
| 667 |
+
details = "Exception iterating responses: {}".format(exception)
|
| 668 |
+
_LOGGER.exception(details)
|
| 669 |
+
_abort(
|
| 670 |
+
state,
|
| 671 |
+
rpc_event.call,
|
| 672 |
+
cygrpc.StatusCode.unknown,
|
| 673 |
+
_common.encode(details),
|
| 674 |
+
)
|
| 675 |
+
return None, False
|
| 676 |
+
|
| 677 |
+
|
| 678 |
+
def _serialize_response(
|
| 679 |
+
rpc_event: cygrpc.BaseEvent,
|
| 680 |
+
state: _RPCState,
|
| 681 |
+
response: Any,
|
| 682 |
+
response_serializer: Optional[SerializingFunction],
|
| 683 |
+
) -> Optional[bytes]:
|
| 684 |
+
serialized_response = _common.serialize(response, response_serializer)
|
| 685 |
+
if serialized_response is None:
|
| 686 |
+
with state.condition:
|
| 687 |
+
_abort(
|
| 688 |
+
state,
|
| 689 |
+
rpc_event.call,
|
| 690 |
+
cygrpc.StatusCode.internal,
|
| 691 |
+
b"Failed to serialize response!",
|
| 692 |
+
)
|
| 693 |
+
return None
|
| 694 |
+
else:
|
| 695 |
+
return serialized_response
|
| 696 |
+
|
| 697 |
+
|
| 698 |
+
def _get_send_message_op_flags_from_state(
|
| 699 |
+
state: _RPCState,
|
| 700 |
+
) -> Union[int, cygrpc.WriteFlag]:
|
| 701 |
+
if state.disable_next_compression:
|
| 702 |
+
return cygrpc.WriteFlag.no_compress
|
| 703 |
+
else:
|
| 704 |
+
return _EMPTY_FLAGS
|
| 705 |
+
|
| 706 |
+
|
| 707 |
+
def _reset_per_message_state(state: _RPCState) -> None:
|
| 708 |
+
with state.condition:
|
| 709 |
+
state.disable_next_compression = False
|
| 710 |
+
|
| 711 |
+
|
| 712 |
+
def _send_response(
|
| 713 |
+
rpc_event: cygrpc.BaseEvent, state: _RPCState, serialized_response: bytes
|
| 714 |
+
) -> bool:
|
| 715 |
+
with state.condition:
|
| 716 |
+
if not _is_rpc_state_active(state):
|
| 717 |
+
return False
|
| 718 |
+
else:
|
| 719 |
+
if state.initial_metadata_allowed:
|
| 720 |
+
operations = (
|
| 721 |
+
_get_initial_metadata_operation(state, None),
|
| 722 |
+
cygrpc.SendMessageOperation(
|
| 723 |
+
serialized_response,
|
| 724 |
+
_get_send_message_op_flags_from_state(state),
|
| 725 |
+
),
|
| 726 |
+
)
|
| 727 |
+
state.initial_metadata_allowed = False
|
| 728 |
+
token = _SEND_INITIAL_METADATA_AND_SEND_MESSAGE_TOKEN
|
| 729 |
+
else:
|
| 730 |
+
operations = (
|
| 731 |
+
cygrpc.SendMessageOperation(
|
| 732 |
+
serialized_response,
|
| 733 |
+
_get_send_message_op_flags_from_state(state),
|
| 734 |
+
),
|
| 735 |
+
)
|
| 736 |
+
token = _SEND_MESSAGE_TOKEN
|
| 737 |
+
rpc_event.call.start_server_batch(
|
| 738 |
+
operations, _send_message(state, token)
|
| 739 |
+
)
|
| 740 |
+
state.due.add(token)
|
| 741 |
+
_reset_per_message_state(state)
|
| 742 |
+
while True:
|
| 743 |
+
state.condition.wait()
|
| 744 |
+
if token not in state.due:
|
| 745 |
+
return _is_rpc_state_active(state)
|
| 746 |
+
|
| 747 |
+
|
| 748 |
+
def _status(
|
| 749 |
+
rpc_event: cygrpc.BaseEvent,
|
| 750 |
+
state: _RPCState,
|
| 751 |
+
serialized_response: Optional[bytes],
|
| 752 |
+
) -> None:
|
| 753 |
+
with state.condition:
|
| 754 |
+
if state.client is not _CANCELLED:
|
| 755 |
+
code = _completion_code(state)
|
| 756 |
+
details = _details(state)
|
| 757 |
+
operations = [
|
| 758 |
+
cygrpc.SendStatusFromServerOperation(
|
| 759 |
+
state.trailing_metadata, code, details, _EMPTY_FLAGS
|
| 760 |
+
),
|
| 761 |
+
]
|
| 762 |
+
if state.initial_metadata_allowed:
|
| 763 |
+
operations.append(_get_initial_metadata_operation(state, None))
|
| 764 |
+
if serialized_response is not None:
|
| 765 |
+
operations.append(
|
| 766 |
+
cygrpc.SendMessageOperation(
|
| 767 |
+
serialized_response,
|
| 768 |
+
_get_send_message_op_flags_from_state(state),
|
| 769 |
+
)
|
| 770 |
+
)
|
| 771 |
+
rpc_event.call.start_server_batch(
|
| 772 |
+
operations,
|
| 773 |
+
_send_status_from_server(state, _SEND_STATUS_FROM_SERVER_TOKEN),
|
| 774 |
+
)
|
| 775 |
+
state.statused = True
|
| 776 |
+
_reset_per_message_state(state)
|
| 777 |
+
state.due.add(_SEND_STATUS_FROM_SERVER_TOKEN)
|
| 778 |
+
|
| 779 |
+
|
| 780 |
+
def _unary_response_in_pool(
|
| 781 |
+
rpc_event: cygrpc.BaseEvent,
|
| 782 |
+
state: _RPCState,
|
| 783 |
+
behavior: ArityAgnosticMethodHandler,
|
| 784 |
+
argument_thunk: Callable[[], Any],
|
| 785 |
+
request_deserializer: Optional[SerializingFunction],
|
| 786 |
+
response_serializer: Optional[SerializingFunction],
|
| 787 |
+
) -> None:
|
| 788 |
+
cygrpc.install_context_from_request_call_event(rpc_event)
|
| 789 |
+
|
| 790 |
+
try:
|
| 791 |
+
argument = argument_thunk()
|
| 792 |
+
if argument is not None:
|
| 793 |
+
response, proceed = _call_behavior(
|
| 794 |
+
rpc_event, state, behavior, argument, request_deserializer
|
| 795 |
+
)
|
| 796 |
+
if proceed:
|
| 797 |
+
serialized_response = _serialize_response(
|
| 798 |
+
rpc_event, state, response, response_serializer
|
| 799 |
+
)
|
| 800 |
+
if serialized_response is not None:
|
| 801 |
+
_status(rpc_event, state, serialized_response)
|
| 802 |
+
except Exception: # pylint: disable=broad-except
|
| 803 |
+
traceback.print_exc()
|
| 804 |
+
finally:
|
| 805 |
+
cygrpc.uninstall_context()
|
| 806 |
+
|
| 807 |
+
|
| 808 |
+
def _stream_response_in_pool(
|
| 809 |
+
rpc_event: cygrpc.BaseEvent,
|
| 810 |
+
state: _RPCState,
|
| 811 |
+
behavior: ArityAgnosticMethodHandler,
|
| 812 |
+
argument_thunk: Callable[[], Any],
|
| 813 |
+
request_deserializer: Optional[DeserializingFunction],
|
| 814 |
+
response_serializer: Optional[SerializingFunction],
|
| 815 |
+
) -> None:
|
| 816 |
+
cygrpc.install_context_from_request_call_event(rpc_event)
|
| 817 |
+
|
| 818 |
+
def send_response(response: Any) -> None:
|
| 819 |
+
if response is None:
|
| 820 |
+
_status(rpc_event, state, None)
|
| 821 |
+
else:
|
| 822 |
+
serialized_response = _serialize_response(
|
| 823 |
+
rpc_event, state, response, response_serializer
|
| 824 |
+
)
|
| 825 |
+
if serialized_response is not None:
|
| 826 |
+
_send_response(rpc_event, state, serialized_response)
|
| 827 |
+
|
| 828 |
+
try:
|
| 829 |
+
argument = argument_thunk()
|
| 830 |
+
if argument is not None:
|
| 831 |
+
if (
|
| 832 |
+
hasattr(behavior, "experimental_non_blocking")
|
| 833 |
+
and behavior.experimental_non_blocking
|
| 834 |
+
):
|
| 835 |
+
_call_behavior(
|
| 836 |
+
rpc_event,
|
| 837 |
+
state,
|
| 838 |
+
behavior,
|
| 839 |
+
argument,
|
| 840 |
+
request_deserializer,
|
| 841 |
+
send_response_callback=send_response,
|
| 842 |
+
)
|
| 843 |
+
else:
|
| 844 |
+
response_iterator, proceed = _call_behavior(
|
| 845 |
+
rpc_event, state, behavior, argument, request_deserializer
|
| 846 |
+
)
|
| 847 |
+
if proceed:
|
| 848 |
+
_send_message_callback_to_blocking_iterator_adapter(
|
| 849 |
+
rpc_event, state, send_response, response_iterator
|
| 850 |
+
)
|
| 851 |
+
except Exception: # pylint: disable=broad-except
|
| 852 |
+
traceback.print_exc()
|
| 853 |
+
finally:
|
| 854 |
+
cygrpc.uninstall_context()
|
| 855 |
+
|
| 856 |
+
|
| 857 |
+
def _is_rpc_state_active(state: _RPCState) -> bool:
|
| 858 |
+
return state.client is not _CANCELLED and not state.statused
|
| 859 |
+
|
| 860 |
+
|
| 861 |
+
def _send_message_callback_to_blocking_iterator_adapter(
|
| 862 |
+
rpc_event: cygrpc.BaseEvent,
|
| 863 |
+
state: _RPCState,
|
| 864 |
+
send_response_callback: Callable[[ResponseType], None],
|
| 865 |
+
response_iterator: Iterator[ResponseType],
|
| 866 |
+
) -> None:
|
| 867 |
+
while True:
|
| 868 |
+
response, proceed = _take_response_from_response_iterator(
|
| 869 |
+
rpc_event, state, response_iterator
|
| 870 |
+
)
|
| 871 |
+
if proceed:
|
| 872 |
+
send_response_callback(response)
|
| 873 |
+
if not _is_rpc_state_active(state):
|
| 874 |
+
break
|
| 875 |
+
else:
|
| 876 |
+
break
|
| 877 |
+
|
| 878 |
+
|
| 879 |
+
def _select_thread_pool_for_behavior(
|
| 880 |
+
behavior: ArityAgnosticMethodHandler,
|
| 881 |
+
default_thread_pool: futures.ThreadPoolExecutor,
|
| 882 |
+
) -> futures.ThreadPoolExecutor:
|
| 883 |
+
if hasattr(behavior, "experimental_thread_pool") and isinstance(
|
| 884 |
+
behavior.experimental_thread_pool, futures.ThreadPoolExecutor
|
| 885 |
+
):
|
| 886 |
+
return behavior.experimental_thread_pool
|
| 887 |
+
else:
|
| 888 |
+
return default_thread_pool
|
| 889 |
+
|
| 890 |
+
|
| 891 |
+
def _handle_unary_unary(
|
| 892 |
+
rpc_event: cygrpc.BaseEvent,
|
| 893 |
+
state: _RPCState,
|
| 894 |
+
method_handler: grpc.RpcMethodHandler,
|
| 895 |
+
default_thread_pool: futures.ThreadPoolExecutor,
|
| 896 |
+
) -> futures.Future:
|
| 897 |
+
unary_request = _unary_request(
|
| 898 |
+
rpc_event, state, method_handler.request_deserializer
|
| 899 |
+
)
|
| 900 |
+
thread_pool = _select_thread_pool_for_behavior(
|
| 901 |
+
method_handler.unary_unary, default_thread_pool
|
| 902 |
+
)
|
| 903 |
+
return thread_pool.submit(
|
| 904 |
+
state.context.run,
|
| 905 |
+
_unary_response_in_pool,
|
| 906 |
+
rpc_event,
|
| 907 |
+
state,
|
| 908 |
+
method_handler.unary_unary,
|
| 909 |
+
unary_request,
|
| 910 |
+
method_handler.request_deserializer,
|
| 911 |
+
method_handler.response_serializer,
|
| 912 |
+
)
|
| 913 |
+
|
| 914 |
+
|
| 915 |
+
def _handle_unary_stream(
|
| 916 |
+
rpc_event: cygrpc.BaseEvent,
|
| 917 |
+
state: _RPCState,
|
| 918 |
+
method_handler: grpc.RpcMethodHandler,
|
| 919 |
+
default_thread_pool: futures.ThreadPoolExecutor,
|
| 920 |
+
) -> futures.Future:
|
| 921 |
+
unary_request = _unary_request(
|
| 922 |
+
rpc_event, state, method_handler.request_deserializer
|
| 923 |
+
)
|
| 924 |
+
thread_pool = _select_thread_pool_for_behavior(
|
| 925 |
+
method_handler.unary_stream, default_thread_pool
|
| 926 |
+
)
|
| 927 |
+
return thread_pool.submit(
|
| 928 |
+
state.context.run,
|
| 929 |
+
_stream_response_in_pool,
|
| 930 |
+
rpc_event,
|
| 931 |
+
state,
|
| 932 |
+
method_handler.unary_stream,
|
| 933 |
+
unary_request,
|
| 934 |
+
method_handler.request_deserializer,
|
| 935 |
+
method_handler.response_serializer,
|
| 936 |
+
)
|
| 937 |
+
|
| 938 |
+
|
| 939 |
+
def _handle_stream_unary(
|
| 940 |
+
rpc_event: cygrpc.BaseEvent,
|
| 941 |
+
state: _RPCState,
|
| 942 |
+
method_handler: grpc.RpcMethodHandler,
|
| 943 |
+
default_thread_pool: futures.ThreadPoolExecutor,
|
| 944 |
+
) -> futures.Future:
|
| 945 |
+
request_iterator = _RequestIterator(
|
| 946 |
+
state, rpc_event.call, method_handler.request_deserializer
|
| 947 |
+
)
|
| 948 |
+
thread_pool = _select_thread_pool_for_behavior(
|
| 949 |
+
method_handler.stream_unary, default_thread_pool
|
| 950 |
+
)
|
| 951 |
+
return thread_pool.submit(
|
| 952 |
+
state.context.run,
|
| 953 |
+
_unary_response_in_pool,
|
| 954 |
+
rpc_event,
|
| 955 |
+
state,
|
| 956 |
+
method_handler.stream_unary,
|
| 957 |
+
lambda: request_iterator,
|
| 958 |
+
method_handler.request_deserializer,
|
| 959 |
+
method_handler.response_serializer,
|
| 960 |
+
)
|
| 961 |
+
|
| 962 |
+
|
| 963 |
+
def _handle_stream_stream(
|
| 964 |
+
rpc_event: cygrpc.BaseEvent,
|
| 965 |
+
state: _RPCState,
|
| 966 |
+
method_handler: grpc.RpcMethodHandler,
|
| 967 |
+
default_thread_pool: futures.ThreadPoolExecutor,
|
| 968 |
+
) -> futures.Future:
|
| 969 |
+
request_iterator = _RequestIterator(
|
| 970 |
+
state, rpc_event.call, method_handler.request_deserializer
|
| 971 |
+
)
|
| 972 |
+
thread_pool = _select_thread_pool_for_behavior(
|
| 973 |
+
method_handler.stream_stream, default_thread_pool
|
| 974 |
+
)
|
| 975 |
+
return thread_pool.submit(
|
| 976 |
+
state.context.run,
|
| 977 |
+
_stream_response_in_pool,
|
| 978 |
+
rpc_event,
|
| 979 |
+
state,
|
| 980 |
+
method_handler.stream_stream,
|
| 981 |
+
lambda: request_iterator,
|
| 982 |
+
method_handler.request_deserializer,
|
| 983 |
+
method_handler.response_serializer,
|
| 984 |
+
)
|
| 985 |
+
|
| 986 |
+
|
| 987 |
+
def _find_method_handler(
|
| 988 |
+
rpc_event: cygrpc.BaseEvent,
|
| 989 |
+
state: _RPCState,
|
| 990 |
+
method_with_handler: _Method,
|
| 991 |
+
interceptor_pipeline: Optional[_interceptor._ServicePipeline],
|
| 992 |
+
) -> Optional[grpc.RpcMethodHandler]:
|
| 993 |
+
def query_handlers(
|
| 994 |
+
handler_call_details: _HandlerCallDetails,
|
| 995 |
+
) -> Optional[grpc.RpcMethodHandler]:
|
| 996 |
+
return method_with_handler.handler(handler_call_details)
|
| 997 |
+
|
| 998 |
+
method_name = method_with_handler.name()
|
| 999 |
+
if not method_name:
|
| 1000 |
+
method_name = _common.decode(rpc_event.call_details.method)
|
| 1001 |
+
|
| 1002 |
+
handler_call_details = _HandlerCallDetails(
|
| 1003 |
+
method_name,
|
| 1004 |
+
rpc_event.invocation_metadata,
|
| 1005 |
+
)
|
| 1006 |
+
|
| 1007 |
+
if interceptor_pipeline is not None:
|
| 1008 |
+
return state.context.run(
|
| 1009 |
+
interceptor_pipeline.execute, query_handlers, handler_call_details
|
| 1010 |
+
)
|
| 1011 |
+
else:
|
| 1012 |
+
return state.context.run(query_handlers, handler_call_details)
|
| 1013 |
+
|
| 1014 |
+
|
| 1015 |
+
def _reject_rpc(
|
| 1016 |
+
rpc_event: cygrpc.BaseEvent,
|
| 1017 |
+
rpc_state: _RPCState,
|
| 1018 |
+
status: cygrpc.StatusCode,
|
| 1019 |
+
details: bytes,
|
| 1020 |
+
):
|
| 1021 |
+
operations = (
|
| 1022 |
+
_get_initial_metadata_operation(rpc_state, None),
|
| 1023 |
+
cygrpc.ReceiveCloseOnServerOperation(_EMPTY_FLAGS),
|
| 1024 |
+
cygrpc.SendStatusFromServerOperation(
|
| 1025 |
+
None, status, details, _EMPTY_FLAGS
|
| 1026 |
+
),
|
| 1027 |
+
)
|
| 1028 |
+
rpc_event.call.start_server_batch(
|
| 1029 |
+
operations,
|
| 1030 |
+
lambda ignored_event: (
|
| 1031 |
+
rpc_state,
|
| 1032 |
+
(),
|
| 1033 |
+
),
|
| 1034 |
+
)
|
| 1035 |
+
|
| 1036 |
+
|
| 1037 |
+
def _handle_with_method_handler(
|
| 1038 |
+
rpc_event: cygrpc.BaseEvent,
|
| 1039 |
+
state: _RPCState,
|
| 1040 |
+
method_handler: grpc.RpcMethodHandler,
|
| 1041 |
+
thread_pool: futures.ThreadPoolExecutor,
|
| 1042 |
+
) -> futures.Future:
|
| 1043 |
+
with state.condition:
|
| 1044 |
+
rpc_event.call.start_server_batch(
|
| 1045 |
+
(cygrpc.ReceiveCloseOnServerOperation(_EMPTY_FLAGS),),
|
| 1046 |
+
_receive_close_on_server(state),
|
| 1047 |
+
)
|
| 1048 |
+
state.due.add(_RECEIVE_CLOSE_ON_SERVER_TOKEN)
|
| 1049 |
+
if method_handler.request_streaming:
|
| 1050 |
+
if method_handler.response_streaming:
|
| 1051 |
+
return _handle_stream_stream(
|
| 1052 |
+
rpc_event, state, method_handler, thread_pool
|
| 1053 |
+
)
|
| 1054 |
+
else:
|
| 1055 |
+
return _handle_stream_unary(
|
| 1056 |
+
rpc_event, state, method_handler, thread_pool
|
| 1057 |
+
)
|
| 1058 |
+
else:
|
| 1059 |
+
if method_handler.response_streaming:
|
| 1060 |
+
return _handle_unary_stream(
|
| 1061 |
+
rpc_event, state, method_handler, thread_pool
|
| 1062 |
+
)
|
| 1063 |
+
else:
|
| 1064 |
+
return _handle_unary_unary(
|
| 1065 |
+
rpc_event, state, method_handler, thread_pool
|
| 1066 |
+
)
|
| 1067 |
+
|
| 1068 |
+
|
| 1069 |
+
def _handle_call(
|
| 1070 |
+
rpc_event: cygrpc.BaseEvent,
|
| 1071 |
+
method_with_handler: _Method,
|
| 1072 |
+
interceptor_pipeline: Optional[_interceptor._ServicePipeline],
|
| 1073 |
+
thread_pool: futures.ThreadPoolExecutor,
|
| 1074 |
+
concurrency_exceeded: bool,
|
| 1075 |
+
) -> Tuple[Optional[_RPCState], Optional[futures.Future]]:
|
| 1076 |
+
"""Handles RPC based on provided handlers.
|
| 1077 |
+
|
| 1078 |
+
When receiving a call event from Core, registered method will have its
|
| 1079 |
+
name as tag, we pass the tag as registered_method_name to this method,
|
| 1080 |
+
then we can find the handler in registered_method_handlers based on
|
| 1081 |
+
the method name.
|
| 1082 |
+
|
| 1083 |
+
For call event with unregistered method, the method name will be included
|
| 1084 |
+
in rpc_event.call_details.method and we need to query the generics handlers
|
| 1085 |
+
to find the actual handler.
|
| 1086 |
+
"""
|
| 1087 |
+
if not rpc_event.success:
|
| 1088 |
+
return None, None
|
| 1089 |
+
if rpc_event.call_details.method or method_with_handler.name():
|
| 1090 |
+
rpc_state = _RPCState()
|
| 1091 |
+
try:
|
| 1092 |
+
method_handler = _find_method_handler(
|
| 1093 |
+
rpc_event,
|
| 1094 |
+
rpc_state,
|
| 1095 |
+
method_with_handler,
|
| 1096 |
+
interceptor_pipeline,
|
| 1097 |
+
)
|
| 1098 |
+
except Exception as exception: # pylint: disable=broad-except
|
| 1099 |
+
details = "Exception servicing handler: {}".format(exception)
|
| 1100 |
+
_LOGGER.exception(details)
|
| 1101 |
+
_reject_rpc(
|
| 1102 |
+
rpc_event,
|
| 1103 |
+
rpc_state,
|
| 1104 |
+
cygrpc.StatusCode.unknown,
|
| 1105 |
+
b"Error in service handler!",
|
| 1106 |
+
)
|
| 1107 |
+
return rpc_state, None
|
| 1108 |
+
if method_handler is None:
|
| 1109 |
+
_reject_rpc(
|
| 1110 |
+
rpc_event,
|
| 1111 |
+
rpc_state,
|
| 1112 |
+
cygrpc.StatusCode.unimplemented,
|
| 1113 |
+
b"Method not found!",
|
| 1114 |
+
)
|
| 1115 |
+
return rpc_state, None
|
| 1116 |
+
elif concurrency_exceeded:
|
| 1117 |
+
_reject_rpc(
|
| 1118 |
+
rpc_event,
|
| 1119 |
+
rpc_state,
|
| 1120 |
+
cygrpc.StatusCode.resource_exhausted,
|
| 1121 |
+
b"Concurrent RPC limit exceeded!",
|
| 1122 |
+
)
|
| 1123 |
+
return rpc_state, None
|
| 1124 |
+
else:
|
| 1125 |
+
return (
|
| 1126 |
+
rpc_state,
|
| 1127 |
+
_handle_with_method_handler(
|
| 1128 |
+
rpc_event, rpc_state, method_handler, thread_pool
|
| 1129 |
+
),
|
| 1130 |
+
)
|
| 1131 |
+
else:
|
| 1132 |
+
return None, None
|
| 1133 |
+
|
| 1134 |
+
|
| 1135 |
+
@enum.unique
|
| 1136 |
+
class _ServerStage(enum.Enum):
|
| 1137 |
+
STOPPED = "stopped"
|
| 1138 |
+
STARTED = "started"
|
| 1139 |
+
GRACE = "grace"
|
| 1140 |
+
|
| 1141 |
+
|
| 1142 |
+
class _ServerState(object):
|
| 1143 |
+
lock: threading.RLock
|
| 1144 |
+
completion_queue: cygrpc.CompletionQueue
|
| 1145 |
+
server: cygrpc.Server
|
| 1146 |
+
generic_handlers: List[grpc.GenericRpcHandler]
|
| 1147 |
+
registered_method_handlers: Dict[str, grpc.RpcMethodHandler]
|
| 1148 |
+
interceptor_pipeline: Optional[_interceptor._ServicePipeline]
|
| 1149 |
+
thread_pool: futures.ThreadPoolExecutor
|
| 1150 |
+
stage: _ServerStage
|
| 1151 |
+
termination_event: threading.Event
|
| 1152 |
+
shutdown_events: List[threading.Event]
|
| 1153 |
+
maximum_concurrent_rpcs: Optional[int]
|
| 1154 |
+
active_rpc_count: int
|
| 1155 |
+
rpc_states: Set[_RPCState]
|
| 1156 |
+
due: Set[str]
|
| 1157 |
+
server_deallocated: bool
|
| 1158 |
+
|
| 1159 |
+
# pylint: disable=too-many-arguments
|
| 1160 |
+
def __init__(
|
| 1161 |
+
self,
|
| 1162 |
+
completion_queue: cygrpc.CompletionQueue,
|
| 1163 |
+
server: cygrpc.Server,
|
| 1164 |
+
generic_handlers: Sequence[grpc.GenericRpcHandler],
|
| 1165 |
+
interceptor_pipeline: Optional[_interceptor._ServicePipeline],
|
| 1166 |
+
thread_pool: futures.ThreadPoolExecutor,
|
| 1167 |
+
maximum_concurrent_rpcs: Optional[int],
|
| 1168 |
+
):
|
| 1169 |
+
self.lock = threading.RLock()
|
| 1170 |
+
self.completion_queue = completion_queue
|
| 1171 |
+
self.server = server
|
| 1172 |
+
self.generic_handlers = list(generic_handlers)
|
| 1173 |
+
self.interceptor_pipeline = interceptor_pipeline
|
| 1174 |
+
self.thread_pool = thread_pool
|
| 1175 |
+
self.stage = _ServerStage.STOPPED
|
| 1176 |
+
self.termination_event = threading.Event()
|
| 1177 |
+
self.shutdown_events = [self.termination_event]
|
| 1178 |
+
self.maximum_concurrent_rpcs = maximum_concurrent_rpcs
|
| 1179 |
+
self.active_rpc_count = 0
|
| 1180 |
+
self.registered_method_handlers = {}
|
| 1181 |
+
|
| 1182 |
+
# TODO(https://github.com/grpc/grpc/issues/6597): eliminate these fields.
|
| 1183 |
+
self.rpc_states = set()
|
| 1184 |
+
self.due = set()
|
| 1185 |
+
|
| 1186 |
+
# A "volatile" flag to interrupt the daemon serving thread
|
| 1187 |
+
self.server_deallocated = False
|
| 1188 |
+
|
| 1189 |
+
|
| 1190 |
+
def _add_generic_handlers(
|
| 1191 |
+
state: _ServerState, generic_handlers: Iterable[grpc.GenericRpcHandler]
|
| 1192 |
+
) -> None:
|
| 1193 |
+
with state.lock:
|
| 1194 |
+
state.generic_handlers.extend(generic_handlers)
|
| 1195 |
+
|
| 1196 |
+
|
| 1197 |
+
def _add_registered_method_handlers(
|
| 1198 |
+
state: _ServerState, method_handlers: Dict[str, grpc.RpcMethodHandler]
|
| 1199 |
+
) -> None:
|
| 1200 |
+
with state.lock:
|
| 1201 |
+
state.registered_method_handlers.update(method_handlers)
|
| 1202 |
+
|
| 1203 |
+
|
| 1204 |
+
def _add_insecure_port(state: _ServerState, address: bytes) -> int:
|
| 1205 |
+
with state.lock:
|
| 1206 |
+
return state.server.add_http2_port(address)
|
| 1207 |
+
|
| 1208 |
+
|
| 1209 |
+
def _add_secure_port(
|
| 1210 |
+
state: _ServerState,
|
| 1211 |
+
address: bytes,
|
| 1212 |
+
server_credentials: grpc.ServerCredentials,
|
| 1213 |
+
) -> int:
|
| 1214 |
+
with state.lock:
|
| 1215 |
+
return state.server.add_http2_port(
|
| 1216 |
+
address, server_credentials._credentials
|
| 1217 |
+
)
|
| 1218 |
+
|
| 1219 |
+
|
| 1220 |
+
def _request_call(state: _ServerState) -> None:
|
| 1221 |
+
state.server.request_call(
|
| 1222 |
+
state.completion_queue, state.completion_queue, _REQUEST_CALL_TAG
|
| 1223 |
+
)
|
| 1224 |
+
state.due.add(_REQUEST_CALL_TAG)
|
| 1225 |
+
|
| 1226 |
+
|
| 1227 |
+
def _request_registered_call(state: _ServerState, method: str) -> None:
|
| 1228 |
+
registered_call_tag = method
|
| 1229 |
+
state.server.request_registered_call(
|
| 1230 |
+
state.completion_queue,
|
| 1231 |
+
state.completion_queue,
|
| 1232 |
+
method,
|
| 1233 |
+
registered_call_tag,
|
| 1234 |
+
)
|
| 1235 |
+
state.due.add(registered_call_tag)
|
| 1236 |
+
|
| 1237 |
+
|
| 1238 |
+
# TODO(https://github.com/grpc/grpc/issues/6597): delete this function.
|
| 1239 |
+
def _stop_serving(state: _ServerState) -> bool:
|
| 1240 |
+
if not state.rpc_states and not state.due:
|
| 1241 |
+
state.server.destroy()
|
| 1242 |
+
for shutdown_event in state.shutdown_events:
|
| 1243 |
+
shutdown_event.set()
|
| 1244 |
+
state.stage = _ServerStage.STOPPED
|
| 1245 |
+
return True
|
| 1246 |
+
else:
|
| 1247 |
+
return False
|
| 1248 |
+
|
| 1249 |
+
|
| 1250 |
+
def _on_call_completed(state: _ServerState) -> None:
|
| 1251 |
+
with state.lock:
|
| 1252 |
+
state.active_rpc_count -= 1
|
| 1253 |
+
|
| 1254 |
+
|
| 1255 |
+
# pylint: disable=too-many-branches
|
| 1256 |
+
def _process_event_and_continue(
|
| 1257 |
+
state: _ServerState, event: cygrpc.BaseEvent
|
| 1258 |
+
) -> bool:
|
| 1259 |
+
should_continue = True
|
| 1260 |
+
if event.tag is _SHUTDOWN_TAG:
|
| 1261 |
+
with state.lock:
|
| 1262 |
+
state.due.remove(_SHUTDOWN_TAG)
|
| 1263 |
+
if _stop_serving(state):
|
| 1264 |
+
should_continue = False
|
| 1265 |
+
elif (
|
| 1266 |
+
event.tag is _REQUEST_CALL_TAG
|
| 1267 |
+
or event.tag in state.registered_method_handlers.keys()
|
| 1268 |
+
):
|
| 1269 |
+
registered_method_name = None
|
| 1270 |
+
if event.tag in state.registered_method_handlers.keys():
|
| 1271 |
+
registered_method_name = event.tag
|
| 1272 |
+
method_with_handler = _RegisteredMethod(
|
| 1273 |
+
registered_method_name,
|
| 1274 |
+
state.registered_method_handlers.get(
|
| 1275 |
+
registered_method_name, None
|
| 1276 |
+
),
|
| 1277 |
+
)
|
| 1278 |
+
else:
|
| 1279 |
+
method_with_handler = _GenericMethod(
|
| 1280 |
+
state.generic_handlers,
|
| 1281 |
+
)
|
| 1282 |
+
with state.lock:
|
| 1283 |
+
state.due.remove(event.tag)
|
| 1284 |
+
concurrency_exceeded = (
|
| 1285 |
+
state.maximum_concurrent_rpcs is not None
|
| 1286 |
+
and state.active_rpc_count >= state.maximum_concurrent_rpcs
|
| 1287 |
+
)
|
| 1288 |
+
rpc_state, rpc_future = _handle_call(
|
| 1289 |
+
event,
|
| 1290 |
+
method_with_handler,
|
| 1291 |
+
state.interceptor_pipeline,
|
| 1292 |
+
state.thread_pool,
|
| 1293 |
+
concurrency_exceeded,
|
| 1294 |
+
)
|
| 1295 |
+
if rpc_state is not None:
|
| 1296 |
+
state.rpc_states.add(rpc_state)
|
| 1297 |
+
if rpc_future is not None:
|
| 1298 |
+
state.active_rpc_count += 1
|
| 1299 |
+
rpc_future.add_done_callback(
|
| 1300 |
+
lambda unused_future: _on_call_completed(state)
|
| 1301 |
+
)
|
| 1302 |
+
if state.stage is _ServerStage.STARTED:
|
| 1303 |
+
if (
|
| 1304 |
+
registered_method_name
|
| 1305 |
+
in state.registered_method_handlers.keys()
|
| 1306 |
+
):
|
| 1307 |
+
_request_registered_call(state, registered_method_name)
|
| 1308 |
+
else:
|
| 1309 |
+
_request_call(state)
|
| 1310 |
+
elif _stop_serving(state):
|
| 1311 |
+
should_continue = False
|
| 1312 |
+
else:
|
| 1313 |
+
rpc_state, callbacks = event.tag(event)
|
| 1314 |
+
for callback in callbacks:
|
| 1315 |
+
try:
|
| 1316 |
+
callback()
|
| 1317 |
+
except Exception: # pylint: disable=broad-except
|
| 1318 |
+
_LOGGER.exception("Exception calling callback!")
|
| 1319 |
+
if rpc_state is not None:
|
| 1320 |
+
with state.lock:
|
| 1321 |
+
state.rpc_states.remove(rpc_state)
|
| 1322 |
+
if _stop_serving(state):
|
| 1323 |
+
should_continue = False
|
| 1324 |
+
return should_continue
|
| 1325 |
+
|
| 1326 |
+
|
| 1327 |
+
def _serve(state: _ServerState) -> None:
|
| 1328 |
+
while True:
|
| 1329 |
+
timeout = time.time() + _DEALLOCATED_SERVER_CHECK_PERIOD_S
|
| 1330 |
+
event = state.completion_queue.poll(timeout)
|
| 1331 |
+
if state.server_deallocated:
|
| 1332 |
+
_begin_shutdown_once(state)
|
| 1333 |
+
if event.completion_type != cygrpc.CompletionType.queue_timeout:
|
| 1334 |
+
if not _process_event_and_continue(state, event):
|
| 1335 |
+
return
|
| 1336 |
+
# We want to force the deletion of the previous event
|
| 1337 |
+
# ~before~ we poll again; if the event has a reference
|
| 1338 |
+
# to a shutdown Call object, this can induce spinlock.
|
| 1339 |
+
event = None
|
| 1340 |
+
|
| 1341 |
+
|
| 1342 |
+
def _begin_shutdown_once(state: _ServerState) -> None:
|
| 1343 |
+
with state.lock:
|
| 1344 |
+
if state.stage is _ServerStage.STARTED:
|
| 1345 |
+
state.server.shutdown(state.completion_queue, _SHUTDOWN_TAG)
|
| 1346 |
+
state.stage = _ServerStage.GRACE
|
| 1347 |
+
state.due.add(_SHUTDOWN_TAG)
|
| 1348 |
+
|
| 1349 |
+
|
| 1350 |
+
def _stop(state: _ServerState, grace: Optional[float]) -> threading.Event:
|
| 1351 |
+
with state.lock:
|
| 1352 |
+
if state.stage is _ServerStage.STOPPED:
|
| 1353 |
+
shutdown_event = threading.Event()
|
| 1354 |
+
shutdown_event.set()
|
| 1355 |
+
return shutdown_event
|
| 1356 |
+
else:
|
| 1357 |
+
_begin_shutdown_once(state)
|
| 1358 |
+
shutdown_event = threading.Event()
|
| 1359 |
+
state.shutdown_events.append(shutdown_event)
|
| 1360 |
+
if grace is None:
|
| 1361 |
+
state.server.cancel_all_calls()
|
| 1362 |
+
else:
|
| 1363 |
+
|
| 1364 |
+
def cancel_all_calls_after_grace():
|
| 1365 |
+
shutdown_event.wait(timeout=grace)
|
| 1366 |
+
with state.lock:
|
| 1367 |
+
state.server.cancel_all_calls()
|
| 1368 |
+
|
| 1369 |
+
thread = threading.Thread(target=cancel_all_calls_after_grace)
|
| 1370 |
+
thread.start()
|
| 1371 |
+
return shutdown_event
|
| 1372 |
+
shutdown_event.wait()
|
| 1373 |
+
return shutdown_event
|
| 1374 |
+
|
| 1375 |
+
|
| 1376 |
+
def _start(state: _ServerState) -> None:
|
| 1377 |
+
with state.lock:
|
| 1378 |
+
if state.stage is not _ServerStage.STOPPED:
|
| 1379 |
+
raise ValueError("Cannot start already-started server!")
|
| 1380 |
+
state.server.start()
|
| 1381 |
+
state.stage = _ServerStage.STARTED
|
| 1382 |
+
# Request a call for each registered method so we can handle any of them.
|
| 1383 |
+
for method in state.registered_method_handlers.keys():
|
| 1384 |
+
_request_registered_call(state, method)
|
| 1385 |
+
# Also request a call for non-registered method.
|
| 1386 |
+
_request_call(state)
|
| 1387 |
+
thread = threading.Thread(target=_serve, args=(state,))
|
| 1388 |
+
thread.daemon = True
|
| 1389 |
+
thread.start()
|
| 1390 |
+
|
| 1391 |
+
|
| 1392 |
+
def _validate_generic_rpc_handlers(
|
| 1393 |
+
generic_rpc_handlers: Iterable[grpc.GenericRpcHandler],
|
| 1394 |
+
) -> None:
|
| 1395 |
+
for generic_rpc_handler in generic_rpc_handlers:
|
| 1396 |
+
service_attribute = getattr(generic_rpc_handler, "service", None)
|
| 1397 |
+
if service_attribute is None:
|
| 1398 |
+
raise AttributeError(
|
| 1399 |
+
'"{}" must conform to grpc.GenericRpcHandler type but does '
|
| 1400 |
+
'not have "service" method!'.format(generic_rpc_handler)
|
| 1401 |
+
)
|
| 1402 |
+
|
| 1403 |
+
|
| 1404 |
+
def _augment_options(
|
| 1405 |
+
base_options: Sequence[ChannelArgumentType],
|
| 1406 |
+
compression: Optional[grpc.Compression],
|
| 1407 |
+
xds: bool,
|
| 1408 |
+
) -> Sequence[ChannelArgumentType]:
|
| 1409 |
+
compression_option = _compression.create_channel_option(compression)
|
| 1410 |
+
maybe_server_call_tracer_factory_option = (
|
| 1411 |
+
_observability.create_server_call_tracer_factory_option(xds)
|
| 1412 |
+
)
|
| 1413 |
+
return (
|
| 1414 |
+
tuple(base_options)
|
| 1415 |
+
+ compression_option
|
| 1416 |
+
+ maybe_server_call_tracer_factory_option
|
| 1417 |
+
)
|
| 1418 |
+
|
| 1419 |
+
|
| 1420 |
+
class _Server(grpc.Server):
|
| 1421 |
+
_state: _ServerState
|
| 1422 |
+
|
| 1423 |
+
# pylint: disable=too-many-arguments
|
| 1424 |
+
def __init__(
|
| 1425 |
+
self,
|
| 1426 |
+
thread_pool: futures.ThreadPoolExecutor,
|
| 1427 |
+
generic_handlers: Sequence[grpc.GenericRpcHandler],
|
| 1428 |
+
interceptors: Sequence[grpc.ServerInterceptor],
|
| 1429 |
+
options: Sequence[ChannelArgumentType],
|
| 1430 |
+
maximum_concurrent_rpcs: Optional[int],
|
| 1431 |
+
compression: Optional[grpc.Compression],
|
| 1432 |
+
xds: bool,
|
| 1433 |
+
):
|
| 1434 |
+
completion_queue = cygrpc.CompletionQueue()
|
| 1435 |
+
server = cygrpc.Server(_augment_options(options, compression, xds), xds)
|
| 1436 |
+
server.register_completion_queue(completion_queue)
|
| 1437 |
+
self._state = _ServerState(
|
| 1438 |
+
completion_queue,
|
| 1439 |
+
server,
|
| 1440 |
+
generic_handlers,
|
| 1441 |
+
_interceptor.service_pipeline(interceptors),
|
| 1442 |
+
thread_pool,
|
| 1443 |
+
maximum_concurrent_rpcs,
|
| 1444 |
+
)
|
| 1445 |
+
self._cy_server = server
|
| 1446 |
+
|
| 1447 |
+
def add_generic_rpc_handlers(
|
| 1448 |
+
self, generic_rpc_handlers: Iterable[grpc.GenericRpcHandler]
|
| 1449 |
+
) -> None:
|
| 1450 |
+
_validate_generic_rpc_handlers(generic_rpc_handlers)
|
| 1451 |
+
_add_generic_handlers(self._state, generic_rpc_handlers)
|
| 1452 |
+
|
| 1453 |
+
def add_registered_method_handlers(
|
| 1454 |
+
self,
|
| 1455 |
+
service_name: str,
|
| 1456 |
+
method_handlers: Dict[str, grpc.RpcMethodHandler],
|
| 1457 |
+
) -> None:
|
| 1458 |
+
# Can't register method once server started.
|
| 1459 |
+
with self._state.lock:
|
| 1460 |
+
if self._state.stage is _ServerStage.STARTED:
|
| 1461 |
+
return
|
| 1462 |
+
|
| 1463 |
+
# TODO(xuanwn): We should validate method_handlers first.
|
| 1464 |
+
method_to_handlers = {
|
| 1465 |
+
_common.fully_qualified_method(service_name, method): method_handler
|
| 1466 |
+
for method, method_handler in method_handlers.items()
|
| 1467 |
+
}
|
| 1468 |
+
for fully_qualified_method in method_to_handlers.keys():
|
| 1469 |
+
self._cy_server.register_method(fully_qualified_method)
|
| 1470 |
+
_add_registered_method_handlers(self._state, method_to_handlers)
|
| 1471 |
+
|
| 1472 |
+
def add_insecure_port(self, address: str) -> int:
|
| 1473 |
+
return _common.validate_port_binding_result(
|
| 1474 |
+
address, _add_insecure_port(self._state, _common.encode(address))
|
| 1475 |
+
)
|
| 1476 |
+
|
| 1477 |
+
def add_secure_port(
|
| 1478 |
+
self, address: str, server_credentials: grpc.ServerCredentials
|
| 1479 |
+
) -> int:
|
| 1480 |
+
return _common.validate_port_binding_result(
|
| 1481 |
+
address,
|
| 1482 |
+
_add_secure_port(
|
| 1483 |
+
self._state, _common.encode(address), server_credentials
|
| 1484 |
+
),
|
| 1485 |
+
)
|
| 1486 |
+
|
| 1487 |
+
def start(self) -> None:
|
| 1488 |
+
_start(self._state)
|
| 1489 |
+
|
| 1490 |
+
def wait_for_termination(self, timeout: Optional[float] = None) -> bool:
|
| 1491 |
+
# NOTE(https://bugs.python.org/issue35935)
|
| 1492 |
+
# Remove this workaround once threading.Event.wait() is working with
|
| 1493 |
+
# CTRL+C across platforms.
|
| 1494 |
+
return _common.wait(
|
| 1495 |
+
self._state.termination_event.wait,
|
| 1496 |
+
self._state.termination_event.is_set,
|
| 1497 |
+
timeout=timeout,
|
| 1498 |
+
)
|
| 1499 |
+
|
| 1500 |
+
def stop(self, grace: Optional[float]) -> threading.Event:
|
| 1501 |
+
return _stop(self._state, grace)
|
| 1502 |
+
|
| 1503 |
+
def __del__(self):
|
| 1504 |
+
if hasattr(self, "_state"):
|
| 1505 |
+
# We can not grab a lock in __del__(), so set a flag to signal the
|
| 1506 |
+
# serving daemon thread (if it exists) to initiate shutdown.
|
| 1507 |
+
self._state.server_deallocated = True
|
| 1508 |
+
|
| 1509 |
+
|
| 1510 |
+
def create_server(
|
| 1511 |
+
thread_pool: futures.ThreadPoolExecutor,
|
| 1512 |
+
generic_rpc_handlers: Sequence[grpc.GenericRpcHandler],
|
| 1513 |
+
interceptors: Sequence[grpc.ServerInterceptor],
|
| 1514 |
+
options: Sequence[ChannelArgumentType],
|
| 1515 |
+
maximum_concurrent_rpcs: Optional[int],
|
| 1516 |
+
compression: Optional[grpc.Compression],
|
| 1517 |
+
xds: bool,
|
| 1518 |
+
) -> _Server:
|
| 1519 |
+
_validate_generic_rpc_handlers(generic_rpc_handlers)
|
| 1520 |
+
return _Server(
|
| 1521 |
+
thread_pool,
|
| 1522 |
+
generic_rpc_handlers,
|
| 1523 |
+
interceptors,
|
| 1524 |
+
options,
|
| 1525 |
+
maximum_concurrent_rpcs,
|
| 1526 |
+
compression,
|
| 1527 |
+
xds,
|
| 1528 |
+
)
|
.venv/lib/python3.11/site-packages/grpc/_simple_stubs.py
ADDED
|
@@ -0,0 +1,588 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Functions that obviate explicit stubs and explicit channels."""
|
| 15 |
+
|
| 16 |
+
import collections
|
| 17 |
+
import datetime
|
| 18 |
+
import logging
|
| 19 |
+
import os
|
| 20 |
+
import threading
|
| 21 |
+
from typing import (
|
| 22 |
+
Any,
|
| 23 |
+
AnyStr,
|
| 24 |
+
Callable,
|
| 25 |
+
Dict,
|
| 26 |
+
Iterator,
|
| 27 |
+
Optional,
|
| 28 |
+
Sequence,
|
| 29 |
+
Tuple,
|
| 30 |
+
TypeVar,
|
| 31 |
+
Union,
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
import grpc
|
| 35 |
+
from grpc.experimental import experimental_api
|
| 36 |
+
|
| 37 |
+
RequestType = TypeVar("RequestType")
|
| 38 |
+
ResponseType = TypeVar("ResponseType")
|
| 39 |
+
|
| 40 |
+
OptionsType = Sequence[Tuple[str, str]]
|
| 41 |
+
CacheKey = Tuple[
|
| 42 |
+
str,
|
| 43 |
+
OptionsType,
|
| 44 |
+
Optional[grpc.ChannelCredentials],
|
| 45 |
+
Optional[grpc.Compression],
|
| 46 |
+
]
|
| 47 |
+
|
| 48 |
+
_LOGGER = logging.getLogger(__name__)
|
| 49 |
+
|
| 50 |
+
_EVICTION_PERIOD_KEY = "GRPC_PYTHON_MANAGED_CHANNEL_EVICTION_SECONDS"
|
| 51 |
+
if _EVICTION_PERIOD_KEY in os.environ:
|
| 52 |
+
_EVICTION_PERIOD = datetime.timedelta(
|
| 53 |
+
seconds=float(os.environ[_EVICTION_PERIOD_KEY])
|
| 54 |
+
)
|
| 55 |
+
_LOGGER.debug(
|
| 56 |
+
"Setting managed channel eviction period to %s", _EVICTION_PERIOD
|
| 57 |
+
)
|
| 58 |
+
else:
|
| 59 |
+
_EVICTION_PERIOD = datetime.timedelta(minutes=10)
|
| 60 |
+
|
| 61 |
+
_MAXIMUM_CHANNELS_KEY = "GRPC_PYTHON_MANAGED_CHANNEL_MAXIMUM"
|
| 62 |
+
if _MAXIMUM_CHANNELS_KEY in os.environ:
|
| 63 |
+
_MAXIMUM_CHANNELS = int(os.environ[_MAXIMUM_CHANNELS_KEY])
|
| 64 |
+
_LOGGER.debug("Setting maximum managed channels to %d", _MAXIMUM_CHANNELS)
|
| 65 |
+
else:
|
| 66 |
+
_MAXIMUM_CHANNELS = 2**8
|
| 67 |
+
|
| 68 |
+
_DEFAULT_TIMEOUT_KEY = "GRPC_PYTHON_DEFAULT_TIMEOUT_SECONDS"
|
| 69 |
+
if _DEFAULT_TIMEOUT_KEY in os.environ:
|
| 70 |
+
_DEFAULT_TIMEOUT = float(os.environ[_DEFAULT_TIMEOUT_KEY])
|
| 71 |
+
_LOGGER.debug("Setting default timeout seconds to %f", _DEFAULT_TIMEOUT)
|
| 72 |
+
else:
|
| 73 |
+
_DEFAULT_TIMEOUT = 60.0
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def _create_channel(
|
| 77 |
+
target: str,
|
| 78 |
+
options: Sequence[Tuple[str, str]],
|
| 79 |
+
channel_credentials: Optional[grpc.ChannelCredentials],
|
| 80 |
+
compression: Optional[grpc.Compression],
|
| 81 |
+
) -> grpc.Channel:
|
| 82 |
+
_LOGGER.debug(
|
| 83 |
+
f"Creating secure channel with credentials '{channel_credentials}', "
|
| 84 |
+
+ f"options '{options}' and compression '{compression}'"
|
| 85 |
+
)
|
| 86 |
+
return grpc.secure_channel(
|
| 87 |
+
target,
|
| 88 |
+
credentials=channel_credentials,
|
| 89 |
+
options=options,
|
| 90 |
+
compression=compression,
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
class ChannelCache:
|
| 95 |
+
# NOTE(rbellevi): Untyped due to reference cycle.
|
| 96 |
+
_singleton = None
|
| 97 |
+
_lock: threading.RLock = threading.RLock()
|
| 98 |
+
_condition: threading.Condition = threading.Condition(lock=_lock)
|
| 99 |
+
_eviction_ready: threading.Event = threading.Event()
|
| 100 |
+
|
| 101 |
+
_mapping: Dict[CacheKey, Tuple[grpc.Channel, datetime.datetime]]
|
| 102 |
+
_eviction_thread: threading.Thread
|
| 103 |
+
|
| 104 |
+
def __init__(self):
|
| 105 |
+
self._mapping = collections.OrderedDict()
|
| 106 |
+
self._eviction_thread = threading.Thread(
|
| 107 |
+
target=ChannelCache._perform_evictions, daemon=True
|
| 108 |
+
)
|
| 109 |
+
self._eviction_thread.start()
|
| 110 |
+
|
| 111 |
+
@staticmethod
|
| 112 |
+
def get():
|
| 113 |
+
with ChannelCache._lock:
|
| 114 |
+
if ChannelCache._singleton is None:
|
| 115 |
+
ChannelCache._singleton = ChannelCache()
|
| 116 |
+
ChannelCache._eviction_ready.wait()
|
| 117 |
+
return ChannelCache._singleton
|
| 118 |
+
|
| 119 |
+
def _evict_locked(self, key: CacheKey):
|
| 120 |
+
channel, _ = self._mapping.pop(key)
|
| 121 |
+
_LOGGER.debug(
|
| 122 |
+
"Evicting channel %s with configuration %s.", channel, key
|
| 123 |
+
)
|
| 124 |
+
channel.close()
|
| 125 |
+
del channel
|
| 126 |
+
|
| 127 |
+
@staticmethod
|
| 128 |
+
def _perform_evictions():
|
| 129 |
+
while True:
|
| 130 |
+
with ChannelCache._lock:
|
| 131 |
+
ChannelCache._eviction_ready.set()
|
| 132 |
+
if not ChannelCache._singleton._mapping:
|
| 133 |
+
ChannelCache._condition.wait()
|
| 134 |
+
elif len(ChannelCache._singleton._mapping) > _MAXIMUM_CHANNELS:
|
| 135 |
+
key = next(iter(ChannelCache._singleton._mapping.keys()))
|
| 136 |
+
ChannelCache._singleton._evict_locked(key)
|
| 137 |
+
# And immediately reevaluate.
|
| 138 |
+
else:
|
| 139 |
+
key, (_, eviction_time) = next(
|
| 140 |
+
iter(ChannelCache._singleton._mapping.items())
|
| 141 |
+
)
|
| 142 |
+
now = datetime.datetime.now()
|
| 143 |
+
if eviction_time <= now:
|
| 144 |
+
ChannelCache._singleton._evict_locked(key)
|
| 145 |
+
continue
|
| 146 |
+
else:
|
| 147 |
+
time_to_eviction = (eviction_time - now).total_seconds()
|
| 148 |
+
# NOTE: We aim to *eventually* coalesce to a state in
|
| 149 |
+
# which no overdue channels are in the cache and the
|
| 150 |
+
# length of the cache is longer than _MAXIMUM_CHANNELS.
|
| 151 |
+
# We tolerate momentary states in which these two
|
| 152 |
+
# criteria are not met.
|
| 153 |
+
ChannelCache._condition.wait(timeout=time_to_eviction)
|
| 154 |
+
|
| 155 |
+
def get_channel(
|
| 156 |
+
self,
|
| 157 |
+
target: str,
|
| 158 |
+
options: Sequence[Tuple[str, str]],
|
| 159 |
+
channel_credentials: Optional[grpc.ChannelCredentials],
|
| 160 |
+
insecure: bool,
|
| 161 |
+
compression: Optional[grpc.Compression],
|
| 162 |
+
method: str,
|
| 163 |
+
_registered_method: bool,
|
| 164 |
+
) -> Tuple[grpc.Channel, Optional[int]]:
|
| 165 |
+
"""Get a channel from cache or creates a new channel.
|
| 166 |
+
|
| 167 |
+
This method also takes care of register method for channel,
|
| 168 |
+
which means we'll register a new call handle if we're calling a
|
| 169 |
+
non-registered method for an existing channel.
|
| 170 |
+
|
| 171 |
+
Returns:
|
| 172 |
+
A tuple with two items. The first item is the channel, second item is
|
| 173 |
+
the call handle if the method is registered, None if it's not registered.
|
| 174 |
+
"""
|
| 175 |
+
if insecure and channel_credentials:
|
| 176 |
+
raise ValueError(
|
| 177 |
+
"The insecure option is mutually exclusive with "
|
| 178 |
+
+ "the channel_credentials option. Please use one "
|
| 179 |
+
+ "or the other."
|
| 180 |
+
)
|
| 181 |
+
if insecure:
|
| 182 |
+
channel_credentials = (
|
| 183 |
+
grpc.experimental.insecure_channel_credentials()
|
| 184 |
+
)
|
| 185 |
+
elif channel_credentials is None:
|
| 186 |
+
_LOGGER.debug("Defaulting to SSL channel credentials.")
|
| 187 |
+
channel_credentials = grpc.ssl_channel_credentials()
|
| 188 |
+
key = (target, options, channel_credentials, compression)
|
| 189 |
+
with self._lock:
|
| 190 |
+
channel_data = self._mapping.get(key, None)
|
| 191 |
+
call_handle = None
|
| 192 |
+
if channel_data is not None:
|
| 193 |
+
channel = channel_data[0]
|
| 194 |
+
# Register a new call handle if we're calling a registered method for an
|
| 195 |
+
# existing channel and this method is not registered.
|
| 196 |
+
if _registered_method:
|
| 197 |
+
call_handle = channel._get_registered_call_handle(method)
|
| 198 |
+
self._mapping.pop(key)
|
| 199 |
+
self._mapping[key] = (
|
| 200 |
+
channel,
|
| 201 |
+
datetime.datetime.now() + _EVICTION_PERIOD,
|
| 202 |
+
)
|
| 203 |
+
return channel, call_handle
|
| 204 |
+
else:
|
| 205 |
+
channel = _create_channel(
|
| 206 |
+
target, options, channel_credentials, compression
|
| 207 |
+
)
|
| 208 |
+
if _registered_method:
|
| 209 |
+
call_handle = channel._get_registered_call_handle(method)
|
| 210 |
+
self._mapping[key] = (
|
| 211 |
+
channel,
|
| 212 |
+
datetime.datetime.now() + _EVICTION_PERIOD,
|
| 213 |
+
)
|
| 214 |
+
if (
|
| 215 |
+
len(self._mapping) == 1
|
| 216 |
+
or len(self._mapping) >= _MAXIMUM_CHANNELS
|
| 217 |
+
):
|
| 218 |
+
self._condition.notify()
|
| 219 |
+
return channel, call_handle
|
| 220 |
+
|
| 221 |
+
def _test_only_channel_count(self) -> int:
|
| 222 |
+
with self._lock:
|
| 223 |
+
return len(self._mapping)
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
@experimental_api
|
| 227 |
+
# pylint: disable=too-many-locals
|
| 228 |
+
def unary_unary(
|
| 229 |
+
request: RequestType,
|
| 230 |
+
target: str,
|
| 231 |
+
method: str,
|
| 232 |
+
request_serializer: Optional[Callable[[Any], bytes]] = None,
|
| 233 |
+
response_deserializer: Optional[Callable[[bytes], Any]] = None,
|
| 234 |
+
options: Sequence[Tuple[AnyStr, AnyStr]] = (),
|
| 235 |
+
channel_credentials: Optional[grpc.ChannelCredentials] = None,
|
| 236 |
+
insecure: bool = False,
|
| 237 |
+
call_credentials: Optional[grpc.CallCredentials] = None,
|
| 238 |
+
compression: Optional[grpc.Compression] = None,
|
| 239 |
+
wait_for_ready: Optional[bool] = None,
|
| 240 |
+
timeout: Optional[float] = _DEFAULT_TIMEOUT,
|
| 241 |
+
metadata: Optional[Sequence[Tuple[str, Union[str, bytes]]]] = None,
|
| 242 |
+
_registered_method: Optional[bool] = False,
|
| 243 |
+
) -> ResponseType:
|
| 244 |
+
"""Invokes a unary-unary RPC without an explicitly specified channel.
|
| 245 |
+
|
| 246 |
+
THIS IS AN EXPERIMENTAL API.
|
| 247 |
+
|
| 248 |
+
This is backed by a per-process cache of channels. Channels are evicted
|
| 249 |
+
from the cache after a fixed period by a background. Channels will also be
|
| 250 |
+
evicted if more than a configured maximum accumulate.
|
| 251 |
+
|
| 252 |
+
The default eviction period is 10 minutes. One may set the environment
|
| 253 |
+
variable "GRPC_PYTHON_MANAGED_CHANNEL_EVICTION_SECONDS" to configure this.
|
| 254 |
+
|
| 255 |
+
The default maximum number of channels is 256. One may set the
|
| 256 |
+
environment variable "GRPC_PYTHON_MANAGED_CHANNEL_MAXIMUM" to configure
|
| 257 |
+
this.
|
| 258 |
+
|
| 259 |
+
Args:
|
| 260 |
+
request: An iterator that yields request values for the RPC.
|
| 261 |
+
target: The server address.
|
| 262 |
+
method: The name of the RPC method.
|
| 263 |
+
request_serializer: Optional :term:`serializer` for serializing the request
|
| 264 |
+
message. Request goes unserialized in case None is passed.
|
| 265 |
+
response_deserializer: Optional :term:`deserializer` for deserializing the response
|
| 266 |
+
message. Response goes undeserialized in case None is passed.
|
| 267 |
+
options: An optional list of key-value pairs (:term:`channel_arguments` in gRPC Core
|
| 268 |
+
runtime) to configure the channel.
|
| 269 |
+
channel_credentials: A credential applied to the whole channel, e.g. the
|
| 270 |
+
return value of grpc.ssl_channel_credentials() or
|
| 271 |
+
grpc.insecure_channel_credentials().
|
| 272 |
+
insecure: If True, specifies channel_credentials as
|
| 273 |
+
:term:`grpc.insecure_channel_credentials()`. This option is mutually
|
| 274 |
+
exclusive with the `channel_credentials` option.
|
| 275 |
+
call_credentials: A call credential applied to each call individually,
|
| 276 |
+
e.g. the output of grpc.metadata_call_credentials() or
|
| 277 |
+
grpc.access_token_call_credentials().
|
| 278 |
+
compression: An optional value indicating the compression method to be
|
| 279 |
+
used over the lifetime of the channel, e.g. grpc.Compression.Gzip.
|
| 280 |
+
wait_for_ready: An optional flag indicating whether the RPC should fail
|
| 281 |
+
immediately if the connection is not ready at the time the RPC is
|
| 282 |
+
invoked, or if it should wait until the connection to the server
|
| 283 |
+
becomes ready. When using this option, the user will likely also want
|
| 284 |
+
to set a timeout. Defaults to True.
|
| 285 |
+
timeout: An optional duration of time in seconds to allow for the RPC,
|
| 286 |
+
after which an exception will be raised. If timeout is unspecified,
|
| 287 |
+
defaults to a timeout controlled by the
|
| 288 |
+
GRPC_PYTHON_DEFAULT_TIMEOUT_SECONDS environment variable. If that is
|
| 289 |
+
unset, defaults to 60 seconds. Supply a value of None to indicate that
|
| 290 |
+
no timeout should be enforced.
|
| 291 |
+
metadata: Optional metadata to send to the server.
|
| 292 |
+
|
| 293 |
+
Returns:
|
| 294 |
+
The response to the RPC.
|
| 295 |
+
"""
|
| 296 |
+
channel, method_handle = ChannelCache.get().get_channel(
|
| 297 |
+
target,
|
| 298 |
+
options,
|
| 299 |
+
channel_credentials,
|
| 300 |
+
insecure,
|
| 301 |
+
compression,
|
| 302 |
+
method,
|
| 303 |
+
_registered_method,
|
| 304 |
+
)
|
| 305 |
+
multicallable = channel.unary_unary(
|
| 306 |
+
method, request_serializer, response_deserializer, method_handle
|
| 307 |
+
)
|
| 308 |
+
wait_for_ready = wait_for_ready if wait_for_ready is not None else True
|
| 309 |
+
return multicallable(
|
| 310 |
+
request,
|
| 311 |
+
metadata=metadata,
|
| 312 |
+
wait_for_ready=wait_for_ready,
|
| 313 |
+
credentials=call_credentials,
|
| 314 |
+
timeout=timeout,
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
@experimental_api
|
| 319 |
+
# pylint: disable=too-many-locals
|
| 320 |
+
def unary_stream(
|
| 321 |
+
request: RequestType,
|
| 322 |
+
target: str,
|
| 323 |
+
method: str,
|
| 324 |
+
request_serializer: Optional[Callable[[Any], bytes]] = None,
|
| 325 |
+
response_deserializer: Optional[Callable[[bytes], Any]] = None,
|
| 326 |
+
options: Sequence[Tuple[AnyStr, AnyStr]] = (),
|
| 327 |
+
channel_credentials: Optional[grpc.ChannelCredentials] = None,
|
| 328 |
+
insecure: bool = False,
|
| 329 |
+
call_credentials: Optional[grpc.CallCredentials] = None,
|
| 330 |
+
compression: Optional[grpc.Compression] = None,
|
| 331 |
+
wait_for_ready: Optional[bool] = None,
|
| 332 |
+
timeout: Optional[float] = _DEFAULT_TIMEOUT,
|
| 333 |
+
metadata: Optional[Sequence[Tuple[str, Union[str, bytes]]]] = None,
|
| 334 |
+
_registered_method: Optional[bool] = False,
|
| 335 |
+
) -> Iterator[ResponseType]:
|
| 336 |
+
"""Invokes a unary-stream RPC without an explicitly specified channel.
|
| 337 |
+
|
| 338 |
+
THIS IS AN EXPERIMENTAL API.
|
| 339 |
+
|
| 340 |
+
This is backed by a per-process cache of channels. Channels are evicted
|
| 341 |
+
from the cache after a fixed period by a background. Channels will also be
|
| 342 |
+
evicted if more than a configured maximum accumulate.
|
| 343 |
+
|
| 344 |
+
The default eviction period is 10 minutes. One may set the environment
|
| 345 |
+
variable "GRPC_PYTHON_MANAGED_CHANNEL_EVICTION_SECONDS" to configure this.
|
| 346 |
+
|
| 347 |
+
The default maximum number of channels is 256. One may set the
|
| 348 |
+
environment variable "GRPC_PYTHON_MANAGED_CHANNEL_MAXIMUM" to configure
|
| 349 |
+
this.
|
| 350 |
+
|
| 351 |
+
Args:
|
| 352 |
+
request: An iterator that yields request values for the RPC.
|
| 353 |
+
target: The server address.
|
| 354 |
+
method: The name of the RPC method.
|
| 355 |
+
request_serializer: Optional :term:`serializer` for serializing the request
|
| 356 |
+
message. Request goes unserialized in case None is passed.
|
| 357 |
+
response_deserializer: Optional :term:`deserializer` for deserializing the response
|
| 358 |
+
message. Response goes undeserialized in case None is passed.
|
| 359 |
+
options: An optional list of key-value pairs (:term:`channel_arguments` in gRPC Core
|
| 360 |
+
runtime) to configure the channel.
|
| 361 |
+
channel_credentials: A credential applied to the whole channel, e.g. the
|
| 362 |
+
return value of grpc.ssl_channel_credentials().
|
| 363 |
+
insecure: If True, specifies channel_credentials as
|
| 364 |
+
:term:`grpc.insecure_channel_credentials()`. This option is mutually
|
| 365 |
+
exclusive with the `channel_credentials` option.
|
| 366 |
+
call_credentials: A call credential applied to each call individually,
|
| 367 |
+
e.g. the output of grpc.metadata_call_credentials() or
|
| 368 |
+
grpc.access_token_call_credentials().
|
| 369 |
+
compression: An optional value indicating the compression method to be
|
| 370 |
+
used over the lifetime of the channel, e.g. grpc.Compression.Gzip.
|
| 371 |
+
wait_for_ready: An optional flag indicating whether the RPC should fail
|
| 372 |
+
immediately if the connection is not ready at the time the RPC is
|
| 373 |
+
invoked, or if it should wait until the connection to the server
|
| 374 |
+
becomes ready. When using this option, the user will likely also want
|
| 375 |
+
to set a timeout. Defaults to True.
|
| 376 |
+
timeout: An optional duration of time in seconds to allow for the RPC,
|
| 377 |
+
after which an exception will be raised. If timeout is unspecified,
|
| 378 |
+
defaults to a timeout controlled by the
|
| 379 |
+
GRPC_PYTHON_DEFAULT_TIMEOUT_SECONDS environment variable. If that is
|
| 380 |
+
unset, defaults to 60 seconds. Supply a value of None to indicate that
|
| 381 |
+
no timeout should be enforced.
|
| 382 |
+
metadata: Optional metadata to send to the server.
|
| 383 |
+
|
| 384 |
+
Returns:
|
| 385 |
+
An iterator of responses.
|
| 386 |
+
"""
|
| 387 |
+
channel, method_handle = ChannelCache.get().get_channel(
|
| 388 |
+
target,
|
| 389 |
+
options,
|
| 390 |
+
channel_credentials,
|
| 391 |
+
insecure,
|
| 392 |
+
compression,
|
| 393 |
+
method,
|
| 394 |
+
_registered_method,
|
| 395 |
+
)
|
| 396 |
+
multicallable = channel.unary_stream(
|
| 397 |
+
method, request_serializer, response_deserializer, method_handle
|
| 398 |
+
)
|
| 399 |
+
wait_for_ready = wait_for_ready if wait_for_ready is not None else True
|
| 400 |
+
return multicallable(
|
| 401 |
+
request,
|
| 402 |
+
metadata=metadata,
|
| 403 |
+
wait_for_ready=wait_for_ready,
|
| 404 |
+
credentials=call_credentials,
|
| 405 |
+
timeout=timeout,
|
| 406 |
+
)
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
@experimental_api
|
| 410 |
+
# pylint: disable=too-many-locals
|
| 411 |
+
def stream_unary(
|
| 412 |
+
request_iterator: Iterator[RequestType],
|
| 413 |
+
target: str,
|
| 414 |
+
method: str,
|
| 415 |
+
request_serializer: Optional[Callable[[Any], bytes]] = None,
|
| 416 |
+
response_deserializer: Optional[Callable[[bytes], Any]] = None,
|
| 417 |
+
options: Sequence[Tuple[AnyStr, AnyStr]] = (),
|
| 418 |
+
channel_credentials: Optional[grpc.ChannelCredentials] = None,
|
| 419 |
+
insecure: bool = False,
|
| 420 |
+
call_credentials: Optional[grpc.CallCredentials] = None,
|
| 421 |
+
compression: Optional[grpc.Compression] = None,
|
| 422 |
+
wait_for_ready: Optional[bool] = None,
|
| 423 |
+
timeout: Optional[float] = _DEFAULT_TIMEOUT,
|
| 424 |
+
metadata: Optional[Sequence[Tuple[str, Union[str, bytes]]]] = None,
|
| 425 |
+
_registered_method: Optional[bool] = False,
|
| 426 |
+
) -> ResponseType:
|
| 427 |
+
"""Invokes a stream-unary RPC without an explicitly specified channel.
|
| 428 |
+
|
| 429 |
+
THIS IS AN EXPERIMENTAL API.
|
| 430 |
+
|
| 431 |
+
This is backed by a per-process cache of channels. Channels are evicted
|
| 432 |
+
from the cache after a fixed period by a background. Channels will also be
|
| 433 |
+
evicted if more than a configured maximum accumulate.
|
| 434 |
+
|
| 435 |
+
The default eviction period is 10 minutes. One may set the environment
|
| 436 |
+
variable "GRPC_PYTHON_MANAGED_CHANNEL_EVICTION_SECONDS" to configure this.
|
| 437 |
+
|
| 438 |
+
The default maximum number of channels is 256. One may set the
|
| 439 |
+
environment variable "GRPC_PYTHON_MANAGED_CHANNEL_MAXIMUM" to configure
|
| 440 |
+
this.
|
| 441 |
+
|
| 442 |
+
Args:
|
| 443 |
+
request_iterator: An iterator that yields request values for the RPC.
|
| 444 |
+
target: The server address.
|
| 445 |
+
method: The name of the RPC method.
|
| 446 |
+
request_serializer: Optional :term:`serializer` for serializing the request
|
| 447 |
+
message. Request goes unserialized in case None is passed.
|
| 448 |
+
response_deserializer: Optional :term:`deserializer` for deserializing the response
|
| 449 |
+
message. Response goes undeserialized in case None is passed.
|
| 450 |
+
options: An optional list of key-value pairs (:term:`channel_arguments` in gRPC Core
|
| 451 |
+
runtime) to configure the channel.
|
| 452 |
+
channel_credentials: A credential applied to the whole channel, e.g. the
|
| 453 |
+
return value of grpc.ssl_channel_credentials().
|
| 454 |
+
call_credentials: A call credential applied to each call individually,
|
| 455 |
+
e.g. the output of grpc.metadata_call_credentials() or
|
| 456 |
+
grpc.access_token_call_credentials().
|
| 457 |
+
insecure: If True, specifies channel_credentials as
|
| 458 |
+
:term:`grpc.insecure_channel_credentials()`. This option is mutually
|
| 459 |
+
exclusive with the `channel_credentials` option.
|
| 460 |
+
compression: An optional value indicating the compression method to be
|
| 461 |
+
used over the lifetime of the channel, e.g. grpc.Compression.Gzip.
|
| 462 |
+
wait_for_ready: An optional flag indicating whether the RPC should fail
|
| 463 |
+
immediately if the connection is not ready at the time the RPC is
|
| 464 |
+
invoked, or if it should wait until the connection to the server
|
| 465 |
+
becomes ready. When using this option, the user will likely also want
|
| 466 |
+
to set a timeout. Defaults to True.
|
| 467 |
+
timeout: An optional duration of time in seconds to allow for the RPC,
|
| 468 |
+
after which an exception will be raised. If timeout is unspecified,
|
| 469 |
+
defaults to a timeout controlled by the
|
| 470 |
+
GRPC_PYTHON_DEFAULT_TIMEOUT_SECONDS environment variable. If that is
|
| 471 |
+
unset, defaults to 60 seconds. Supply a value of None to indicate that
|
| 472 |
+
no timeout should be enforced.
|
| 473 |
+
metadata: Optional metadata to send to the server.
|
| 474 |
+
|
| 475 |
+
Returns:
|
| 476 |
+
The response to the RPC.
|
| 477 |
+
"""
|
| 478 |
+
channel, method_handle = ChannelCache.get().get_channel(
|
| 479 |
+
target,
|
| 480 |
+
options,
|
| 481 |
+
channel_credentials,
|
| 482 |
+
insecure,
|
| 483 |
+
compression,
|
| 484 |
+
method,
|
| 485 |
+
_registered_method,
|
| 486 |
+
)
|
| 487 |
+
multicallable = channel.stream_unary(
|
| 488 |
+
method, request_serializer, response_deserializer, method_handle
|
| 489 |
+
)
|
| 490 |
+
wait_for_ready = wait_for_ready if wait_for_ready is not None else True
|
| 491 |
+
return multicallable(
|
| 492 |
+
request_iterator,
|
| 493 |
+
metadata=metadata,
|
| 494 |
+
wait_for_ready=wait_for_ready,
|
| 495 |
+
credentials=call_credentials,
|
| 496 |
+
timeout=timeout,
|
| 497 |
+
)
|
| 498 |
+
|
| 499 |
+
|
| 500 |
+
@experimental_api
|
| 501 |
+
# pylint: disable=too-many-locals
|
| 502 |
+
def stream_stream(
|
| 503 |
+
request_iterator: Iterator[RequestType],
|
| 504 |
+
target: str,
|
| 505 |
+
method: str,
|
| 506 |
+
request_serializer: Optional[Callable[[Any], bytes]] = None,
|
| 507 |
+
response_deserializer: Optional[Callable[[bytes], Any]] = None,
|
| 508 |
+
options: Sequence[Tuple[AnyStr, AnyStr]] = (),
|
| 509 |
+
channel_credentials: Optional[grpc.ChannelCredentials] = None,
|
| 510 |
+
insecure: bool = False,
|
| 511 |
+
call_credentials: Optional[grpc.CallCredentials] = None,
|
| 512 |
+
compression: Optional[grpc.Compression] = None,
|
| 513 |
+
wait_for_ready: Optional[bool] = None,
|
| 514 |
+
timeout: Optional[float] = _DEFAULT_TIMEOUT,
|
| 515 |
+
metadata: Optional[Sequence[Tuple[str, Union[str, bytes]]]] = None,
|
| 516 |
+
_registered_method: Optional[bool] = False,
|
| 517 |
+
) -> Iterator[ResponseType]:
|
| 518 |
+
"""Invokes a stream-stream RPC without an explicitly specified channel.
|
| 519 |
+
|
| 520 |
+
THIS IS AN EXPERIMENTAL API.
|
| 521 |
+
|
| 522 |
+
This is backed by a per-process cache of channels. Channels are evicted
|
| 523 |
+
from the cache after a fixed period by a background. Channels will also be
|
| 524 |
+
evicted if more than a configured maximum accumulate.
|
| 525 |
+
|
| 526 |
+
The default eviction period is 10 minutes. One may set the environment
|
| 527 |
+
variable "GRPC_PYTHON_MANAGED_CHANNEL_EVICTION_SECONDS" to configure this.
|
| 528 |
+
|
| 529 |
+
The default maximum number of channels is 256. One may set the
|
| 530 |
+
environment variable "GRPC_PYTHON_MANAGED_CHANNEL_MAXIMUM" to configure
|
| 531 |
+
this.
|
| 532 |
+
|
| 533 |
+
Args:
|
| 534 |
+
request_iterator: An iterator that yields request values for the RPC.
|
| 535 |
+
target: The server address.
|
| 536 |
+
method: The name of the RPC method.
|
| 537 |
+
request_serializer: Optional :term:`serializer` for serializing the request
|
| 538 |
+
message. Request goes unserialized in case None is passed.
|
| 539 |
+
response_deserializer: Optional :term:`deserializer` for deserializing the response
|
| 540 |
+
message. Response goes undeserialized in case None is passed.
|
| 541 |
+
options: An optional list of key-value pairs (:term:`channel_arguments` in gRPC Core
|
| 542 |
+
runtime) to configure the channel.
|
| 543 |
+
channel_credentials: A credential applied to the whole channel, e.g. the
|
| 544 |
+
return value of grpc.ssl_channel_credentials().
|
| 545 |
+
call_credentials: A call credential applied to each call individually,
|
| 546 |
+
e.g. the output of grpc.metadata_call_credentials() or
|
| 547 |
+
grpc.access_token_call_credentials().
|
| 548 |
+
insecure: If True, specifies channel_credentials as
|
| 549 |
+
:term:`grpc.insecure_channel_credentials()`. This option is mutually
|
| 550 |
+
exclusive with the `channel_credentials` option.
|
| 551 |
+
compression: An optional value indicating the compression method to be
|
| 552 |
+
used over the lifetime of the channel, e.g. grpc.Compression.Gzip.
|
| 553 |
+
wait_for_ready: An optional flag indicating whether the RPC should fail
|
| 554 |
+
immediately if the connection is not ready at the time the RPC is
|
| 555 |
+
invoked, or if it should wait until the connection to the server
|
| 556 |
+
becomes ready. When using this option, the user will likely also want
|
| 557 |
+
to set a timeout. Defaults to True.
|
| 558 |
+
timeout: An optional duration of time in seconds to allow for the RPC,
|
| 559 |
+
after which an exception will be raised. If timeout is unspecified,
|
| 560 |
+
defaults to a timeout controlled by the
|
| 561 |
+
GRPC_PYTHON_DEFAULT_TIMEOUT_SECONDS environment variable. If that is
|
| 562 |
+
unset, defaults to 60 seconds. Supply a value of None to indicate that
|
| 563 |
+
no timeout should be enforced.
|
| 564 |
+
metadata: Optional metadata to send to the server.
|
| 565 |
+
|
| 566 |
+
Returns:
|
| 567 |
+
An iterator of responses.
|
| 568 |
+
"""
|
| 569 |
+
channel, method_handle = ChannelCache.get().get_channel(
|
| 570 |
+
target,
|
| 571 |
+
options,
|
| 572 |
+
channel_credentials,
|
| 573 |
+
insecure,
|
| 574 |
+
compression,
|
| 575 |
+
method,
|
| 576 |
+
_registered_method,
|
| 577 |
+
)
|
| 578 |
+
multicallable = channel.stream_stream(
|
| 579 |
+
method, request_serializer, response_deserializer, method_handle
|
| 580 |
+
)
|
| 581 |
+
wait_for_ready = wait_for_ready if wait_for_ready is not None else True
|
| 582 |
+
return multicallable(
|
| 583 |
+
request_iterator,
|
| 584 |
+
metadata=metadata,
|
| 585 |
+
wait_for_ready=wait_for_ready,
|
| 586 |
+
credentials=call_credentials,
|
| 587 |
+
timeout=timeout,
|
| 588 |
+
)
|
.venv/lib/python3.11/site-packages/grpc/_typing.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Common types for gRPC Sync API"""
|
| 15 |
+
|
| 16 |
+
from typing import (
|
| 17 |
+
TYPE_CHECKING,
|
| 18 |
+
Any,
|
| 19 |
+
Callable,
|
| 20 |
+
Iterable,
|
| 21 |
+
Iterator,
|
| 22 |
+
Optional,
|
| 23 |
+
Sequence,
|
| 24 |
+
Tuple,
|
| 25 |
+
TypeVar,
|
| 26 |
+
Union,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
from grpc._cython import cygrpc
|
| 30 |
+
|
| 31 |
+
if TYPE_CHECKING:
|
| 32 |
+
from grpc import ServicerContext
|
| 33 |
+
from grpc._server import _RPCState
|
| 34 |
+
|
| 35 |
+
RequestType = TypeVar("RequestType")
|
| 36 |
+
ResponseType = TypeVar("ResponseType")
|
| 37 |
+
SerializingFunction = Callable[[Any], bytes]
|
| 38 |
+
DeserializingFunction = Callable[[bytes], Any]
|
| 39 |
+
MetadataType = Sequence[Tuple[str, Union[str, bytes]]]
|
| 40 |
+
ChannelArgumentType = Tuple[str, Any]
|
| 41 |
+
DoneCallbackType = Callable[[Any], None]
|
| 42 |
+
NullaryCallbackType = Callable[[], None]
|
| 43 |
+
RequestIterableType = Iterable[Any]
|
| 44 |
+
ResponseIterableType = Iterable[Any]
|
| 45 |
+
UserTag = Callable[[cygrpc.BaseEvent], bool]
|
| 46 |
+
IntegratedCallFactory = Callable[
|
| 47 |
+
[
|
| 48 |
+
int,
|
| 49 |
+
bytes,
|
| 50 |
+
None,
|
| 51 |
+
Optional[float],
|
| 52 |
+
Optional[MetadataType],
|
| 53 |
+
Optional[cygrpc.CallCredentials],
|
| 54 |
+
Sequence[Sequence[cygrpc.Operation]],
|
| 55 |
+
UserTag,
|
| 56 |
+
Any,
|
| 57 |
+
],
|
| 58 |
+
cygrpc.IntegratedCall,
|
| 59 |
+
]
|
| 60 |
+
ServerTagCallbackType = Tuple[
|
| 61 |
+
Optional["_RPCState"], Sequence[NullaryCallbackType]
|
| 62 |
+
]
|
| 63 |
+
ServerCallbackTag = Callable[[cygrpc.BaseEvent], ServerTagCallbackType]
|
| 64 |
+
ArityAgnosticMethodHandler = Union[
|
| 65 |
+
Callable[
|
| 66 |
+
[RequestType, "ServicerContext", Callable[[ResponseType], None]],
|
| 67 |
+
ResponseType,
|
| 68 |
+
],
|
| 69 |
+
Callable[
|
| 70 |
+
[RequestType, "ServicerContext", Callable[[ResponseType], None]],
|
| 71 |
+
Iterator[ResponseType],
|
| 72 |
+
],
|
| 73 |
+
Callable[
|
| 74 |
+
[
|
| 75 |
+
Iterator[RequestType],
|
| 76 |
+
"ServicerContext",
|
| 77 |
+
Callable[[ResponseType], None],
|
| 78 |
+
],
|
| 79 |
+
ResponseType,
|
| 80 |
+
],
|
| 81 |
+
Callable[
|
| 82 |
+
[
|
| 83 |
+
Iterator[RequestType],
|
| 84 |
+
"ServicerContext",
|
| 85 |
+
Callable[[ResponseType], None],
|
| 86 |
+
],
|
| 87 |
+
Iterator[ResponseType],
|
| 88 |
+
],
|
| 89 |
+
Callable[[RequestType, "ServicerContext"], ResponseType],
|
| 90 |
+
Callable[[RequestType, "ServicerContext"], Iterator[ResponseType]],
|
| 91 |
+
Callable[[Iterator[RequestType], "ServicerContext"], ResponseType],
|
| 92 |
+
Callable[
|
| 93 |
+
[Iterator[RequestType], "ServicerContext"], Iterator[ResponseType]
|
| 94 |
+
],
|
| 95 |
+
]
|
.venv/lib/python3.11/site-packages/grpc/_utilities.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Internal utilities for gRPC Python."""
|
| 15 |
+
|
| 16 |
+
import collections
|
| 17 |
+
import logging
|
| 18 |
+
import threading
|
| 19 |
+
import time
|
| 20 |
+
from typing import Callable, Dict, Optional, Sequence
|
| 21 |
+
|
| 22 |
+
import grpc # pytype: disable=pyi-error
|
| 23 |
+
from grpc import _common # pytype: disable=pyi-error
|
| 24 |
+
from grpc._typing import DoneCallbackType
|
| 25 |
+
|
| 26 |
+
_LOGGER = logging.getLogger(__name__)
|
| 27 |
+
|
| 28 |
+
_DONE_CALLBACK_EXCEPTION_LOG_MESSAGE = (
|
| 29 |
+
'Exception calling connectivity future "done" callback!'
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class RpcMethodHandler(
|
| 34 |
+
collections.namedtuple(
|
| 35 |
+
"_RpcMethodHandler",
|
| 36 |
+
(
|
| 37 |
+
"request_streaming",
|
| 38 |
+
"response_streaming",
|
| 39 |
+
"request_deserializer",
|
| 40 |
+
"response_serializer",
|
| 41 |
+
"unary_unary",
|
| 42 |
+
"unary_stream",
|
| 43 |
+
"stream_unary",
|
| 44 |
+
"stream_stream",
|
| 45 |
+
),
|
| 46 |
+
),
|
| 47 |
+
grpc.RpcMethodHandler,
|
| 48 |
+
):
|
| 49 |
+
pass
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class DictionaryGenericHandler(grpc.ServiceRpcHandler):
|
| 53 |
+
_name: str
|
| 54 |
+
_method_handlers: Dict[str, grpc.RpcMethodHandler]
|
| 55 |
+
|
| 56 |
+
def __init__(
|
| 57 |
+
self, service: str, method_handlers: Dict[str, grpc.RpcMethodHandler]
|
| 58 |
+
):
|
| 59 |
+
self._name = service
|
| 60 |
+
self._method_handlers = {
|
| 61 |
+
_common.fully_qualified_method(service, method): method_handler
|
| 62 |
+
for method, method_handler in method_handlers.items()
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
def service_name(self) -> str:
|
| 66 |
+
return self._name
|
| 67 |
+
|
| 68 |
+
def service(
|
| 69 |
+
self, handler_call_details: grpc.HandlerCallDetails
|
| 70 |
+
) -> Optional[grpc.RpcMethodHandler]:
|
| 71 |
+
details_method = handler_call_details.method
|
| 72 |
+
return self._method_handlers.get(
|
| 73 |
+
details_method
|
| 74 |
+
) # pytype: disable=attribute-error
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
class _ChannelReadyFuture(grpc.Future):
|
| 78 |
+
_condition: threading.Condition
|
| 79 |
+
_channel: grpc.Channel
|
| 80 |
+
_matured: bool
|
| 81 |
+
_cancelled: bool
|
| 82 |
+
_done_callbacks: Sequence[Callable]
|
| 83 |
+
|
| 84 |
+
def __init__(self, channel: grpc.Channel):
|
| 85 |
+
self._condition = threading.Condition()
|
| 86 |
+
self._channel = channel
|
| 87 |
+
|
| 88 |
+
self._matured = False
|
| 89 |
+
self._cancelled = False
|
| 90 |
+
self._done_callbacks = []
|
| 91 |
+
|
| 92 |
+
def _block(self, timeout: Optional[float]) -> None:
|
| 93 |
+
until = None if timeout is None else time.time() + timeout
|
| 94 |
+
with self._condition:
|
| 95 |
+
while True:
|
| 96 |
+
if self._cancelled:
|
| 97 |
+
raise grpc.FutureCancelledError()
|
| 98 |
+
elif self._matured:
|
| 99 |
+
return
|
| 100 |
+
else:
|
| 101 |
+
if until is None:
|
| 102 |
+
self._condition.wait()
|
| 103 |
+
else:
|
| 104 |
+
remaining = until - time.time()
|
| 105 |
+
if remaining < 0:
|
| 106 |
+
raise grpc.FutureTimeoutError()
|
| 107 |
+
else:
|
| 108 |
+
self._condition.wait(timeout=remaining)
|
| 109 |
+
|
| 110 |
+
def _update(self, connectivity: Optional[grpc.ChannelConnectivity]) -> None:
|
| 111 |
+
with self._condition:
|
| 112 |
+
if (
|
| 113 |
+
not self._cancelled
|
| 114 |
+
and connectivity is grpc.ChannelConnectivity.READY
|
| 115 |
+
):
|
| 116 |
+
self._matured = True
|
| 117 |
+
self._channel.unsubscribe(self._update)
|
| 118 |
+
self._condition.notify_all()
|
| 119 |
+
done_callbacks = tuple(self._done_callbacks)
|
| 120 |
+
self._done_callbacks = None
|
| 121 |
+
else:
|
| 122 |
+
return
|
| 123 |
+
|
| 124 |
+
for done_callback in done_callbacks:
|
| 125 |
+
try:
|
| 126 |
+
done_callback(self)
|
| 127 |
+
except Exception: # pylint: disable=broad-except
|
| 128 |
+
_LOGGER.exception(_DONE_CALLBACK_EXCEPTION_LOG_MESSAGE)
|
| 129 |
+
|
| 130 |
+
def cancel(self) -> bool:
|
| 131 |
+
with self._condition:
|
| 132 |
+
if not self._matured:
|
| 133 |
+
self._cancelled = True
|
| 134 |
+
self._channel.unsubscribe(self._update)
|
| 135 |
+
self._condition.notify_all()
|
| 136 |
+
done_callbacks = tuple(self._done_callbacks)
|
| 137 |
+
self._done_callbacks = None
|
| 138 |
+
else:
|
| 139 |
+
return False
|
| 140 |
+
|
| 141 |
+
for done_callback in done_callbacks:
|
| 142 |
+
try:
|
| 143 |
+
done_callback(self)
|
| 144 |
+
except Exception: # pylint: disable=broad-except
|
| 145 |
+
_LOGGER.exception(_DONE_CALLBACK_EXCEPTION_LOG_MESSAGE)
|
| 146 |
+
|
| 147 |
+
return True
|
| 148 |
+
|
| 149 |
+
def cancelled(self) -> bool:
|
| 150 |
+
with self._condition:
|
| 151 |
+
return self._cancelled
|
| 152 |
+
|
| 153 |
+
def running(self) -> bool:
|
| 154 |
+
with self._condition:
|
| 155 |
+
return not self._cancelled and not self._matured
|
| 156 |
+
|
| 157 |
+
def done(self) -> bool:
|
| 158 |
+
with self._condition:
|
| 159 |
+
return self._cancelled or self._matured
|
| 160 |
+
|
| 161 |
+
def result(self, timeout: Optional[float] = None) -> None:
|
| 162 |
+
self._block(timeout)
|
| 163 |
+
|
| 164 |
+
def exception(self, timeout: Optional[float] = None) -> None:
|
| 165 |
+
self._block(timeout)
|
| 166 |
+
|
| 167 |
+
def traceback(self, timeout: Optional[float] = None) -> None:
|
| 168 |
+
self._block(timeout)
|
| 169 |
+
|
| 170 |
+
def add_done_callback(self, fn: DoneCallbackType):
|
| 171 |
+
with self._condition:
|
| 172 |
+
if not self._cancelled and not self._matured:
|
| 173 |
+
self._done_callbacks.append(fn)
|
| 174 |
+
return
|
| 175 |
+
|
| 176 |
+
fn(self)
|
| 177 |
+
|
| 178 |
+
def start(self):
|
| 179 |
+
with self._condition:
|
| 180 |
+
self._channel.subscribe(self._update, try_to_connect=True)
|
| 181 |
+
|
| 182 |
+
def __del__(self):
|
| 183 |
+
with self._condition:
|
| 184 |
+
if not self._cancelled and not self._matured:
|
| 185 |
+
self._channel.unsubscribe(self._update)
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def channel_ready_future(channel: grpc.Channel) -> _ChannelReadyFuture:
|
| 189 |
+
ready_future = _ChannelReadyFuture(channel)
|
| 190 |
+
ready_future.start()
|
| 191 |
+
return ready_future
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def first_version_is_lower(version1: str, version2: str) -> bool:
|
| 195 |
+
"""
|
| 196 |
+
Compares two versions in the format '1.60.1' or '1.60.1.dev0'.
|
| 197 |
+
|
| 198 |
+
This method will be used in all stubs generated by grpcio-tools to check whether
|
| 199 |
+
the stub version is compatible with the runtime grpcio.
|
| 200 |
+
|
| 201 |
+
Args:
|
| 202 |
+
version1: The first version string.
|
| 203 |
+
version2: The second version string.
|
| 204 |
+
|
| 205 |
+
Returns:
|
| 206 |
+
True if version1 is lower, False otherwise.
|
| 207 |
+
"""
|
| 208 |
+
version1_list = version1.split(".")
|
| 209 |
+
version2_list = version2.split(".")
|
| 210 |
+
|
| 211 |
+
try:
|
| 212 |
+
for i in range(3):
|
| 213 |
+
if int(version1_list[i]) < int(version2_list[i]):
|
| 214 |
+
return True
|
| 215 |
+
elif int(version1_list[i]) > int(version2_list[i]):
|
| 216 |
+
return False
|
| 217 |
+
except ValueError:
|
| 218 |
+
# Return false in case we can't convert version to int.
|
| 219 |
+
return False
|
| 220 |
+
|
| 221 |
+
# The version without dev0 will be considered lower.
|
| 222 |
+
return len(version1_list) < len(version2_list)
|
.venv/lib/python3.11/site-packages/grpc/beta/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
.venv/lib/python3.11/site-packages/grpc/beta/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (182 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/beta/__pycache__/_client_adaptations.cpython-311.pyc
ADDED
|
Binary file (29.4 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/beta/__pycache__/_metadata.cpython-311.pyc
ADDED
|
Binary file (2.32 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/beta/__pycache__/_server_adaptations.cpython-311.pyc
ADDED
|
Binary file (20.7 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/beta/__pycache__/implementations.cpython-311.pyc
ADDED
|
Binary file (12.6 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/beta/__pycache__/interfaces.cpython-311.pyc
ADDED
|
Binary file (7.56 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/beta/__pycache__/utilities.cpython-311.pyc
ADDED
|
Binary file (8.21 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/beta/_client_adaptations.py
ADDED
|
@@ -0,0 +1,1015 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Translates gRPC's client-side API into gRPC's client-side Beta API."""
|
| 15 |
+
|
| 16 |
+
import grpc
|
| 17 |
+
from grpc import _common
|
| 18 |
+
from grpc.beta import _metadata
|
| 19 |
+
from grpc.beta import interfaces
|
| 20 |
+
from grpc.framework.common import cardinality
|
| 21 |
+
from grpc.framework.foundation import future
|
| 22 |
+
from grpc.framework.interfaces.face import face
|
| 23 |
+
|
| 24 |
+
# pylint: disable=too-many-arguments,too-many-locals,unused-argument
|
| 25 |
+
|
| 26 |
+
_STATUS_CODE_TO_ABORTION_KIND_AND_ABORTION_ERROR_CLASS = {
|
| 27 |
+
grpc.StatusCode.CANCELLED: (
|
| 28 |
+
face.Abortion.Kind.CANCELLED,
|
| 29 |
+
face.CancellationError,
|
| 30 |
+
),
|
| 31 |
+
grpc.StatusCode.UNKNOWN: (
|
| 32 |
+
face.Abortion.Kind.REMOTE_FAILURE,
|
| 33 |
+
face.RemoteError,
|
| 34 |
+
),
|
| 35 |
+
grpc.StatusCode.DEADLINE_EXCEEDED: (
|
| 36 |
+
face.Abortion.Kind.EXPIRED,
|
| 37 |
+
face.ExpirationError,
|
| 38 |
+
),
|
| 39 |
+
grpc.StatusCode.UNIMPLEMENTED: (
|
| 40 |
+
face.Abortion.Kind.LOCAL_FAILURE,
|
| 41 |
+
face.LocalError,
|
| 42 |
+
),
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _effective_metadata(metadata, metadata_transformer):
|
| 47 |
+
non_none_metadata = () if metadata is None else metadata
|
| 48 |
+
if metadata_transformer is None:
|
| 49 |
+
return non_none_metadata
|
| 50 |
+
else:
|
| 51 |
+
return metadata_transformer(non_none_metadata)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def _credentials(grpc_call_options):
|
| 55 |
+
return None if grpc_call_options is None else grpc_call_options.credentials
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def _abortion(rpc_error_call):
|
| 59 |
+
code = rpc_error_call.code()
|
| 60 |
+
pair = _STATUS_CODE_TO_ABORTION_KIND_AND_ABORTION_ERROR_CLASS.get(code)
|
| 61 |
+
error_kind = face.Abortion.Kind.LOCAL_FAILURE if pair is None else pair[0]
|
| 62 |
+
return face.Abortion(
|
| 63 |
+
error_kind,
|
| 64 |
+
rpc_error_call.initial_metadata(),
|
| 65 |
+
rpc_error_call.trailing_metadata(),
|
| 66 |
+
code,
|
| 67 |
+
rpc_error_call.details(),
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def _abortion_error(rpc_error_call):
|
| 72 |
+
code = rpc_error_call.code()
|
| 73 |
+
pair = _STATUS_CODE_TO_ABORTION_KIND_AND_ABORTION_ERROR_CLASS.get(code)
|
| 74 |
+
exception_class = face.AbortionError if pair is None else pair[1]
|
| 75 |
+
return exception_class(
|
| 76 |
+
rpc_error_call.initial_metadata(),
|
| 77 |
+
rpc_error_call.trailing_metadata(),
|
| 78 |
+
code,
|
| 79 |
+
rpc_error_call.details(),
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class _InvocationProtocolContext(interfaces.GRPCInvocationContext):
|
| 84 |
+
def disable_next_request_compression(self):
|
| 85 |
+
pass # TODO(https://github.com/grpc/grpc/issues/4078): design, implement.
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class _Rendezvous(future.Future, face.Call):
|
| 89 |
+
def __init__(self, response_future, response_iterator, call):
|
| 90 |
+
self._future = response_future
|
| 91 |
+
self._iterator = response_iterator
|
| 92 |
+
self._call = call
|
| 93 |
+
|
| 94 |
+
def cancel(self):
|
| 95 |
+
return self._call.cancel()
|
| 96 |
+
|
| 97 |
+
def cancelled(self):
|
| 98 |
+
return self._future.cancelled()
|
| 99 |
+
|
| 100 |
+
def running(self):
|
| 101 |
+
return self._future.running()
|
| 102 |
+
|
| 103 |
+
def done(self):
|
| 104 |
+
return self._future.done()
|
| 105 |
+
|
| 106 |
+
def result(self, timeout=None):
|
| 107 |
+
try:
|
| 108 |
+
return self._future.result(timeout=timeout)
|
| 109 |
+
except grpc.RpcError as rpc_error_call:
|
| 110 |
+
raise _abortion_error(rpc_error_call)
|
| 111 |
+
except grpc.FutureTimeoutError:
|
| 112 |
+
raise future.TimeoutError()
|
| 113 |
+
except grpc.FutureCancelledError:
|
| 114 |
+
raise future.CancelledError()
|
| 115 |
+
|
| 116 |
+
def exception(self, timeout=None):
|
| 117 |
+
try:
|
| 118 |
+
rpc_error_call = self._future.exception(timeout=timeout)
|
| 119 |
+
if rpc_error_call is None:
|
| 120 |
+
return None
|
| 121 |
+
else:
|
| 122 |
+
return _abortion_error(rpc_error_call)
|
| 123 |
+
except grpc.FutureTimeoutError:
|
| 124 |
+
raise future.TimeoutError()
|
| 125 |
+
except grpc.FutureCancelledError:
|
| 126 |
+
raise future.CancelledError()
|
| 127 |
+
|
| 128 |
+
def traceback(self, timeout=None):
|
| 129 |
+
try:
|
| 130 |
+
return self._future.traceback(timeout=timeout)
|
| 131 |
+
except grpc.FutureTimeoutError:
|
| 132 |
+
raise future.TimeoutError()
|
| 133 |
+
except grpc.FutureCancelledError:
|
| 134 |
+
raise future.CancelledError()
|
| 135 |
+
|
| 136 |
+
def add_done_callback(self, fn):
|
| 137 |
+
self._future.add_done_callback(lambda ignored_callback: fn(self))
|
| 138 |
+
|
| 139 |
+
def __iter__(self):
|
| 140 |
+
return self
|
| 141 |
+
|
| 142 |
+
def _next(self):
|
| 143 |
+
try:
|
| 144 |
+
return next(self._iterator)
|
| 145 |
+
except grpc.RpcError as rpc_error_call:
|
| 146 |
+
raise _abortion_error(rpc_error_call)
|
| 147 |
+
|
| 148 |
+
def __next__(self):
|
| 149 |
+
return self._next()
|
| 150 |
+
|
| 151 |
+
def next(self):
|
| 152 |
+
return self._next()
|
| 153 |
+
|
| 154 |
+
def is_active(self):
|
| 155 |
+
return self._call.is_active()
|
| 156 |
+
|
| 157 |
+
def time_remaining(self):
|
| 158 |
+
return self._call.time_remaining()
|
| 159 |
+
|
| 160 |
+
def add_abortion_callback(self, abortion_callback):
|
| 161 |
+
def done_callback():
|
| 162 |
+
if self.code() is not grpc.StatusCode.OK:
|
| 163 |
+
abortion_callback(_abortion(self._call))
|
| 164 |
+
|
| 165 |
+
registered = self._call.add_callback(done_callback)
|
| 166 |
+
return None if registered else done_callback()
|
| 167 |
+
|
| 168 |
+
def protocol_context(self):
|
| 169 |
+
return _InvocationProtocolContext()
|
| 170 |
+
|
| 171 |
+
def initial_metadata(self):
|
| 172 |
+
return _metadata.beta(self._call.initial_metadata())
|
| 173 |
+
|
| 174 |
+
def terminal_metadata(self):
|
| 175 |
+
return _metadata.beta(self._call.terminal_metadata())
|
| 176 |
+
|
| 177 |
+
def code(self):
|
| 178 |
+
return self._call.code()
|
| 179 |
+
|
| 180 |
+
def details(self):
|
| 181 |
+
return self._call.details()
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
def _blocking_unary_unary(
|
| 185 |
+
channel,
|
| 186 |
+
group,
|
| 187 |
+
method,
|
| 188 |
+
timeout,
|
| 189 |
+
with_call,
|
| 190 |
+
protocol_options,
|
| 191 |
+
metadata,
|
| 192 |
+
metadata_transformer,
|
| 193 |
+
request,
|
| 194 |
+
request_serializer,
|
| 195 |
+
response_deserializer,
|
| 196 |
+
):
|
| 197 |
+
try:
|
| 198 |
+
multi_callable = channel.unary_unary(
|
| 199 |
+
_common.fully_qualified_method(group, method),
|
| 200 |
+
request_serializer=request_serializer,
|
| 201 |
+
response_deserializer=response_deserializer,
|
| 202 |
+
)
|
| 203 |
+
effective_metadata = _effective_metadata(metadata, metadata_transformer)
|
| 204 |
+
if with_call:
|
| 205 |
+
response, call = multi_callable.with_call(
|
| 206 |
+
request,
|
| 207 |
+
timeout=timeout,
|
| 208 |
+
metadata=_metadata.unbeta(effective_metadata),
|
| 209 |
+
credentials=_credentials(protocol_options),
|
| 210 |
+
)
|
| 211 |
+
return response, _Rendezvous(None, None, call)
|
| 212 |
+
else:
|
| 213 |
+
return multi_callable(
|
| 214 |
+
request,
|
| 215 |
+
timeout=timeout,
|
| 216 |
+
metadata=_metadata.unbeta(effective_metadata),
|
| 217 |
+
credentials=_credentials(protocol_options),
|
| 218 |
+
)
|
| 219 |
+
except grpc.RpcError as rpc_error_call:
|
| 220 |
+
raise _abortion_error(rpc_error_call)
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def _future_unary_unary(
|
| 224 |
+
channel,
|
| 225 |
+
group,
|
| 226 |
+
method,
|
| 227 |
+
timeout,
|
| 228 |
+
protocol_options,
|
| 229 |
+
metadata,
|
| 230 |
+
metadata_transformer,
|
| 231 |
+
request,
|
| 232 |
+
request_serializer,
|
| 233 |
+
response_deserializer,
|
| 234 |
+
):
|
| 235 |
+
multi_callable = channel.unary_unary(
|
| 236 |
+
_common.fully_qualified_method(group, method),
|
| 237 |
+
request_serializer=request_serializer,
|
| 238 |
+
response_deserializer=response_deserializer,
|
| 239 |
+
)
|
| 240 |
+
effective_metadata = _effective_metadata(metadata, metadata_transformer)
|
| 241 |
+
response_future = multi_callable.future(
|
| 242 |
+
request,
|
| 243 |
+
timeout=timeout,
|
| 244 |
+
metadata=_metadata.unbeta(effective_metadata),
|
| 245 |
+
credentials=_credentials(protocol_options),
|
| 246 |
+
)
|
| 247 |
+
return _Rendezvous(response_future, None, response_future)
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def _unary_stream(
|
| 251 |
+
channel,
|
| 252 |
+
group,
|
| 253 |
+
method,
|
| 254 |
+
timeout,
|
| 255 |
+
protocol_options,
|
| 256 |
+
metadata,
|
| 257 |
+
metadata_transformer,
|
| 258 |
+
request,
|
| 259 |
+
request_serializer,
|
| 260 |
+
response_deserializer,
|
| 261 |
+
):
|
| 262 |
+
multi_callable = channel.unary_stream(
|
| 263 |
+
_common.fully_qualified_method(group, method),
|
| 264 |
+
request_serializer=request_serializer,
|
| 265 |
+
response_deserializer=response_deserializer,
|
| 266 |
+
)
|
| 267 |
+
effective_metadata = _effective_metadata(metadata, metadata_transformer)
|
| 268 |
+
response_iterator = multi_callable(
|
| 269 |
+
request,
|
| 270 |
+
timeout=timeout,
|
| 271 |
+
metadata=_metadata.unbeta(effective_metadata),
|
| 272 |
+
credentials=_credentials(protocol_options),
|
| 273 |
+
)
|
| 274 |
+
return _Rendezvous(None, response_iterator, response_iterator)
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def _blocking_stream_unary(
|
| 278 |
+
channel,
|
| 279 |
+
group,
|
| 280 |
+
method,
|
| 281 |
+
timeout,
|
| 282 |
+
with_call,
|
| 283 |
+
protocol_options,
|
| 284 |
+
metadata,
|
| 285 |
+
metadata_transformer,
|
| 286 |
+
request_iterator,
|
| 287 |
+
request_serializer,
|
| 288 |
+
response_deserializer,
|
| 289 |
+
):
|
| 290 |
+
try:
|
| 291 |
+
multi_callable = channel.stream_unary(
|
| 292 |
+
_common.fully_qualified_method(group, method),
|
| 293 |
+
request_serializer=request_serializer,
|
| 294 |
+
response_deserializer=response_deserializer,
|
| 295 |
+
)
|
| 296 |
+
effective_metadata = _effective_metadata(metadata, metadata_transformer)
|
| 297 |
+
if with_call:
|
| 298 |
+
response, call = multi_callable.with_call(
|
| 299 |
+
request_iterator,
|
| 300 |
+
timeout=timeout,
|
| 301 |
+
metadata=_metadata.unbeta(effective_metadata),
|
| 302 |
+
credentials=_credentials(protocol_options),
|
| 303 |
+
)
|
| 304 |
+
return response, _Rendezvous(None, None, call)
|
| 305 |
+
else:
|
| 306 |
+
return multi_callable(
|
| 307 |
+
request_iterator,
|
| 308 |
+
timeout=timeout,
|
| 309 |
+
metadata=_metadata.unbeta(effective_metadata),
|
| 310 |
+
credentials=_credentials(protocol_options),
|
| 311 |
+
)
|
| 312 |
+
except grpc.RpcError as rpc_error_call:
|
| 313 |
+
raise _abortion_error(rpc_error_call)
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
def _future_stream_unary(
|
| 317 |
+
channel,
|
| 318 |
+
group,
|
| 319 |
+
method,
|
| 320 |
+
timeout,
|
| 321 |
+
protocol_options,
|
| 322 |
+
metadata,
|
| 323 |
+
metadata_transformer,
|
| 324 |
+
request_iterator,
|
| 325 |
+
request_serializer,
|
| 326 |
+
response_deserializer,
|
| 327 |
+
):
|
| 328 |
+
multi_callable = channel.stream_unary(
|
| 329 |
+
_common.fully_qualified_method(group, method),
|
| 330 |
+
request_serializer=request_serializer,
|
| 331 |
+
response_deserializer=response_deserializer,
|
| 332 |
+
)
|
| 333 |
+
effective_metadata = _effective_metadata(metadata, metadata_transformer)
|
| 334 |
+
response_future = multi_callable.future(
|
| 335 |
+
request_iterator,
|
| 336 |
+
timeout=timeout,
|
| 337 |
+
metadata=_metadata.unbeta(effective_metadata),
|
| 338 |
+
credentials=_credentials(protocol_options),
|
| 339 |
+
)
|
| 340 |
+
return _Rendezvous(response_future, None, response_future)
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
def _stream_stream(
|
| 344 |
+
channel,
|
| 345 |
+
group,
|
| 346 |
+
method,
|
| 347 |
+
timeout,
|
| 348 |
+
protocol_options,
|
| 349 |
+
metadata,
|
| 350 |
+
metadata_transformer,
|
| 351 |
+
request_iterator,
|
| 352 |
+
request_serializer,
|
| 353 |
+
response_deserializer,
|
| 354 |
+
):
|
| 355 |
+
multi_callable = channel.stream_stream(
|
| 356 |
+
_common.fully_qualified_method(group, method),
|
| 357 |
+
request_serializer=request_serializer,
|
| 358 |
+
response_deserializer=response_deserializer,
|
| 359 |
+
)
|
| 360 |
+
effective_metadata = _effective_metadata(metadata, metadata_transformer)
|
| 361 |
+
response_iterator = multi_callable(
|
| 362 |
+
request_iterator,
|
| 363 |
+
timeout=timeout,
|
| 364 |
+
metadata=_metadata.unbeta(effective_metadata),
|
| 365 |
+
credentials=_credentials(protocol_options),
|
| 366 |
+
)
|
| 367 |
+
return _Rendezvous(None, response_iterator, response_iterator)
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
class _UnaryUnaryMultiCallable(face.UnaryUnaryMultiCallable):
|
| 371 |
+
def __init__(
|
| 372 |
+
self,
|
| 373 |
+
channel,
|
| 374 |
+
group,
|
| 375 |
+
method,
|
| 376 |
+
metadata_transformer,
|
| 377 |
+
request_serializer,
|
| 378 |
+
response_deserializer,
|
| 379 |
+
):
|
| 380 |
+
self._channel = channel
|
| 381 |
+
self._group = group
|
| 382 |
+
self._method = method
|
| 383 |
+
self._metadata_transformer = metadata_transformer
|
| 384 |
+
self._request_serializer = request_serializer
|
| 385 |
+
self._response_deserializer = response_deserializer
|
| 386 |
+
|
| 387 |
+
def __call__(
|
| 388 |
+
self,
|
| 389 |
+
request,
|
| 390 |
+
timeout,
|
| 391 |
+
metadata=None,
|
| 392 |
+
with_call=False,
|
| 393 |
+
protocol_options=None,
|
| 394 |
+
):
|
| 395 |
+
return _blocking_unary_unary(
|
| 396 |
+
self._channel,
|
| 397 |
+
self._group,
|
| 398 |
+
self._method,
|
| 399 |
+
timeout,
|
| 400 |
+
with_call,
|
| 401 |
+
protocol_options,
|
| 402 |
+
metadata,
|
| 403 |
+
self._metadata_transformer,
|
| 404 |
+
request,
|
| 405 |
+
self._request_serializer,
|
| 406 |
+
self._response_deserializer,
|
| 407 |
+
)
|
| 408 |
+
|
| 409 |
+
def future(self, request, timeout, metadata=None, protocol_options=None):
|
| 410 |
+
return _future_unary_unary(
|
| 411 |
+
self._channel,
|
| 412 |
+
self._group,
|
| 413 |
+
self._method,
|
| 414 |
+
timeout,
|
| 415 |
+
protocol_options,
|
| 416 |
+
metadata,
|
| 417 |
+
self._metadata_transformer,
|
| 418 |
+
request,
|
| 419 |
+
self._request_serializer,
|
| 420 |
+
self._response_deserializer,
|
| 421 |
+
)
|
| 422 |
+
|
| 423 |
+
def event(
|
| 424 |
+
self,
|
| 425 |
+
request,
|
| 426 |
+
receiver,
|
| 427 |
+
abortion_callback,
|
| 428 |
+
timeout,
|
| 429 |
+
metadata=None,
|
| 430 |
+
protocol_options=None,
|
| 431 |
+
):
|
| 432 |
+
raise NotImplementedError()
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
class _UnaryStreamMultiCallable(face.UnaryStreamMultiCallable):
|
| 436 |
+
def __init__(
|
| 437 |
+
self,
|
| 438 |
+
channel,
|
| 439 |
+
group,
|
| 440 |
+
method,
|
| 441 |
+
metadata_transformer,
|
| 442 |
+
request_serializer,
|
| 443 |
+
response_deserializer,
|
| 444 |
+
):
|
| 445 |
+
self._channel = channel
|
| 446 |
+
self._group = group
|
| 447 |
+
self._method = method
|
| 448 |
+
self._metadata_transformer = metadata_transformer
|
| 449 |
+
self._request_serializer = request_serializer
|
| 450 |
+
self._response_deserializer = response_deserializer
|
| 451 |
+
|
| 452 |
+
def __call__(self, request, timeout, metadata=None, protocol_options=None):
|
| 453 |
+
return _unary_stream(
|
| 454 |
+
self._channel,
|
| 455 |
+
self._group,
|
| 456 |
+
self._method,
|
| 457 |
+
timeout,
|
| 458 |
+
protocol_options,
|
| 459 |
+
metadata,
|
| 460 |
+
self._metadata_transformer,
|
| 461 |
+
request,
|
| 462 |
+
self._request_serializer,
|
| 463 |
+
self._response_deserializer,
|
| 464 |
+
)
|
| 465 |
+
|
| 466 |
+
def event(
|
| 467 |
+
self,
|
| 468 |
+
request,
|
| 469 |
+
receiver,
|
| 470 |
+
abortion_callback,
|
| 471 |
+
timeout,
|
| 472 |
+
metadata=None,
|
| 473 |
+
protocol_options=None,
|
| 474 |
+
):
|
| 475 |
+
raise NotImplementedError()
|
| 476 |
+
|
| 477 |
+
|
| 478 |
+
class _StreamUnaryMultiCallable(face.StreamUnaryMultiCallable):
|
| 479 |
+
def __init__(
|
| 480 |
+
self,
|
| 481 |
+
channel,
|
| 482 |
+
group,
|
| 483 |
+
method,
|
| 484 |
+
metadata_transformer,
|
| 485 |
+
request_serializer,
|
| 486 |
+
response_deserializer,
|
| 487 |
+
):
|
| 488 |
+
self._channel = channel
|
| 489 |
+
self._group = group
|
| 490 |
+
self._method = method
|
| 491 |
+
self._metadata_transformer = metadata_transformer
|
| 492 |
+
self._request_serializer = request_serializer
|
| 493 |
+
self._response_deserializer = response_deserializer
|
| 494 |
+
|
| 495 |
+
def __call__(
|
| 496 |
+
self,
|
| 497 |
+
request_iterator,
|
| 498 |
+
timeout,
|
| 499 |
+
metadata=None,
|
| 500 |
+
with_call=False,
|
| 501 |
+
protocol_options=None,
|
| 502 |
+
):
|
| 503 |
+
return _blocking_stream_unary(
|
| 504 |
+
self._channel,
|
| 505 |
+
self._group,
|
| 506 |
+
self._method,
|
| 507 |
+
timeout,
|
| 508 |
+
with_call,
|
| 509 |
+
protocol_options,
|
| 510 |
+
metadata,
|
| 511 |
+
self._metadata_transformer,
|
| 512 |
+
request_iterator,
|
| 513 |
+
self._request_serializer,
|
| 514 |
+
self._response_deserializer,
|
| 515 |
+
)
|
| 516 |
+
|
| 517 |
+
def future(
|
| 518 |
+
self, request_iterator, timeout, metadata=None, protocol_options=None
|
| 519 |
+
):
|
| 520 |
+
return _future_stream_unary(
|
| 521 |
+
self._channel,
|
| 522 |
+
self._group,
|
| 523 |
+
self._method,
|
| 524 |
+
timeout,
|
| 525 |
+
protocol_options,
|
| 526 |
+
metadata,
|
| 527 |
+
self._metadata_transformer,
|
| 528 |
+
request_iterator,
|
| 529 |
+
self._request_serializer,
|
| 530 |
+
self._response_deserializer,
|
| 531 |
+
)
|
| 532 |
+
|
| 533 |
+
def event(
|
| 534 |
+
self,
|
| 535 |
+
receiver,
|
| 536 |
+
abortion_callback,
|
| 537 |
+
timeout,
|
| 538 |
+
metadata=None,
|
| 539 |
+
protocol_options=None,
|
| 540 |
+
):
|
| 541 |
+
raise NotImplementedError()
|
| 542 |
+
|
| 543 |
+
|
| 544 |
+
class _StreamStreamMultiCallable(face.StreamStreamMultiCallable):
|
| 545 |
+
def __init__(
|
| 546 |
+
self,
|
| 547 |
+
channel,
|
| 548 |
+
group,
|
| 549 |
+
method,
|
| 550 |
+
metadata_transformer,
|
| 551 |
+
request_serializer,
|
| 552 |
+
response_deserializer,
|
| 553 |
+
):
|
| 554 |
+
self._channel = channel
|
| 555 |
+
self._group = group
|
| 556 |
+
self._method = method
|
| 557 |
+
self._metadata_transformer = metadata_transformer
|
| 558 |
+
self._request_serializer = request_serializer
|
| 559 |
+
self._response_deserializer = response_deserializer
|
| 560 |
+
|
| 561 |
+
def __call__(
|
| 562 |
+
self, request_iterator, timeout, metadata=None, protocol_options=None
|
| 563 |
+
):
|
| 564 |
+
return _stream_stream(
|
| 565 |
+
self._channel,
|
| 566 |
+
self._group,
|
| 567 |
+
self._method,
|
| 568 |
+
timeout,
|
| 569 |
+
protocol_options,
|
| 570 |
+
metadata,
|
| 571 |
+
self._metadata_transformer,
|
| 572 |
+
request_iterator,
|
| 573 |
+
self._request_serializer,
|
| 574 |
+
self._response_deserializer,
|
| 575 |
+
)
|
| 576 |
+
|
| 577 |
+
def event(
|
| 578 |
+
self,
|
| 579 |
+
receiver,
|
| 580 |
+
abortion_callback,
|
| 581 |
+
timeout,
|
| 582 |
+
metadata=None,
|
| 583 |
+
protocol_options=None,
|
| 584 |
+
):
|
| 585 |
+
raise NotImplementedError()
|
| 586 |
+
|
| 587 |
+
|
| 588 |
+
class _GenericStub(face.GenericStub):
|
| 589 |
+
def __init__(
|
| 590 |
+
self,
|
| 591 |
+
channel,
|
| 592 |
+
metadata_transformer,
|
| 593 |
+
request_serializers,
|
| 594 |
+
response_deserializers,
|
| 595 |
+
):
|
| 596 |
+
self._channel = channel
|
| 597 |
+
self._metadata_transformer = metadata_transformer
|
| 598 |
+
self._request_serializers = request_serializers or {}
|
| 599 |
+
self._response_deserializers = response_deserializers or {}
|
| 600 |
+
|
| 601 |
+
def blocking_unary_unary(
|
| 602 |
+
self,
|
| 603 |
+
group,
|
| 604 |
+
method,
|
| 605 |
+
request,
|
| 606 |
+
timeout,
|
| 607 |
+
metadata=None,
|
| 608 |
+
with_call=None,
|
| 609 |
+
protocol_options=None,
|
| 610 |
+
):
|
| 611 |
+
request_serializer = self._request_serializers.get(
|
| 612 |
+
(
|
| 613 |
+
group,
|
| 614 |
+
method,
|
| 615 |
+
)
|
| 616 |
+
)
|
| 617 |
+
response_deserializer = self._response_deserializers.get(
|
| 618 |
+
(
|
| 619 |
+
group,
|
| 620 |
+
method,
|
| 621 |
+
)
|
| 622 |
+
)
|
| 623 |
+
return _blocking_unary_unary(
|
| 624 |
+
self._channel,
|
| 625 |
+
group,
|
| 626 |
+
method,
|
| 627 |
+
timeout,
|
| 628 |
+
with_call,
|
| 629 |
+
protocol_options,
|
| 630 |
+
metadata,
|
| 631 |
+
self._metadata_transformer,
|
| 632 |
+
request,
|
| 633 |
+
request_serializer,
|
| 634 |
+
response_deserializer,
|
| 635 |
+
)
|
| 636 |
+
|
| 637 |
+
def future_unary_unary(
|
| 638 |
+
self,
|
| 639 |
+
group,
|
| 640 |
+
method,
|
| 641 |
+
request,
|
| 642 |
+
timeout,
|
| 643 |
+
metadata=None,
|
| 644 |
+
protocol_options=None,
|
| 645 |
+
):
|
| 646 |
+
request_serializer = self._request_serializers.get(
|
| 647 |
+
(
|
| 648 |
+
group,
|
| 649 |
+
method,
|
| 650 |
+
)
|
| 651 |
+
)
|
| 652 |
+
response_deserializer = self._response_deserializers.get(
|
| 653 |
+
(
|
| 654 |
+
group,
|
| 655 |
+
method,
|
| 656 |
+
)
|
| 657 |
+
)
|
| 658 |
+
return _future_unary_unary(
|
| 659 |
+
self._channel,
|
| 660 |
+
group,
|
| 661 |
+
method,
|
| 662 |
+
timeout,
|
| 663 |
+
protocol_options,
|
| 664 |
+
metadata,
|
| 665 |
+
self._metadata_transformer,
|
| 666 |
+
request,
|
| 667 |
+
request_serializer,
|
| 668 |
+
response_deserializer,
|
| 669 |
+
)
|
| 670 |
+
|
| 671 |
+
def inline_unary_stream(
|
| 672 |
+
self,
|
| 673 |
+
group,
|
| 674 |
+
method,
|
| 675 |
+
request,
|
| 676 |
+
timeout,
|
| 677 |
+
metadata=None,
|
| 678 |
+
protocol_options=None,
|
| 679 |
+
):
|
| 680 |
+
request_serializer = self._request_serializers.get(
|
| 681 |
+
(
|
| 682 |
+
group,
|
| 683 |
+
method,
|
| 684 |
+
)
|
| 685 |
+
)
|
| 686 |
+
response_deserializer = self._response_deserializers.get(
|
| 687 |
+
(
|
| 688 |
+
group,
|
| 689 |
+
method,
|
| 690 |
+
)
|
| 691 |
+
)
|
| 692 |
+
return _unary_stream(
|
| 693 |
+
self._channel,
|
| 694 |
+
group,
|
| 695 |
+
method,
|
| 696 |
+
timeout,
|
| 697 |
+
protocol_options,
|
| 698 |
+
metadata,
|
| 699 |
+
self._metadata_transformer,
|
| 700 |
+
request,
|
| 701 |
+
request_serializer,
|
| 702 |
+
response_deserializer,
|
| 703 |
+
)
|
| 704 |
+
|
| 705 |
+
def blocking_stream_unary(
|
| 706 |
+
self,
|
| 707 |
+
group,
|
| 708 |
+
method,
|
| 709 |
+
request_iterator,
|
| 710 |
+
timeout,
|
| 711 |
+
metadata=None,
|
| 712 |
+
with_call=None,
|
| 713 |
+
protocol_options=None,
|
| 714 |
+
):
|
| 715 |
+
request_serializer = self._request_serializers.get(
|
| 716 |
+
(
|
| 717 |
+
group,
|
| 718 |
+
method,
|
| 719 |
+
)
|
| 720 |
+
)
|
| 721 |
+
response_deserializer = self._response_deserializers.get(
|
| 722 |
+
(
|
| 723 |
+
group,
|
| 724 |
+
method,
|
| 725 |
+
)
|
| 726 |
+
)
|
| 727 |
+
return _blocking_stream_unary(
|
| 728 |
+
self._channel,
|
| 729 |
+
group,
|
| 730 |
+
method,
|
| 731 |
+
timeout,
|
| 732 |
+
with_call,
|
| 733 |
+
protocol_options,
|
| 734 |
+
metadata,
|
| 735 |
+
self._metadata_transformer,
|
| 736 |
+
request_iterator,
|
| 737 |
+
request_serializer,
|
| 738 |
+
response_deserializer,
|
| 739 |
+
)
|
| 740 |
+
|
| 741 |
+
def future_stream_unary(
|
| 742 |
+
self,
|
| 743 |
+
group,
|
| 744 |
+
method,
|
| 745 |
+
request_iterator,
|
| 746 |
+
timeout,
|
| 747 |
+
metadata=None,
|
| 748 |
+
protocol_options=None,
|
| 749 |
+
):
|
| 750 |
+
request_serializer = self._request_serializers.get(
|
| 751 |
+
(
|
| 752 |
+
group,
|
| 753 |
+
method,
|
| 754 |
+
)
|
| 755 |
+
)
|
| 756 |
+
response_deserializer = self._response_deserializers.get(
|
| 757 |
+
(
|
| 758 |
+
group,
|
| 759 |
+
method,
|
| 760 |
+
)
|
| 761 |
+
)
|
| 762 |
+
return _future_stream_unary(
|
| 763 |
+
self._channel,
|
| 764 |
+
group,
|
| 765 |
+
method,
|
| 766 |
+
timeout,
|
| 767 |
+
protocol_options,
|
| 768 |
+
metadata,
|
| 769 |
+
self._metadata_transformer,
|
| 770 |
+
request_iterator,
|
| 771 |
+
request_serializer,
|
| 772 |
+
response_deserializer,
|
| 773 |
+
)
|
| 774 |
+
|
| 775 |
+
def inline_stream_stream(
|
| 776 |
+
self,
|
| 777 |
+
group,
|
| 778 |
+
method,
|
| 779 |
+
request_iterator,
|
| 780 |
+
timeout,
|
| 781 |
+
metadata=None,
|
| 782 |
+
protocol_options=None,
|
| 783 |
+
):
|
| 784 |
+
request_serializer = self._request_serializers.get(
|
| 785 |
+
(
|
| 786 |
+
group,
|
| 787 |
+
method,
|
| 788 |
+
)
|
| 789 |
+
)
|
| 790 |
+
response_deserializer = self._response_deserializers.get(
|
| 791 |
+
(
|
| 792 |
+
group,
|
| 793 |
+
method,
|
| 794 |
+
)
|
| 795 |
+
)
|
| 796 |
+
return _stream_stream(
|
| 797 |
+
self._channel,
|
| 798 |
+
group,
|
| 799 |
+
method,
|
| 800 |
+
timeout,
|
| 801 |
+
protocol_options,
|
| 802 |
+
metadata,
|
| 803 |
+
self._metadata_transformer,
|
| 804 |
+
request_iterator,
|
| 805 |
+
request_serializer,
|
| 806 |
+
response_deserializer,
|
| 807 |
+
)
|
| 808 |
+
|
| 809 |
+
def event_unary_unary(
|
| 810 |
+
self,
|
| 811 |
+
group,
|
| 812 |
+
method,
|
| 813 |
+
request,
|
| 814 |
+
receiver,
|
| 815 |
+
abortion_callback,
|
| 816 |
+
timeout,
|
| 817 |
+
metadata=None,
|
| 818 |
+
protocol_options=None,
|
| 819 |
+
):
|
| 820 |
+
raise NotImplementedError()
|
| 821 |
+
|
| 822 |
+
def event_unary_stream(
|
| 823 |
+
self,
|
| 824 |
+
group,
|
| 825 |
+
method,
|
| 826 |
+
request,
|
| 827 |
+
receiver,
|
| 828 |
+
abortion_callback,
|
| 829 |
+
timeout,
|
| 830 |
+
metadata=None,
|
| 831 |
+
protocol_options=None,
|
| 832 |
+
):
|
| 833 |
+
raise NotImplementedError()
|
| 834 |
+
|
| 835 |
+
def event_stream_unary(
|
| 836 |
+
self,
|
| 837 |
+
group,
|
| 838 |
+
method,
|
| 839 |
+
receiver,
|
| 840 |
+
abortion_callback,
|
| 841 |
+
timeout,
|
| 842 |
+
metadata=None,
|
| 843 |
+
protocol_options=None,
|
| 844 |
+
):
|
| 845 |
+
raise NotImplementedError()
|
| 846 |
+
|
| 847 |
+
def event_stream_stream(
|
| 848 |
+
self,
|
| 849 |
+
group,
|
| 850 |
+
method,
|
| 851 |
+
receiver,
|
| 852 |
+
abortion_callback,
|
| 853 |
+
timeout,
|
| 854 |
+
metadata=None,
|
| 855 |
+
protocol_options=None,
|
| 856 |
+
):
|
| 857 |
+
raise NotImplementedError()
|
| 858 |
+
|
| 859 |
+
def unary_unary(self, group, method):
|
| 860 |
+
request_serializer = self._request_serializers.get(
|
| 861 |
+
(
|
| 862 |
+
group,
|
| 863 |
+
method,
|
| 864 |
+
)
|
| 865 |
+
)
|
| 866 |
+
response_deserializer = self._response_deserializers.get(
|
| 867 |
+
(
|
| 868 |
+
group,
|
| 869 |
+
method,
|
| 870 |
+
)
|
| 871 |
+
)
|
| 872 |
+
return _UnaryUnaryMultiCallable(
|
| 873 |
+
self._channel,
|
| 874 |
+
group,
|
| 875 |
+
method,
|
| 876 |
+
self._metadata_transformer,
|
| 877 |
+
request_serializer,
|
| 878 |
+
response_deserializer,
|
| 879 |
+
)
|
| 880 |
+
|
| 881 |
+
def unary_stream(self, group, method):
|
| 882 |
+
request_serializer = self._request_serializers.get(
|
| 883 |
+
(
|
| 884 |
+
group,
|
| 885 |
+
method,
|
| 886 |
+
)
|
| 887 |
+
)
|
| 888 |
+
response_deserializer = self._response_deserializers.get(
|
| 889 |
+
(
|
| 890 |
+
group,
|
| 891 |
+
method,
|
| 892 |
+
)
|
| 893 |
+
)
|
| 894 |
+
return _UnaryStreamMultiCallable(
|
| 895 |
+
self._channel,
|
| 896 |
+
group,
|
| 897 |
+
method,
|
| 898 |
+
self._metadata_transformer,
|
| 899 |
+
request_serializer,
|
| 900 |
+
response_deserializer,
|
| 901 |
+
)
|
| 902 |
+
|
| 903 |
+
def stream_unary(self, group, method):
|
| 904 |
+
request_serializer = self._request_serializers.get(
|
| 905 |
+
(
|
| 906 |
+
group,
|
| 907 |
+
method,
|
| 908 |
+
)
|
| 909 |
+
)
|
| 910 |
+
response_deserializer = self._response_deserializers.get(
|
| 911 |
+
(
|
| 912 |
+
group,
|
| 913 |
+
method,
|
| 914 |
+
)
|
| 915 |
+
)
|
| 916 |
+
return _StreamUnaryMultiCallable(
|
| 917 |
+
self._channel,
|
| 918 |
+
group,
|
| 919 |
+
method,
|
| 920 |
+
self._metadata_transformer,
|
| 921 |
+
request_serializer,
|
| 922 |
+
response_deserializer,
|
| 923 |
+
)
|
| 924 |
+
|
| 925 |
+
def stream_stream(self, group, method):
|
| 926 |
+
request_serializer = self._request_serializers.get(
|
| 927 |
+
(
|
| 928 |
+
group,
|
| 929 |
+
method,
|
| 930 |
+
)
|
| 931 |
+
)
|
| 932 |
+
response_deserializer = self._response_deserializers.get(
|
| 933 |
+
(
|
| 934 |
+
group,
|
| 935 |
+
method,
|
| 936 |
+
)
|
| 937 |
+
)
|
| 938 |
+
return _StreamStreamMultiCallable(
|
| 939 |
+
self._channel,
|
| 940 |
+
group,
|
| 941 |
+
method,
|
| 942 |
+
self._metadata_transformer,
|
| 943 |
+
request_serializer,
|
| 944 |
+
response_deserializer,
|
| 945 |
+
)
|
| 946 |
+
|
| 947 |
+
def __enter__(self):
|
| 948 |
+
return self
|
| 949 |
+
|
| 950 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 951 |
+
return False
|
| 952 |
+
|
| 953 |
+
|
| 954 |
+
class _DynamicStub(face.DynamicStub):
|
| 955 |
+
def __init__(self, backing_generic_stub, group, cardinalities):
|
| 956 |
+
self._generic_stub = backing_generic_stub
|
| 957 |
+
self._group = group
|
| 958 |
+
self._cardinalities = cardinalities
|
| 959 |
+
|
| 960 |
+
def __getattr__(self, attr):
|
| 961 |
+
method_cardinality = self._cardinalities.get(attr)
|
| 962 |
+
if method_cardinality is cardinality.Cardinality.UNARY_UNARY:
|
| 963 |
+
return self._generic_stub.unary_unary(self._group, attr)
|
| 964 |
+
elif method_cardinality is cardinality.Cardinality.UNARY_STREAM:
|
| 965 |
+
return self._generic_stub.unary_stream(self._group, attr)
|
| 966 |
+
elif method_cardinality is cardinality.Cardinality.STREAM_UNARY:
|
| 967 |
+
return self._generic_stub.stream_unary(self._group, attr)
|
| 968 |
+
elif method_cardinality is cardinality.Cardinality.STREAM_STREAM:
|
| 969 |
+
return self._generic_stub.stream_stream(self._group, attr)
|
| 970 |
+
else:
|
| 971 |
+
raise AttributeError(
|
| 972 |
+
'_DynamicStub object has no attribute "%s"!' % attr
|
| 973 |
+
)
|
| 974 |
+
|
| 975 |
+
def __enter__(self):
|
| 976 |
+
return self
|
| 977 |
+
|
| 978 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 979 |
+
return False
|
| 980 |
+
|
| 981 |
+
|
| 982 |
+
def generic_stub(
|
| 983 |
+
channel,
|
| 984 |
+
host,
|
| 985 |
+
metadata_transformer,
|
| 986 |
+
request_serializers,
|
| 987 |
+
response_deserializers,
|
| 988 |
+
):
|
| 989 |
+
return _GenericStub(
|
| 990 |
+
channel,
|
| 991 |
+
metadata_transformer,
|
| 992 |
+
request_serializers,
|
| 993 |
+
response_deserializers,
|
| 994 |
+
)
|
| 995 |
+
|
| 996 |
+
|
| 997 |
+
def dynamic_stub(
|
| 998 |
+
channel,
|
| 999 |
+
service,
|
| 1000 |
+
cardinalities,
|
| 1001 |
+
host,
|
| 1002 |
+
metadata_transformer,
|
| 1003 |
+
request_serializers,
|
| 1004 |
+
response_deserializers,
|
| 1005 |
+
):
|
| 1006 |
+
return _DynamicStub(
|
| 1007 |
+
_GenericStub(
|
| 1008 |
+
channel,
|
| 1009 |
+
metadata_transformer,
|
| 1010 |
+
request_serializers,
|
| 1011 |
+
response_deserializers,
|
| 1012 |
+
),
|
| 1013 |
+
service,
|
| 1014 |
+
cardinalities,
|
| 1015 |
+
)
|
.venv/lib/python3.11/site-packages/grpc/beta/_metadata.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""API metadata conversion utilities."""
|
| 15 |
+
|
| 16 |
+
import collections
|
| 17 |
+
|
| 18 |
+
_Metadatum = collections.namedtuple(
|
| 19 |
+
"_Metadatum",
|
| 20 |
+
(
|
| 21 |
+
"key",
|
| 22 |
+
"value",
|
| 23 |
+
),
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def _beta_metadatum(key, value):
|
| 28 |
+
beta_key = key if isinstance(key, (bytes,)) else key.encode("ascii")
|
| 29 |
+
beta_value = value if isinstance(value, (bytes,)) else value.encode("ascii")
|
| 30 |
+
return _Metadatum(beta_key, beta_value)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def _metadatum(beta_key, beta_value):
|
| 34 |
+
key = beta_key if isinstance(beta_key, (str,)) else beta_key.decode("utf8")
|
| 35 |
+
if isinstance(beta_value, (str,)) or key[-4:] == "-bin":
|
| 36 |
+
value = beta_value
|
| 37 |
+
else:
|
| 38 |
+
value = beta_value.decode("utf8")
|
| 39 |
+
return _Metadatum(key, value)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def beta(metadata):
|
| 43 |
+
if metadata is None:
|
| 44 |
+
return ()
|
| 45 |
+
else:
|
| 46 |
+
return tuple(_beta_metadatum(key, value) for key, value in metadata)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def unbeta(beta_metadata):
|
| 50 |
+
if beta_metadata is None:
|
| 51 |
+
return ()
|
| 52 |
+
else:
|
| 53 |
+
return tuple(
|
| 54 |
+
_metadatum(beta_key, beta_value)
|
| 55 |
+
for beta_key, beta_value in beta_metadata
|
| 56 |
+
)
|
.venv/lib/python3.11/site-packages/grpc/beta/_server_adaptations.py
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Translates gRPC's server-side API into gRPC's server-side Beta API."""
|
| 15 |
+
|
| 16 |
+
import collections
|
| 17 |
+
import threading
|
| 18 |
+
|
| 19 |
+
import grpc
|
| 20 |
+
from grpc import _common
|
| 21 |
+
from grpc.beta import _metadata
|
| 22 |
+
from grpc.beta import interfaces
|
| 23 |
+
from grpc.framework.common import cardinality
|
| 24 |
+
from grpc.framework.common import style
|
| 25 |
+
from grpc.framework.foundation import abandonment
|
| 26 |
+
from grpc.framework.foundation import logging_pool
|
| 27 |
+
from grpc.framework.foundation import stream
|
| 28 |
+
from grpc.framework.interfaces.face import face
|
| 29 |
+
|
| 30 |
+
# pylint: disable=too-many-return-statements
|
| 31 |
+
|
| 32 |
+
_DEFAULT_POOL_SIZE = 8
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class _ServerProtocolContext(interfaces.GRPCServicerContext):
|
| 36 |
+
def __init__(self, servicer_context):
|
| 37 |
+
self._servicer_context = servicer_context
|
| 38 |
+
|
| 39 |
+
def peer(self):
|
| 40 |
+
return self._servicer_context.peer()
|
| 41 |
+
|
| 42 |
+
def disable_next_response_compression(self):
|
| 43 |
+
pass # TODO(https://github.com/grpc/grpc/issues/4078): design, implement.
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class _FaceServicerContext(face.ServicerContext):
|
| 47 |
+
def __init__(self, servicer_context):
|
| 48 |
+
self._servicer_context = servicer_context
|
| 49 |
+
|
| 50 |
+
def is_active(self):
|
| 51 |
+
return self._servicer_context.is_active()
|
| 52 |
+
|
| 53 |
+
def time_remaining(self):
|
| 54 |
+
return self._servicer_context.time_remaining()
|
| 55 |
+
|
| 56 |
+
def add_abortion_callback(self, abortion_callback):
|
| 57 |
+
raise NotImplementedError(
|
| 58 |
+
"add_abortion_callback no longer supported server-side!"
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
def cancel(self):
|
| 62 |
+
self._servicer_context.cancel()
|
| 63 |
+
|
| 64 |
+
def protocol_context(self):
|
| 65 |
+
return _ServerProtocolContext(self._servicer_context)
|
| 66 |
+
|
| 67 |
+
def invocation_metadata(self):
|
| 68 |
+
return _metadata.beta(self._servicer_context.invocation_metadata())
|
| 69 |
+
|
| 70 |
+
def initial_metadata(self, initial_metadata):
|
| 71 |
+
self._servicer_context.send_initial_metadata(
|
| 72 |
+
_metadata.unbeta(initial_metadata)
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
def terminal_metadata(self, terminal_metadata):
|
| 76 |
+
self._servicer_context.set_terminal_metadata(
|
| 77 |
+
_metadata.unbeta(terminal_metadata)
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
def code(self, code):
|
| 81 |
+
self._servicer_context.set_code(code)
|
| 82 |
+
|
| 83 |
+
def details(self, details):
|
| 84 |
+
self._servicer_context.set_details(details)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def _adapt_unary_request_inline(unary_request_inline):
|
| 88 |
+
def adaptation(request, servicer_context):
|
| 89 |
+
return unary_request_inline(
|
| 90 |
+
request, _FaceServicerContext(servicer_context)
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
return adaptation
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def _adapt_stream_request_inline(stream_request_inline):
|
| 97 |
+
def adaptation(request_iterator, servicer_context):
|
| 98 |
+
return stream_request_inline(
|
| 99 |
+
request_iterator, _FaceServicerContext(servicer_context)
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
return adaptation
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class _Callback(stream.Consumer):
|
| 106 |
+
def __init__(self):
|
| 107 |
+
self._condition = threading.Condition()
|
| 108 |
+
self._values = []
|
| 109 |
+
self._terminated = False
|
| 110 |
+
self._cancelled = False
|
| 111 |
+
|
| 112 |
+
def consume(self, value):
|
| 113 |
+
with self._condition:
|
| 114 |
+
self._values.append(value)
|
| 115 |
+
self._condition.notify_all()
|
| 116 |
+
|
| 117 |
+
def terminate(self):
|
| 118 |
+
with self._condition:
|
| 119 |
+
self._terminated = True
|
| 120 |
+
self._condition.notify_all()
|
| 121 |
+
|
| 122 |
+
def consume_and_terminate(self, value):
|
| 123 |
+
with self._condition:
|
| 124 |
+
self._values.append(value)
|
| 125 |
+
self._terminated = True
|
| 126 |
+
self._condition.notify_all()
|
| 127 |
+
|
| 128 |
+
def cancel(self):
|
| 129 |
+
with self._condition:
|
| 130 |
+
self._cancelled = True
|
| 131 |
+
self._condition.notify_all()
|
| 132 |
+
|
| 133 |
+
def draw_one_value(self):
|
| 134 |
+
with self._condition:
|
| 135 |
+
while True:
|
| 136 |
+
if self._cancelled:
|
| 137 |
+
raise abandonment.Abandoned()
|
| 138 |
+
elif self._values:
|
| 139 |
+
return self._values.pop(0)
|
| 140 |
+
elif self._terminated:
|
| 141 |
+
return None
|
| 142 |
+
else:
|
| 143 |
+
self._condition.wait()
|
| 144 |
+
|
| 145 |
+
def draw_all_values(self):
|
| 146 |
+
with self._condition:
|
| 147 |
+
while True:
|
| 148 |
+
if self._cancelled:
|
| 149 |
+
raise abandonment.Abandoned()
|
| 150 |
+
elif self._terminated:
|
| 151 |
+
all_values = tuple(self._values)
|
| 152 |
+
self._values = None
|
| 153 |
+
return all_values
|
| 154 |
+
else:
|
| 155 |
+
self._condition.wait()
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def _run_request_pipe_thread(
|
| 159 |
+
request_iterator, request_consumer, servicer_context
|
| 160 |
+
):
|
| 161 |
+
thread_joined = threading.Event()
|
| 162 |
+
|
| 163 |
+
def pipe_requests():
|
| 164 |
+
for request in request_iterator:
|
| 165 |
+
if not servicer_context.is_active() or thread_joined.is_set():
|
| 166 |
+
return
|
| 167 |
+
request_consumer.consume(request)
|
| 168 |
+
if not servicer_context.is_active() or thread_joined.is_set():
|
| 169 |
+
return
|
| 170 |
+
request_consumer.terminate()
|
| 171 |
+
|
| 172 |
+
request_pipe_thread = threading.Thread(target=pipe_requests)
|
| 173 |
+
request_pipe_thread.daemon = True
|
| 174 |
+
request_pipe_thread.start()
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def _adapt_unary_unary_event(unary_unary_event):
|
| 178 |
+
def adaptation(request, servicer_context):
|
| 179 |
+
callback = _Callback()
|
| 180 |
+
if not servicer_context.add_callback(callback.cancel):
|
| 181 |
+
raise abandonment.Abandoned()
|
| 182 |
+
unary_unary_event(
|
| 183 |
+
request,
|
| 184 |
+
callback.consume_and_terminate,
|
| 185 |
+
_FaceServicerContext(servicer_context),
|
| 186 |
+
)
|
| 187 |
+
return callback.draw_all_values()[0]
|
| 188 |
+
|
| 189 |
+
return adaptation
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def _adapt_unary_stream_event(unary_stream_event):
|
| 193 |
+
def adaptation(request, servicer_context):
|
| 194 |
+
callback = _Callback()
|
| 195 |
+
if not servicer_context.add_callback(callback.cancel):
|
| 196 |
+
raise abandonment.Abandoned()
|
| 197 |
+
unary_stream_event(
|
| 198 |
+
request, callback, _FaceServicerContext(servicer_context)
|
| 199 |
+
)
|
| 200 |
+
while True:
|
| 201 |
+
response = callback.draw_one_value()
|
| 202 |
+
if response is None:
|
| 203 |
+
return
|
| 204 |
+
else:
|
| 205 |
+
yield response
|
| 206 |
+
|
| 207 |
+
return adaptation
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
def _adapt_stream_unary_event(stream_unary_event):
|
| 211 |
+
def adaptation(request_iterator, servicer_context):
|
| 212 |
+
callback = _Callback()
|
| 213 |
+
if not servicer_context.add_callback(callback.cancel):
|
| 214 |
+
raise abandonment.Abandoned()
|
| 215 |
+
request_consumer = stream_unary_event(
|
| 216 |
+
callback.consume_and_terminate,
|
| 217 |
+
_FaceServicerContext(servicer_context),
|
| 218 |
+
)
|
| 219 |
+
_run_request_pipe_thread(
|
| 220 |
+
request_iterator, request_consumer, servicer_context
|
| 221 |
+
)
|
| 222 |
+
return callback.draw_all_values()[0]
|
| 223 |
+
|
| 224 |
+
return adaptation
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def _adapt_stream_stream_event(stream_stream_event):
|
| 228 |
+
def adaptation(request_iterator, servicer_context):
|
| 229 |
+
callback = _Callback()
|
| 230 |
+
if not servicer_context.add_callback(callback.cancel):
|
| 231 |
+
raise abandonment.Abandoned()
|
| 232 |
+
request_consumer = stream_stream_event(
|
| 233 |
+
callback, _FaceServicerContext(servicer_context)
|
| 234 |
+
)
|
| 235 |
+
_run_request_pipe_thread(
|
| 236 |
+
request_iterator, request_consumer, servicer_context
|
| 237 |
+
)
|
| 238 |
+
while True:
|
| 239 |
+
response = callback.draw_one_value()
|
| 240 |
+
if response is None:
|
| 241 |
+
return
|
| 242 |
+
else:
|
| 243 |
+
yield response
|
| 244 |
+
|
| 245 |
+
return adaptation
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
class _SimpleMethodHandler(
|
| 249 |
+
collections.namedtuple(
|
| 250 |
+
"_MethodHandler",
|
| 251 |
+
(
|
| 252 |
+
"request_streaming",
|
| 253 |
+
"response_streaming",
|
| 254 |
+
"request_deserializer",
|
| 255 |
+
"response_serializer",
|
| 256 |
+
"unary_unary",
|
| 257 |
+
"unary_stream",
|
| 258 |
+
"stream_unary",
|
| 259 |
+
"stream_stream",
|
| 260 |
+
),
|
| 261 |
+
),
|
| 262 |
+
grpc.RpcMethodHandler,
|
| 263 |
+
):
|
| 264 |
+
pass
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
def _simple_method_handler(
|
| 268 |
+
implementation, request_deserializer, response_serializer
|
| 269 |
+
):
|
| 270 |
+
if implementation.style is style.Service.INLINE:
|
| 271 |
+
if implementation.cardinality is cardinality.Cardinality.UNARY_UNARY:
|
| 272 |
+
return _SimpleMethodHandler(
|
| 273 |
+
False,
|
| 274 |
+
False,
|
| 275 |
+
request_deserializer,
|
| 276 |
+
response_serializer,
|
| 277 |
+
_adapt_unary_request_inline(implementation.unary_unary_inline),
|
| 278 |
+
None,
|
| 279 |
+
None,
|
| 280 |
+
None,
|
| 281 |
+
)
|
| 282 |
+
elif implementation.cardinality is cardinality.Cardinality.UNARY_STREAM:
|
| 283 |
+
return _SimpleMethodHandler(
|
| 284 |
+
False,
|
| 285 |
+
True,
|
| 286 |
+
request_deserializer,
|
| 287 |
+
response_serializer,
|
| 288 |
+
None,
|
| 289 |
+
_adapt_unary_request_inline(implementation.unary_stream_inline),
|
| 290 |
+
None,
|
| 291 |
+
None,
|
| 292 |
+
)
|
| 293 |
+
elif implementation.cardinality is cardinality.Cardinality.STREAM_UNARY:
|
| 294 |
+
return _SimpleMethodHandler(
|
| 295 |
+
True,
|
| 296 |
+
False,
|
| 297 |
+
request_deserializer,
|
| 298 |
+
response_serializer,
|
| 299 |
+
None,
|
| 300 |
+
None,
|
| 301 |
+
_adapt_stream_request_inline(
|
| 302 |
+
implementation.stream_unary_inline
|
| 303 |
+
),
|
| 304 |
+
None,
|
| 305 |
+
)
|
| 306 |
+
elif (
|
| 307 |
+
implementation.cardinality is cardinality.Cardinality.STREAM_STREAM
|
| 308 |
+
):
|
| 309 |
+
return _SimpleMethodHandler(
|
| 310 |
+
True,
|
| 311 |
+
True,
|
| 312 |
+
request_deserializer,
|
| 313 |
+
response_serializer,
|
| 314 |
+
None,
|
| 315 |
+
None,
|
| 316 |
+
None,
|
| 317 |
+
_adapt_stream_request_inline(
|
| 318 |
+
implementation.stream_stream_inline
|
| 319 |
+
),
|
| 320 |
+
)
|
| 321 |
+
elif implementation.style is style.Service.EVENT:
|
| 322 |
+
if implementation.cardinality is cardinality.Cardinality.UNARY_UNARY:
|
| 323 |
+
return _SimpleMethodHandler(
|
| 324 |
+
False,
|
| 325 |
+
False,
|
| 326 |
+
request_deserializer,
|
| 327 |
+
response_serializer,
|
| 328 |
+
_adapt_unary_unary_event(implementation.unary_unary_event),
|
| 329 |
+
None,
|
| 330 |
+
None,
|
| 331 |
+
None,
|
| 332 |
+
)
|
| 333 |
+
elif implementation.cardinality is cardinality.Cardinality.UNARY_STREAM:
|
| 334 |
+
return _SimpleMethodHandler(
|
| 335 |
+
False,
|
| 336 |
+
True,
|
| 337 |
+
request_deserializer,
|
| 338 |
+
response_serializer,
|
| 339 |
+
None,
|
| 340 |
+
_adapt_unary_stream_event(implementation.unary_stream_event),
|
| 341 |
+
None,
|
| 342 |
+
None,
|
| 343 |
+
)
|
| 344 |
+
elif implementation.cardinality is cardinality.Cardinality.STREAM_UNARY:
|
| 345 |
+
return _SimpleMethodHandler(
|
| 346 |
+
True,
|
| 347 |
+
False,
|
| 348 |
+
request_deserializer,
|
| 349 |
+
response_serializer,
|
| 350 |
+
None,
|
| 351 |
+
None,
|
| 352 |
+
_adapt_stream_unary_event(implementation.stream_unary_event),
|
| 353 |
+
None,
|
| 354 |
+
)
|
| 355 |
+
elif (
|
| 356 |
+
implementation.cardinality is cardinality.Cardinality.STREAM_STREAM
|
| 357 |
+
):
|
| 358 |
+
return _SimpleMethodHandler(
|
| 359 |
+
True,
|
| 360 |
+
True,
|
| 361 |
+
request_deserializer,
|
| 362 |
+
response_serializer,
|
| 363 |
+
None,
|
| 364 |
+
None,
|
| 365 |
+
None,
|
| 366 |
+
_adapt_stream_stream_event(implementation.stream_stream_event),
|
| 367 |
+
)
|
| 368 |
+
raise ValueError()
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def _flatten_method_pair_map(method_pair_map):
|
| 372 |
+
method_pair_map = method_pair_map or {}
|
| 373 |
+
flat_map = {}
|
| 374 |
+
for method_pair in method_pair_map:
|
| 375 |
+
method = _common.fully_qualified_method(method_pair[0], method_pair[1])
|
| 376 |
+
flat_map[method] = method_pair_map[method_pair]
|
| 377 |
+
return flat_map
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
class _GenericRpcHandler(grpc.GenericRpcHandler):
|
| 381 |
+
def __init__(
|
| 382 |
+
self,
|
| 383 |
+
method_implementations,
|
| 384 |
+
multi_method_implementation,
|
| 385 |
+
request_deserializers,
|
| 386 |
+
response_serializers,
|
| 387 |
+
):
|
| 388 |
+
self._method_implementations = _flatten_method_pair_map(
|
| 389 |
+
method_implementations
|
| 390 |
+
)
|
| 391 |
+
self._request_deserializers = _flatten_method_pair_map(
|
| 392 |
+
request_deserializers
|
| 393 |
+
)
|
| 394 |
+
self._response_serializers = _flatten_method_pair_map(
|
| 395 |
+
response_serializers
|
| 396 |
+
)
|
| 397 |
+
self._multi_method_implementation = multi_method_implementation
|
| 398 |
+
|
| 399 |
+
def service(self, handler_call_details):
|
| 400 |
+
method_implementation = self._method_implementations.get(
|
| 401 |
+
handler_call_details.method
|
| 402 |
+
)
|
| 403 |
+
if method_implementation is not None:
|
| 404 |
+
return _simple_method_handler(
|
| 405 |
+
method_implementation,
|
| 406 |
+
self._request_deserializers.get(handler_call_details.method),
|
| 407 |
+
self._response_serializers.get(handler_call_details.method),
|
| 408 |
+
)
|
| 409 |
+
elif self._multi_method_implementation is None:
|
| 410 |
+
return None
|
| 411 |
+
else:
|
| 412 |
+
try:
|
| 413 |
+
return None # TODO(nathaniel): call the multimethod.
|
| 414 |
+
except face.NoSuchMethodError:
|
| 415 |
+
return None
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
class _Server(interfaces.Server):
|
| 419 |
+
def __init__(self, grpc_server):
|
| 420 |
+
self._grpc_server = grpc_server
|
| 421 |
+
|
| 422 |
+
def add_insecure_port(self, address):
|
| 423 |
+
return self._grpc_server.add_insecure_port(address)
|
| 424 |
+
|
| 425 |
+
def add_secure_port(self, address, server_credentials):
|
| 426 |
+
return self._grpc_server.add_secure_port(address, server_credentials)
|
| 427 |
+
|
| 428 |
+
def start(self):
|
| 429 |
+
self._grpc_server.start()
|
| 430 |
+
|
| 431 |
+
def stop(self, grace):
|
| 432 |
+
return self._grpc_server.stop(grace)
|
| 433 |
+
|
| 434 |
+
def __enter__(self):
|
| 435 |
+
self._grpc_server.start()
|
| 436 |
+
return self
|
| 437 |
+
|
| 438 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 439 |
+
self._grpc_server.stop(None)
|
| 440 |
+
return False
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
def server(
|
| 444 |
+
service_implementations,
|
| 445 |
+
multi_method_implementation,
|
| 446 |
+
request_deserializers,
|
| 447 |
+
response_serializers,
|
| 448 |
+
thread_pool,
|
| 449 |
+
thread_pool_size,
|
| 450 |
+
):
|
| 451 |
+
generic_rpc_handler = _GenericRpcHandler(
|
| 452 |
+
service_implementations,
|
| 453 |
+
multi_method_implementation,
|
| 454 |
+
request_deserializers,
|
| 455 |
+
response_serializers,
|
| 456 |
+
)
|
| 457 |
+
if thread_pool is None:
|
| 458 |
+
effective_thread_pool = logging_pool.pool(
|
| 459 |
+
_DEFAULT_POOL_SIZE if thread_pool_size is None else thread_pool_size
|
| 460 |
+
)
|
| 461 |
+
else:
|
| 462 |
+
effective_thread_pool = thread_pool
|
| 463 |
+
return _Server(
|
| 464 |
+
grpc.server(effective_thread_pool, handlers=(generic_rpc_handler,))
|
| 465 |
+
)
|
.venv/lib/python3.11/site-packages/grpc/beta/implementations.py
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015-2016 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Entry points into the Beta API of gRPC Python."""
|
| 15 |
+
|
| 16 |
+
# threading is referenced from specification in this module.
|
| 17 |
+
import threading # pylint: disable=unused-import
|
| 18 |
+
|
| 19 |
+
# interfaces, cardinality, and face are referenced from specification in this
|
| 20 |
+
# module.
|
| 21 |
+
import grpc
|
| 22 |
+
from grpc import _auth
|
| 23 |
+
from grpc.beta import _client_adaptations
|
| 24 |
+
from grpc.beta import _metadata
|
| 25 |
+
from grpc.beta import _server_adaptations
|
| 26 |
+
from grpc.beta import interfaces # pylint: disable=unused-import
|
| 27 |
+
from grpc.framework.common import cardinality # pylint: disable=unused-import
|
| 28 |
+
from grpc.framework.interfaces.face import face # pylint: disable=unused-import
|
| 29 |
+
|
| 30 |
+
# pylint: disable=too-many-arguments
|
| 31 |
+
|
| 32 |
+
ChannelCredentials = grpc.ChannelCredentials
|
| 33 |
+
ssl_channel_credentials = grpc.ssl_channel_credentials
|
| 34 |
+
CallCredentials = grpc.CallCredentials
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def metadata_call_credentials(metadata_plugin, name=None):
|
| 38 |
+
def plugin(context, callback):
|
| 39 |
+
def wrapped_callback(beta_metadata, error):
|
| 40 |
+
callback(_metadata.unbeta(beta_metadata), error)
|
| 41 |
+
|
| 42 |
+
metadata_plugin(context, wrapped_callback)
|
| 43 |
+
|
| 44 |
+
return grpc.metadata_call_credentials(plugin, name=name)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def google_call_credentials(credentials):
|
| 48 |
+
"""Construct CallCredentials from GoogleCredentials.
|
| 49 |
+
|
| 50 |
+
Args:
|
| 51 |
+
credentials: A GoogleCredentials object from the oauth2client library.
|
| 52 |
+
|
| 53 |
+
Returns:
|
| 54 |
+
A CallCredentials object for use in a GRPCCallOptions object.
|
| 55 |
+
"""
|
| 56 |
+
return metadata_call_credentials(_auth.GoogleCallCredentials(credentials))
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
access_token_call_credentials = grpc.access_token_call_credentials
|
| 60 |
+
composite_call_credentials = grpc.composite_call_credentials
|
| 61 |
+
composite_channel_credentials = grpc.composite_channel_credentials
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class Channel(object):
|
| 65 |
+
"""A channel to a remote host through which RPCs may be conducted.
|
| 66 |
+
|
| 67 |
+
Only the "subscribe" and "unsubscribe" methods are supported for application
|
| 68 |
+
use. This class' instance constructor and all other attributes are
|
| 69 |
+
unsupported.
|
| 70 |
+
"""
|
| 71 |
+
|
| 72 |
+
def __init__(self, channel):
|
| 73 |
+
self._channel = channel
|
| 74 |
+
|
| 75 |
+
def subscribe(self, callback, try_to_connect=None):
|
| 76 |
+
"""Subscribes to this Channel's connectivity.
|
| 77 |
+
|
| 78 |
+
Args:
|
| 79 |
+
callback: A callable to be invoked and passed an
|
| 80 |
+
interfaces.ChannelConnectivity identifying this Channel's connectivity.
|
| 81 |
+
The callable will be invoked immediately upon subscription and again for
|
| 82 |
+
every change to this Channel's connectivity thereafter until it is
|
| 83 |
+
unsubscribed.
|
| 84 |
+
try_to_connect: A boolean indicating whether or not this Channel should
|
| 85 |
+
attempt to connect if it is not already connected and ready to conduct
|
| 86 |
+
RPCs.
|
| 87 |
+
"""
|
| 88 |
+
self._channel.subscribe(callback, try_to_connect=try_to_connect)
|
| 89 |
+
|
| 90 |
+
def unsubscribe(self, callback):
|
| 91 |
+
"""Unsubscribes a callback from this Channel's connectivity.
|
| 92 |
+
|
| 93 |
+
Args:
|
| 94 |
+
callback: A callable previously registered with this Channel from having
|
| 95 |
+
been passed to its "subscribe" method.
|
| 96 |
+
"""
|
| 97 |
+
self._channel.unsubscribe(callback)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def insecure_channel(host, port):
|
| 101 |
+
"""Creates an insecure Channel to a remote host.
|
| 102 |
+
|
| 103 |
+
Args:
|
| 104 |
+
host: The name of the remote host to which to connect.
|
| 105 |
+
port: The port of the remote host to which to connect.
|
| 106 |
+
If None only the 'host' part will be used.
|
| 107 |
+
|
| 108 |
+
Returns:
|
| 109 |
+
A Channel to the remote host through which RPCs may be conducted.
|
| 110 |
+
"""
|
| 111 |
+
channel = grpc.insecure_channel(
|
| 112 |
+
host if port is None else "%s:%d" % (host, port)
|
| 113 |
+
)
|
| 114 |
+
return Channel(channel)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def secure_channel(host, port, channel_credentials):
|
| 118 |
+
"""Creates a secure Channel to a remote host.
|
| 119 |
+
|
| 120 |
+
Args:
|
| 121 |
+
host: The name of the remote host to which to connect.
|
| 122 |
+
port: The port of the remote host to which to connect.
|
| 123 |
+
If None only the 'host' part will be used.
|
| 124 |
+
channel_credentials: A ChannelCredentials.
|
| 125 |
+
|
| 126 |
+
Returns:
|
| 127 |
+
A secure Channel to the remote host through which RPCs may be conducted.
|
| 128 |
+
"""
|
| 129 |
+
channel = grpc.secure_channel(
|
| 130 |
+
host if port is None else "%s:%d" % (host, port), channel_credentials
|
| 131 |
+
)
|
| 132 |
+
return Channel(channel)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
class StubOptions(object):
|
| 136 |
+
"""A value encapsulating the various options for creation of a Stub.
|
| 137 |
+
|
| 138 |
+
This class and its instances have no supported interface - it exists to define
|
| 139 |
+
the type of its instances and its instances exist to be passed to other
|
| 140 |
+
functions.
|
| 141 |
+
"""
|
| 142 |
+
|
| 143 |
+
def __init__(
|
| 144 |
+
self,
|
| 145 |
+
host,
|
| 146 |
+
request_serializers,
|
| 147 |
+
response_deserializers,
|
| 148 |
+
metadata_transformer,
|
| 149 |
+
thread_pool,
|
| 150 |
+
thread_pool_size,
|
| 151 |
+
):
|
| 152 |
+
self.host = host
|
| 153 |
+
self.request_serializers = request_serializers
|
| 154 |
+
self.response_deserializers = response_deserializers
|
| 155 |
+
self.metadata_transformer = metadata_transformer
|
| 156 |
+
self.thread_pool = thread_pool
|
| 157 |
+
self.thread_pool_size = thread_pool_size
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
_EMPTY_STUB_OPTIONS = StubOptions(None, None, None, None, None, None)
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def stub_options(
|
| 164 |
+
host=None,
|
| 165 |
+
request_serializers=None,
|
| 166 |
+
response_deserializers=None,
|
| 167 |
+
metadata_transformer=None,
|
| 168 |
+
thread_pool=None,
|
| 169 |
+
thread_pool_size=None,
|
| 170 |
+
):
|
| 171 |
+
"""Creates a StubOptions value to be passed at stub creation.
|
| 172 |
+
|
| 173 |
+
All parameters are optional and should always be passed by keyword.
|
| 174 |
+
|
| 175 |
+
Args:
|
| 176 |
+
host: A host string to set on RPC calls.
|
| 177 |
+
request_serializers: A dictionary from service name-method name pair to
|
| 178 |
+
request serialization behavior.
|
| 179 |
+
response_deserializers: A dictionary from service name-method name pair to
|
| 180 |
+
response deserialization behavior.
|
| 181 |
+
metadata_transformer: A callable that given a metadata object produces
|
| 182 |
+
another metadata object to be used in the underlying communication on the
|
| 183 |
+
wire.
|
| 184 |
+
thread_pool: A thread pool to use in stubs.
|
| 185 |
+
thread_pool_size: The size of thread pool to create for use in stubs;
|
| 186 |
+
ignored if thread_pool has been passed.
|
| 187 |
+
|
| 188 |
+
Returns:
|
| 189 |
+
A StubOptions value created from the passed parameters.
|
| 190 |
+
"""
|
| 191 |
+
return StubOptions(
|
| 192 |
+
host,
|
| 193 |
+
request_serializers,
|
| 194 |
+
response_deserializers,
|
| 195 |
+
metadata_transformer,
|
| 196 |
+
thread_pool,
|
| 197 |
+
thread_pool_size,
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
def generic_stub(channel, options=None):
|
| 202 |
+
"""Creates a face.GenericStub on which RPCs can be made.
|
| 203 |
+
|
| 204 |
+
Args:
|
| 205 |
+
channel: A Channel for use by the created stub.
|
| 206 |
+
options: A StubOptions customizing the created stub.
|
| 207 |
+
|
| 208 |
+
Returns:
|
| 209 |
+
A face.GenericStub on which RPCs can be made.
|
| 210 |
+
"""
|
| 211 |
+
effective_options = _EMPTY_STUB_OPTIONS if options is None else options
|
| 212 |
+
return _client_adaptations.generic_stub(
|
| 213 |
+
channel._channel, # pylint: disable=protected-access
|
| 214 |
+
effective_options.host,
|
| 215 |
+
effective_options.metadata_transformer,
|
| 216 |
+
effective_options.request_serializers,
|
| 217 |
+
effective_options.response_deserializers,
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
def dynamic_stub(channel, service, cardinalities, options=None):
|
| 222 |
+
"""Creates a face.DynamicStub with which RPCs can be invoked.
|
| 223 |
+
|
| 224 |
+
Args:
|
| 225 |
+
channel: A Channel for the returned face.DynamicStub to use.
|
| 226 |
+
service: The package-qualified full name of the service.
|
| 227 |
+
cardinalities: A dictionary from RPC method name to cardinality.Cardinality
|
| 228 |
+
value identifying the cardinality of the RPC method.
|
| 229 |
+
options: An optional StubOptions value further customizing the functionality
|
| 230 |
+
of the returned face.DynamicStub.
|
| 231 |
+
|
| 232 |
+
Returns:
|
| 233 |
+
A face.DynamicStub with which RPCs can be invoked.
|
| 234 |
+
"""
|
| 235 |
+
effective_options = _EMPTY_STUB_OPTIONS if options is None else options
|
| 236 |
+
return _client_adaptations.dynamic_stub(
|
| 237 |
+
channel._channel, # pylint: disable=protected-access
|
| 238 |
+
service,
|
| 239 |
+
cardinalities,
|
| 240 |
+
effective_options.host,
|
| 241 |
+
effective_options.metadata_transformer,
|
| 242 |
+
effective_options.request_serializers,
|
| 243 |
+
effective_options.response_deserializers,
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
ServerCredentials = grpc.ServerCredentials
|
| 248 |
+
ssl_server_credentials = grpc.ssl_server_credentials
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
class ServerOptions(object):
|
| 252 |
+
"""A value encapsulating the various options for creation of a Server.
|
| 253 |
+
|
| 254 |
+
This class and its instances have no supported interface - it exists to define
|
| 255 |
+
the type of its instances and its instances exist to be passed to other
|
| 256 |
+
functions.
|
| 257 |
+
"""
|
| 258 |
+
|
| 259 |
+
def __init__(
|
| 260 |
+
self,
|
| 261 |
+
multi_method_implementation,
|
| 262 |
+
request_deserializers,
|
| 263 |
+
response_serializers,
|
| 264 |
+
thread_pool,
|
| 265 |
+
thread_pool_size,
|
| 266 |
+
default_timeout,
|
| 267 |
+
maximum_timeout,
|
| 268 |
+
):
|
| 269 |
+
self.multi_method_implementation = multi_method_implementation
|
| 270 |
+
self.request_deserializers = request_deserializers
|
| 271 |
+
self.response_serializers = response_serializers
|
| 272 |
+
self.thread_pool = thread_pool
|
| 273 |
+
self.thread_pool_size = thread_pool_size
|
| 274 |
+
self.default_timeout = default_timeout
|
| 275 |
+
self.maximum_timeout = maximum_timeout
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
_EMPTY_SERVER_OPTIONS = ServerOptions(None, None, None, None, None, None, None)
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def server_options(
|
| 282 |
+
multi_method_implementation=None,
|
| 283 |
+
request_deserializers=None,
|
| 284 |
+
response_serializers=None,
|
| 285 |
+
thread_pool=None,
|
| 286 |
+
thread_pool_size=None,
|
| 287 |
+
default_timeout=None,
|
| 288 |
+
maximum_timeout=None,
|
| 289 |
+
):
|
| 290 |
+
"""Creates a ServerOptions value to be passed at server creation.
|
| 291 |
+
|
| 292 |
+
All parameters are optional and should always be passed by keyword.
|
| 293 |
+
|
| 294 |
+
Args:
|
| 295 |
+
multi_method_implementation: A face.MultiMethodImplementation to be called
|
| 296 |
+
to service an RPC if the server has no specific method implementation for
|
| 297 |
+
the name of the RPC for which service was requested.
|
| 298 |
+
request_deserializers: A dictionary from service name-method name pair to
|
| 299 |
+
request deserialization behavior.
|
| 300 |
+
response_serializers: A dictionary from service name-method name pair to
|
| 301 |
+
response serialization behavior.
|
| 302 |
+
thread_pool: A thread pool to use in stubs.
|
| 303 |
+
thread_pool_size: The size of thread pool to create for use in stubs;
|
| 304 |
+
ignored if thread_pool has been passed.
|
| 305 |
+
default_timeout: A duration in seconds to allow for RPC service when
|
| 306 |
+
servicing RPCs that did not include a timeout value when invoked.
|
| 307 |
+
maximum_timeout: A duration in seconds to allow for RPC service when
|
| 308 |
+
servicing RPCs no matter what timeout value was passed when the RPC was
|
| 309 |
+
invoked.
|
| 310 |
+
|
| 311 |
+
Returns:
|
| 312 |
+
A StubOptions value created from the passed parameters.
|
| 313 |
+
"""
|
| 314 |
+
return ServerOptions(
|
| 315 |
+
multi_method_implementation,
|
| 316 |
+
request_deserializers,
|
| 317 |
+
response_serializers,
|
| 318 |
+
thread_pool,
|
| 319 |
+
thread_pool_size,
|
| 320 |
+
default_timeout,
|
| 321 |
+
maximum_timeout,
|
| 322 |
+
)
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
def server(service_implementations, options=None):
|
| 326 |
+
"""Creates an interfaces.Server with which RPCs can be serviced.
|
| 327 |
+
|
| 328 |
+
Args:
|
| 329 |
+
service_implementations: A dictionary from service name-method name pair to
|
| 330 |
+
face.MethodImplementation.
|
| 331 |
+
options: An optional ServerOptions value further customizing the
|
| 332 |
+
functionality of the returned Server.
|
| 333 |
+
|
| 334 |
+
Returns:
|
| 335 |
+
An interfaces.Server with which RPCs can be serviced.
|
| 336 |
+
"""
|
| 337 |
+
effective_options = _EMPTY_SERVER_OPTIONS if options is None else options
|
| 338 |
+
return _server_adaptations.server(
|
| 339 |
+
service_implementations,
|
| 340 |
+
effective_options.multi_method_implementation,
|
| 341 |
+
effective_options.request_deserializers,
|
| 342 |
+
effective_options.response_serializers,
|
| 343 |
+
effective_options.thread_pool,
|
| 344 |
+
effective_options.thread_pool_size,
|
| 345 |
+
)
|
.venv/lib/python3.11/site-packages/grpc/beta/interfaces.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Constants and interfaces of the Beta API of gRPC Python."""
|
| 15 |
+
|
| 16 |
+
import abc
|
| 17 |
+
|
| 18 |
+
import grpc
|
| 19 |
+
|
| 20 |
+
ChannelConnectivity = grpc.ChannelConnectivity
|
| 21 |
+
# FATAL_FAILURE was a Beta-API name for SHUTDOWN
|
| 22 |
+
ChannelConnectivity.FATAL_FAILURE = ChannelConnectivity.SHUTDOWN
|
| 23 |
+
|
| 24 |
+
StatusCode = grpc.StatusCode
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class GRPCCallOptions(object):
|
| 28 |
+
"""A value encapsulating gRPC-specific options passed on RPC invocation.
|
| 29 |
+
|
| 30 |
+
This class and its instances have no supported interface - it exists to
|
| 31 |
+
define the type of its instances and its instances exist to be passed to
|
| 32 |
+
other functions.
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
def __init__(self, disable_compression, subcall_of, credentials):
|
| 36 |
+
self.disable_compression = disable_compression
|
| 37 |
+
self.subcall_of = subcall_of
|
| 38 |
+
self.credentials = credentials
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def grpc_call_options(disable_compression=False, credentials=None):
|
| 42 |
+
"""Creates a GRPCCallOptions value to be passed at RPC invocation.
|
| 43 |
+
|
| 44 |
+
All parameters are optional and should always be passed by keyword.
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
disable_compression: A boolean indicating whether or not compression should
|
| 48 |
+
be disabled for the request object of the RPC. Only valid for
|
| 49 |
+
request-unary RPCs.
|
| 50 |
+
credentials: A CallCredentials object to use for the invoked RPC.
|
| 51 |
+
"""
|
| 52 |
+
return GRPCCallOptions(disable_compression, None, credentials)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
GRPCAuthMetadataContext = grpc.AuthMetadataContext
|
| 56 |
+
GRPCAuthMetadataPluginCallback = grpc.AuthMetadataPluginCallback
|
| 57 |
+
GRPCAuthMetadataPlugin = grpc.AuthMetadataPlugin
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class GRPCServicerContext(abc.ABC):
|
| 61 |
+
"""Exposes gRPC-specific options and behaviors to code servicing RPCs."""
|
| 62 |
+
|
| 63 |
+
@abc.abstractmethod
|
| 64 |
+
def peer(self):
|
| 65 |
+
"""Identifies the peer that invoked the RPC being serviced.
|
| 66 |
+
|
| 67 |
+
Returns:
|
| 68 |
+
A string identifying the peer that invoked the RPC being serviced.
|
| 69 |
+
"""
|
| 70 |
+
raise NotImplementedError()
|
| 71 |
+
|
| 72 |
+
@abc.abstractmethod
|
| 73 |
+
def disable_next_response_compression(self):
|
| 74 |
+
"""Disables compression of the next response passed by the application."""
|
| 75 |
+
raise NotImplementedError()
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class GRPCInvocationContext(abc.ABC):
|
| 79 |
+
"""Exposes gRPC-specific options and behaviors to code invoking RPCs."""
|
| 80 |
+
|
| 81 |
+
@abc.abstractmethod
|
| 82 |
+
def disable_next_request_compression(self):
|
| 83 |
+
"""Disables compression of the next request passed by the application."""
|
| 84 |
+
raise NotImplementedError()
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
class Server(abc.ABC):
|
| 88 |
+
"""Services RPCs."""
|
| 89 |
+
|
| 90 |
+
@abc.abstractmethod
|
| 91 |
+
def add_insecure_port(self, address):
|
| 92 |
+
"""Reserves a port for insecure RPC service once this Server becomes active.
|
| 93 |
+
|
| 94 |
+
This method may only be called before calling this Server's start method is
|
| 95 |
+
called.
|
| 96 |
+
|
| 97 |
+
Args:
|
| 98 |
+
address: The address for which to open a port.
|
| 99 |
+
|
| 100 |
+
Returns:
|
| 101 |
+
An integer port on which RPCs will be serviced after this link has been
|
| 102 |
+
started. This is typically the same number as the port number contained
|
| 103 |
+
in the passed address, but will likely be different if the port number
|
| 104 |
+
contained in the passed address was zero.
|
| 105 |
+
"""
|
| 106 |
+
raise NotImplementedError()
|
| 107 |
+
|
| 108 |
+
@abc.abstractmethod
|
| 109 |
+
def add_secure_port(self, address, server_credentials):
|
| 110 |
+
"""Reserves a port for secure RPC service after this Server becomes active.
|
| 111 |
+
|
| 112 |
+
This method may only be called before calling this Server's start method is
|
| 113 |
+
called.
|
| 114 |
+
|
| 115 |
+
Args:
|
| 116 |
+
address: The address for which to open a port.
|
| 117 |
+
server_credentials: A ServerCredentials.
|
| 118 |
+
|
| 119 |
+
Returns:
|
| 120 |
+
An integer port on which RPCs will be serviced after this link has been
|
| 121 |
+
started. This is typically the same number as the port number contained
|
| 122 |
+
in the passed address, but will likely be different if the port number
|
| 123 |
+
contained in the passed address was zero.
|
| 124 |
+
"""
|
| 125 |
+
raise NotImplementedError()
|
| 126 |
+
|
| 127 |
+
@abc.abstractmethod
|
| 128 |
+
def start(self):
|
| 129 |
+
"""Starts this Server's service of RPCs.
|
| 130 |
+
|
| 131 |
+
This method may only be called while the server is not serving RPCs (i.e. it
|
| 132 |
+
is not idempotent).
|
| 133 |
+
"""
|
| 134 |
+
raise NotImplementedError()
|
| 135 |
+
|
| 136 |
+
@abc.abstractmethod
|
| 137 |
+
def stop(self, grace):
|
| 138 |
+
"""Stops this Server's service of RPCs.
|
| 139 |
+
|
| 140 |
+
All calls to this method immediately stop service of new RPCs. When existing
|
| 141 |
+
RPCs are aborted is controlled by the grace period parameter passed to this
|
| 142 |
+
method.
|
| 143 |
+
|
| 144 |
+
This method may be called at any time and is idempotent. Passing a smaller
|
| 145 |
+
grace value than has been passed in a previous call will have the effect of
|
| 146 |
+
stopping the Server sooner. Passing a larger grace value than has been
|
| 147 |
+
passed in a previous call will not have the effect of stopping the server
|
| 148 |
+
later.
|
| 149 |
+
|
| 150 |
+
Args:
|
| 151 |
+
grace: A duration of time in seconds to allow existing RPCs to complete
|
| 152 |
+
before being aborted by this Server's stopping. May be zero for
|
| 153 |
+
immediate abortion of all in-progress RPCs.
|
| 154 |
+
|
| 155 |
+
Returns:
|
| 156 |
+
A threading.Event that will be set when this Server has completely
|
| 157 |
+
stopped. The returned event may not be set until after the full grace
|
| 158 |
+
period (if some ongoing RPC continues for the full length of the period)
|
| 159 |
+
of it may be set much sooner (such as if this Server had no RPCs underway
|
| 160 |
+
at the time it was stopped or if all RPCs that it had underway completed
|
| 161 |
+
very early in the grace period).
|
| 162 |
+
"""
|
| 163 |
+
raise NotImplementedError()
|
.venv/lib/python3.11/site-packages/grpc/beta/utilities.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Utilities for the gRPC Python Beta API."""
|
| 15 |
+
|
| 16 |
+
import threading
|
| 17 |
+
import time
|
| 18 |
+
|
| 19 |
+
# implementations is referenced from specification in this module.
|
| 20 |
+
from grpc.beta import implementations # pylint: disable=unused-import
|
| 21 |
+
from grpc.beta import interfaces
|
| 22 |
+
from grpc.framework.foundation import callable_util
|
| 23 |
+
from grpc.framework.foundation import future
|
| 24 |
+
|
| 25 |
+
_DONE_CALLBACK_EXCEPTION_LOG_MESSAGE = (
|
| 26 |
+
'Exception calling connectivity future "done" callback!'
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class _ChannelReadyFuture(future.Future):
|
| 31 |
+
def __init__(self, channel):
|
| 32 |
+
self._condition = threading.Condition()
|
| 33 |
+
self._channel = channel
|
| 34 |
+
|
| 35 |
+
self._matured = False
|
| 36 |
+
self._cancelled = False
|
| 37 |
+
self._done_callbacks = []
|
| 38 |
+
|
| 39 |
+
def _block(self, timeout):
|
| 40 |
+
until = None if timeout is None else time.time() + timeout
|
| 41 |
+
with self._condition:
|
| 42 |
+
while True:
|
| 43 |
+
if self._cancelled:
|
| 44 |
+
raise future.CancelledError()
|
| 45 |
+
elif self._matured:
|
| 46 |
+
return
|
| 47 |
+
else:
|
| 48 |
+
if until is None:
|
| 49 |
+
self._condition.wait()
|
| 50 |
+
else:
|
| 51 |
+
remaining = until - time.time()
|
| 52 |
+
if remaining < 0:
|
| 53 |
+
raise future.TimeoutError()
|
| 54 |
+
else:
|
| 55 |
+
self._condition.wait(timeout=remaining)
|
| 56 |
+
|
| 57 |
+
def _update(self, connectivity):
|
| 58 |
+
with self._condition:
|
| 59 |
+
if (
|
| 60 |
+
not self._cancelled
|
| 61 |
+
and connectivity is interfaces.ChannelConnectivity.READY
|
| 62 |
+
):
|
| 63 |
+
self._matured = True
|
| 64 |
+
self._channel.unsubscribe(self._update)
|
| 65 |
+
self._condition.notify_all()
|
| 66 |
+
done_callbacks = tuple(self._done_callbacks)
|
| 67 |
+
self._done_callbacks = None
|
| 68 |
+
else:
|
| 69 |
+
return
|
| 70 |
+
|
| 71 |
+
for done_callback in done_callbacks:
|
| 72 |
+
callable_util.call_logging_exceptions(
|
| 73 |
+
done_callback, _DONE_CALLBACK_EXCEPTION_LOG_MESSAGE, self
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
def cancel(self):
|
| 77 |
+
with self._condition:
|
| 78 |
+
if not self._matured:
|
| 79 |
+
self._cancelled = True
|
| 80 |
+
self._channel.unsubscribe(self._update)
|
| 81 |
+
self._condition.notify_all()
|
| 82 |
+
done_callbacks = tuple(self._done_callbacks)
|
| 83 |
+
self._done_callbacks = None
|
| 84 |
+
else:
|
| 85 |
+
return False
|
| 86 |
+
|
| 87 |
+
for done_callback in done_callbacks:
|
| 88 |
+
callable_util.call_logging_exceptions(
|
| 89 |
+
done_callback, _DONE_CALLBACK_EXCEPTION_LOG_MESSAGE, self
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
return True
|
| 93 |
+
|
| 94 |
+
def cancelled(self):
|
| 95 |
+
with self._condition:
|
| 96 |
+
return self._cancelled
|
| 97 |
+
|
| 98 |
+
def running(self):
|
| 99 |
+
with self._condition:
|
| 100 |
+
return not self._cancelled and not self._matured
|
| 101 |
+
|
| 102 |
+
def done(self):
|
| 103 |
+
with self._condition:
|
| 104 |
+
return self._cancelled or self._matured
|
| 105 |
+
|
| 106 |
+
def result(self, timeout=None):
|
| 107 |
+
self._block(timeout)
|
| 108 |
+
return None
|
| 109 |
+
|
| 110 |
+
def exception(self, timeout=None):
|
| 111 |
+
self._block(timeout)
|
| 112 |
+
return None
|
| 113 |
+
|
| 114 |
+
def traceback(self, timeout=None):
|
| 115 |
+
self._block(timeout)
|
| 116 |
+
return None
|
| 117 |
+
|
| 118 |
+
def add_done_callback(self, fn):
|
| 119 |
+
with self._condition:
|
| 120 |
+
if not self._cancelled and not self._matured:
|
| 121 |
+
self._done_callbacks.append(fn)
|
| 122 |
+
return
|
| 123 |
+
|
| 124 |
+
fn(self)
|
| 125 |
+
|
| 126 |
+
def start(self):
|
| 127 |
+
with self._condition:
|
| 128 |
+
self._channel.subscribe(self._update, try_to_connect=True)
|
| 129 |
+
|
| 130 |
+
def __del__(self):
|
| 131 |
+
with self._condition:
|
| 132 |
+
if not self._cancelled and not self._matured:
|
| 133 |
+
self._channel.unsubscribe(self._update)
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def channel_ready_future(channel):
|
| 137 |
+
"""Creates a future.Future tracking when an implementations.Channel is ready.
|
| 138 |
+
|
| 139 |
+
Cancelling the returned future.Future does not tell the given
|
| 140 |
+
implementations.Channel to abandon attempts it may have been making to
|
| 141 |
+
connect; cancelling merely deactivates the return future.Future's
|
| 142 |
+
subscription to the given implementations.Channel's connectivity.
|
| 143 |
+
|
| 144 |
+
Args:
|
| 145 |
+
channel: An implementations.Channel.
|
| 146 |
+
|
| 147 |
+
Returns:
|
| 148 |
+
A future.Future that matures when the given Channel has connectivity
|
| 149 |
+
interfaces.ChannelConnectivity.READY.
|
| 150 |
+
"""
|
| 151 |
+
ready_future = _ChannelReadyFuture(channel)
|
| 152 |
+
ready_future.start()
|
| 153 |
+
return ready_future
|
.venv/lib/python3.11/site-packages/grpc/framework/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
.venv/lib/python3.11/site-packages/grpc/framework/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (187 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/framework/common/__pycache__/cardinality.cpython-311.pyc
ADDED
|
Binary file (902 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/framework/common/__pycache__/style.cpython-311.pyc
ADDED
|
Binary file (726 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/framework/common/cardinality.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Defines an enum for classifying RPC methods by streaming semantics."""
|
| 15 |
+
|
| 16 |
+
import enum
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@enum.unique
|
| 20 |
+
class Cardinality(enum.Enum):
|
| 21 |
+
"""Describes the streaming semantics of an RPC method."""
|
| 22 |
+
|
| 23 |
+
UNARY_UNARY = "request-unary/response-unary"
|
| 24 |
+
UNARY_STREAM = "request-unary/response-streaming"
|
| 25 |
+
STREAM_UNARY = "request-streaming/response-unary"
|
| 26 |
+
STREAM_STREAM = "request-streaming/response-streaming"
|
.venv/lib/python3.11/site-packages/grpc/framework/common/style.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Defines an enum for classifying RPC methods by control flow semantics."""
|
| 15 |
+
|
| 16 |
+
import enum
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@enum.unique
|
| 20 |
+
class Service(enum.Enum):
|
| 21 |
+
"""Describes the control flow style of RPC method implementation."""
|
| 22 |
+
|
| 23 |
+
INLINE = "inline"
|
| 24 |
+
EVENT = "event"
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (198 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/abandonment.cpython-311.pyc
ADDED
|
Binary file (737 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/callable_util.cpython-311.pyc
ADDED
|
Binary file (4.22 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/future.cpython-311.pyc
ADDED
|
Binary file (8.25 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/logging_pool.cpython-311.pyc
ADDED
|
Binary file (3.4 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/stream.cpython-311.pyc
ADDED
|
Binary file (1.61 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/__pycache__/stream_util.cpython-311.pyc
ADDED
|
Binary file (8.83 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/abandonment.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Utilities for indicating abandonment of computation."""
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class Abandoned(Exception):
|
| 18 |
+
"""Indicates that some computation is being abandoned.
|
| 19 |
+
|
| 20 |
+
Abandoning a computation is different than returning a value or raising
|
| 21 |
+
an exception indicating some operational or programming defect.
|
| 22 |
+
"""
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/callable_util.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Utilities for working with callables."""
|
| 15 |
+
|
| 16 |
+
from abc import ABC
|
| 17 |
+
import collections
|
| 18 |
+
import enum
|
| 19 |
+
import functools
|
| 20 |
+
import logging
|
| 21 |
+
|
| 22 |
+
_LOGGER = logging.getLogger(__name__)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class Outcome(ABC):
|
| 26 |
+
"""A sum type describing the outcome of some call.
|
| 27 |
+
|
| 28 |
+
Attributes:
|
| 29 |
+
kind: One of Kind.RETURNED or Kind.RAISED respectively indicating that the
|
| 30 |
+
call returned a value or raised an exception.
|
| 31 |
+
return_value: The value returned by the call. Must be present if kind is
|
| 32 |
+
Kind.RETURNED.
|
| 33 |
+
exception: The exception raised by the call. Must be present if kind is
|
| 34 |
+
Kind.RAISED.
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
@enum.unique
|
| 38 |
+
class Kind(enum.Enum):
|
| 39 |
+
"""Identifies the general kind of the outcome of some call."""
|
| 40 |
+
|
| 41 |
+
RETURNED = object()
|
| 42 |
+
RAISED = object()
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class _EasyOutcome(
|
| 46 |
+
collections.namedtuple(
|
| 47 |
+
"_EasyOutcome", ["kind", "return_value", "exception"]
|
| 48 |
+
),
|
| 49 |
+
Outcome,
|
| 50 |
+
):
|
| 51 |
+
"""A trivial implementation of Outcome."""
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def _call_logging_exceptions(behavior, message, *args, **kwargs):
|
| 55 |
+
try:
|
| 56 |
+
return _EasyOutcome(
|
| 57 |
+
Outcome.Kind.RETURNED, behavior(*args, **kwargs), None
|
| 58 |
+
)
|
| 59 |
+
except Exception as e: # pylint: disable=broad-except
|
| 60 |
+
_LOGGER.exception(message)
|
| 61 |
+
return _EasyOutcome(Outcome.Kind.RAISED, None, e)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def with_exceptions_logged(behavior, message):
|
| 65 |
+
"""Wraps a callable in a try-except that logs any exceptions it raises.
|
| 66 |
+
|
| 67 |
+
Args:
|
| 68 |
+
behavior: Any callable.
|
| 69 |
+
message: A string to log if the behavior raises an exception.
|
| 70 |
+
|
| 71 |
+
Returns:
|
| 72 |
+
A callable that when executed invokes the given behavior. The returned
|
| 73 |
+
callable takes the same arguments as the given behavior but returns a
|
| 74 |
+
future.Outcome describing whether the given behavior returned a value or
|
| 75 |
+
raised an exception.
|
| 76 |
+
"""
|
| 77 |
+
|
| 78 |
+
@functools.wraps(behavior)
|
| 79 |
+
def wrapped_behavior(*args, **kwargs):
|
| 80 |
+
return _call_logging_exceptions(behavior, message, *args, **kwargs)
|
| 81 |
+
|
| 82 |
+
return wrapped_behavior
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def call_logging_exceptions(behavior, message, *args, **kwargs):
|
| 86 |
+
"""Calls a behavior in a try-except that logs any exceptions it raises.
|
| 87 |
+
|
| 88 |
+
Args:
|
| 89 |
+
behavior: Any callable.
|
| 90 |
+
message: A string to log if the behavior raises an exception.
|
| 91 |
+
*args: Positional arguments to pass to the given behavior.
|
| 92 |
+
**kwargs: Keyword arguments to pass to the given behavior.
|
| 93 |
+
|
| 94 |
+
Returns:
|
| 95 |
+
An Outcome describing whether the given behavior returned a value or raised
|
| 96 |
+
an exception.
|
| 97 |
+
"""
|
| 98 |
+
return _call_logging_exceptions(behavior, message, *args, **kwargs)
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/future.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""A Future interface.
|
| 15 |
+
|
| 16 |
+
Python doesn't have a Future interface in its standard library. In the absence
|
| 17 |
+
of such a standard, three separate, incompatible implementations
|
| 18 |
+
(concurrent.futures.Future, ndb.Future, and asyncio.Future) have appeared. This
|
| 19 |
+
interface attempts to be as compatible as possible with
|
| 20 |
+
concurrent.futures.Future. From ndb.Future it adopts a traceback-object accessor
|
| 21 |
+
method.
|
| 22 |
+
|
| 23 |
+
Unlike the concrete and implemented Future classes listed above, the Future
|
| 24 |
+
class defined in this module is an entirely abstract interface that anyone may
|
| 25 |
+
implement and use.
|
| 26 |
+
|
| 27 |
+
The one known incompatibility between this interface and the interface of
|
| 28 |
+
concurrent.futures.Future is that this interface defines its own CancelledError
|
| 29 |
+
and TimeoutError exceptions rather than raising the implementation-private
|
| 30 |
+
concurrent.futures._base.CancelledError and the
|
| 31 |
+
built-in-but-only-in-3.3-and-later TimeoutError.
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
import abc
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class TimeoutError(Exception):
|
| 38 |
+
"""Indicates that a particular call timed out."""
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class CancelledError(Exception):
|
| 42 |
+
"""Indicates that the computation underlying a Future was cancelled."""
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class Future(abc.ABC):
|
| 46 |
+
"""A representation of a computation in another control flow.
|
| 47 |
+
|
| 48 |
+
Computations represented by a Future may be yet to be begun, may be ongoing,
|
| 49 |
+
or may have already completed.
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
# NOTE(nathaniel): This isn't the return type that I would want to have if it
|
| 53 |
+
# were up to me. Were this interface being written from scratch, the return
|
| 54 |
+
# type of this method would probably be a sum type like:
|
| 55 |
+
#
|
| 56 |
+
# NOT_COMMENCED
|
| 57 |
+
# COMMENCED_AND_NOT_COMPLETED
|
| 58 |
+
# PARTIAL_RESULT<Partial_Result_Type>
|
| 59 |
+
# COMPLETED<Result_Type>
|
| 60 |
+
# UNCANCELLABLE
|
| 61 |
+
# NOT_IMMEDIATELY_DETERMINABLE
|
| 62 |
+
@abc.abstractmethod
|
| 63 |
+
def cancel(self):
|
| 64 |
+
"""Attempts to cancel the computation.
|
| 65 |
+
|
| 66 |
+
This method does not block.
|
| 67 |
+
|
| 68 |
+
Returns:
|
| 69 |
+
True if the computation has not yet begun, will not be allowed to take
|
| 70 |
+
place, and determination of both was possible without blocking. False
|
| 71 |
+
under all other circumstances including but not limited to the
|
| 72 |
+
computation's already having begun, the computation's already having
|
| 73 |
+
finished, and the computation's having been scheduled for execution on a
|
| 74 |
+
remote system for which a determination of whether or not it commenced
|
| 75 |
+
before being cancelled cannot be made without blocking.
|
| 76 |
+
"""
|
| 77 |
+
raise NotImplementedError()
|
| 78 |
+
|
| 79 |
+
# NOTE(nathaniel): Here too this isn't the return type that I'd want this
|
| 80 |
+
# method to have if it were up to me. I think I'd go with another sum type
|
| 81 |
+
# like:
|
| 82 |
+
#
|
| 83 |
+
# NOT_CANCELLED (this object's cancel method hasn't been called)
|
| 84 |
+
# NOT_COMMENCED
|
| 85 |
+
# COMMENCED_AND_NOT_COMPLETED
|
| 86 |
+
# PARTIAL_RESULT<Partial_Result_Type>
|
| 87 |
+
# COMPLETED<Result_Type>
|
| 88 |
+
# UNCANCELLABLE
|
| 89 |
+
# NOT_IMMEDIATELY_DETERMINABLE
|
| 90 |
+
#
|
| 91 |
+
# Notice how giving the cancel method the right semantics obviates most
|
| 92 |
+
# reasons for this method to exist.
|
| 93 |
+
@abc.abstractmethod
|
| 94 |
+
def cancelled(self):
|
| 95 |
+
"""Describes whether the computation was cancelled.
|
| 96 |
+
|
| 97 |
+
This method does not block.
|
| 98 |
+
|
| 99 |
+
Returns:
|
| 100 |
+
True if the computation was cancelled any time before its result became
|
| 101 |
+
immediately available. False under all other circumstances including but
|
| 102 |
+
not limited to this object's cancel method not having been called and
|
| 103 |
+
the computation's result having become immediately available.
|
| 104 |
+
"""
|
| 105 |
+
raise NotImplementedError()
|
| 106 |
+
|
| 107 |
+
@abc.abstractmethod
|
| 108 |
+
def running(self):
|
| 109 |
+
"""Describes whether the computation is taking place.
|
| 110 |
+
|
| 111 |
+
This method does not block.
|
| 112 |
+
|
| 113 |
+
Returns:
|
| 114 |
+
True if the computation is scheduled to take place in the future or is
|
| 115 |
+
taking place now, or False if the computation took place in the past or
|
| 116 |
+
was cancelled.
|
| 117 |
+
"""
|
| 118 |
+
raise NotImplementedError()
|
| 119 |
+
|
| 120 |
+
# NOTE(nathaniel): These aren't quite the semantics I'd like here either. I
|
| 121 |
+
# would rather this only returned True in cases in which the underlying
|
| 122 |
+
# computation completed successfully. A computation's having been cancelled
|
| 123 |
+
# conflicts with considering that computation "done".
|
| 124 |
+
@abc.abstractmethod
|
| 125 |
+
def done(self):
|
| 126 |
+
"""Describes whether the computation has taken place.
|
| 127 |
+
|
| 128 |
+
This method does not block.
|
| 129 |
+
|
| 130 |
+
Returns:
|
| 131 |
+
True if the computation is known to have either completed or have been
|
| 132 |
+
unscheduled or interrupted. False if the computation may possibly be
|
| 133 |
+
executing or scheduled to execute later.
|
| 134 |
+
"""
|
| 135 |
+
raise NotImplementedError()
|
| 136 |
+
|
| 137 |
+
@abc.abstractmethod
|
| 138 |
+
def result(self, timeout=None):
|
| 139 |
+
"""Accesses the outcome of the computation or raises its exception.
|
| 140 |
+
|
| 141 |
+
This method may return immediately or may block.
|
| 142 |
+
|
| 143 |
+
Args:
|
| 144 |
+
timeout: The length of time in seconds to wait for the computation to
|
| 145 |
+
finish or be cancelled, or None if this method should block until the
|
| 146 |
+
computation has finished or is cancelled no matter how long that takes.
|
| 147 |
+
|
| 148 |
+
Returns:
|
| 149 |
+
The return value of the computation.
|
| 150 |
+
|
| 151 |
+
Raises:
|
| 152 |
+
TimeoutError: If a timeout value is passed and the computation does not
|
| 153 |
+
terminate within the allotted time.
|
| 154 |
+
CancelledError: If the computation was cancelled.
|
| 155 |
+
Exception: If the computation raised an exception, this call will raise
|
| 156 |
+
the same exception.
|
| 157 |
+
"""
|
| 158 |
+
raise NotImplementedError()
|
| 159 |
+
|
| 160 |
+
@abc.abstractmethod
|
| 161 |
+
def exception(self, timeout=None):
|
| 162 |
+
"""Return the exception raised by the computation.
|
| 163 |
+
|
| 164 |
+
This method may return immediately or may block.
|
| 165 |
+
|
| 166 |
+
Args:
|
| 167 |
+
timeout: The length of time in seconds to wait for the computation to
|
| 168 |
+
terminate or be cancelled, or None if this method should block until
|
| 169 |
+
the computation is terminated or is cancelled no matter how long that
|
| 170 |
+
takes.
|
| 171 |
+
|
| 172 |
+
Returns:
|
| 173 |
+
The exception raised by the computation, or None if the computation did
|
| 174 |
+
not raise an exception.
|
| 175 |
+
|
| 176 |
+
Raises:
|
| 177 |
+
TimeoutError: If a timeout value is passed and the computation does not
|
| 178 |
+
terminate within the allotted time.
|
| 179 |
+
CancelledError: If the computation was cancelled.
|
| 180 |
+
"""
|
| 181 |
+
raise NotImplementedError()
|
| 182 |
+
|
| 183 |
+
@abc.abstractmethod
|
| 184 |
+
def traceback(self, timeout=None):
|
| 185 |
+
"""Access the traceback of the exception raised by the computation.
|
| 186 |
+
|
| 187 |
+
This method may return immediately or may block.
|
| 188 |
+
|
| 189 |
+
Args:
|
| 190 |
+
timeout: The length of time in seconds to wait for the computation to
|
| 191 |
+
terminate or be cancelled, or None if this method should block until
|
| 192 |
+
the computation is terminated or is cancelled no matter how long that
|
| 193 |
+
takes.
|
| 194 |
+
|
| 195 |
+
Returns:
|
| 196 |
+
The traceback of the exception raised by the computation, or None if the
|
| 197 |
+
computation did not raise an exception.
|
| 198 |
+
|
| 199 |
+
Raises:
|
| 200 |
+
TimeoutError: If a timeout value is passed and the computation does not
|
| 201 |
+
terminate within the allotted time.
|
| 202 |
+
CancelledError: If the computation was cancelled.
|
| 203 |
+
"""
|
| 204 |
+
raise NotImplementedError()
|
| 205 |
+
|
| 206 |
+
@abc.abstractmethod
|
| 207 |
+
def add_done_callback(self, fn):
|
| 208 |
+
"""Adds a function to be called at completion of the computation.
|
| 209 |
+
|
| 210 |
+
The callback will be passed this Future object describing the outcome of
|
| 211 |
+
the computation.
|
| 212 |
+
|
| 213 |
+
If the computation has already completed, the callback will be called
|
| 214 |
+
immediately.
|
| 215 |
+
|
| 216 |
+
Args:
|
| 217 |
+
fn: A callable taking this Future object as its single parameter.
|
| 218 |
+
"""
|
| 219 |
+
raise NotImplementedError()
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/logging_pool.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""A thread pool that logs exceptions raised by tasks executed within it."""
|
| 15 |
+
|
| 16 |
+
from concurrent import futures
|
| 17 |
+
import logging
|
| 18 |
+
|
| 19 |
+
_LOGGER = logging.getLogger(__name__)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def _wrap(behavior):
|
| 23 |
+
"""Wraps an arbitrary callable behavior in exception-logging."""
|
| 24 |
+
|
| 25 |
+
def _wrapping(*args, **kwargs):
|
| 26 |
+
try:
|
| 27 |
+
return behavior(*args, **kwargs)
|
| 28 |
+
except Exception:
|
| 29 |
+
_LOGGER.exception(
|
| 30 |
+
"Unexpected exception from %s executed in logging pool!",
|
| 31 |
+
behavior,
|
| 32 |
+
)
|
| 33 |
+
raise
|
| 34 |
+
|
| 35 |
+
return _wrapping
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class _LoggingPool(object):
|
| 39 |
+
"""An exception-logging futures.ThreadPoolExecutor-compatible thread pool."""
|
| 40 |
+
|
| 41 |
+
def __init__(self, backing_pool):
|
| 42 |
+
self._backing_pool = backing_pool
|
| 43 |
+
|
| 44 |
+
def __enter__(self):
|
| 45 |
+
return self
|
| 46 |
+
|
| 47 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 48 |
+
self._backing_pool.shutdown(wait=True)
|
| 49 |
+
|
| 50 |
+
def submit(self, fn, *args, **kwargs):
|
| 51 |
+
return self._backing_pool.submit(_wrap(fn), *args, **kwargs)
|
| 52 |
+
|
| 53 |
+
def map(self, func, *iterables, **kwargs):
|
| 54 |
+
return self._backing_pool.map(
|
| 55 |
+
_wrap(func), *iterables, timeout=kwargs.get("timeout", None)
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
def shutdown(self, wait=True):
|
| 59 |
+
self._backing_pool.shutdown(wait=wait)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def pool(max_workers):
|
| 63 |
+
"""Creates a thread pool that logs exceptions raised by the tasks within it.
|
| 64 |
+
|
| 65 |
+
Args:
|
| 66 |
+
max_workers: The maximum number of worker threads to allow the pool.
|
| 67 |
+
|
| 68 |
+
Returns:
|
| 69 |
+
A futures.ThreadPoolExecutor-compatible thread pool that logs exceptions
|
| 70 |
+
raised by the tasks executed within it.
|
| 71 |
+
"""
|
| 72 |
+
return _LoggingPool(futures.ThreadPoolExecutor(max_workers))
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/stream.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Interfaces related to streams of values or objects."""
|
| 15 |
+
|
| 16 |
+
import abc
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class Consumer(abc.ABC):
|
| 20 |
+
"""Interface for consumers of finite streams of values or objects."""
|
| 21 |
+
|
| 22 |
+
@abc.abstractmethod
|
| 23 |
+
def consume(self, value):
|
| 24 |
+
"""Accepts a value.
|
| 25 |
+
|
| 26 |
+
Args:
|
| 27 |
+
value: Any value accepted by this Consumer.
|
| 28 |
+
"""
|
| 29 |
+
raise NotImplementedError()
|
| 30 |
+
|
| 31 |
+
@abc.abstractmethod
|
| 32 |
+
def terminate(self):
|
| 33 |
+
"""Indicates to this Consumer that no more values will be supplied."""
|
| 34 |
+
raise NotImplementedError()
|
| 35 |
+
|
| 36 |
+
@abc.abstractmethod
|
| 37 |
+
def consume_and_terminate(self, value):
|
| 38 |
+
"""Supplies a value and signals that no more values will be supplied.
|
| 39 |
+
|
| 40 |
+
Args:
|
| 41 |
+
value: Any value accepted by this Consumer.
|
| 42 |
+
"""
|
| 43 |
+
raise NotImplementedError()
|
.venv/lib/python3.11/site-packages/grpc/framework/foundation/stream_util.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 gRPC authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Helpful utilities related to the stream module."""
|
| 15 |
+
|
| 16 |
+
import logging
|
| 17 |
+
import threading
|
| 18 |
+
|
| 19 |
+
from grpc.framework.foundation import stream
|
| 20 |
+
|
| 21 |
+
_NO_VALUE = object()
|
| 22 |
+
_LOGGER = logging.getLogger(__name__)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class TransformingConsumer(stream.Consumer):
|
| 26 |
+
"""A stream.Consumer that passes a transformation of its input to another."""
|
| 27 |
+
|
| 28 |
+
def __init__(self, transformation, downstream):
|
| 29 |
+
self._transformation = transformation
|
| 30 |
+
self._downstream = downstream
|
| 31 |
+
|
| 32 |
+
def consume(self, value):
|
| 33 |
+
self._downstream.consume(self._transformation(value))
|
| 34 |
+
|
| 35 |
+
def terminate(self):
|
| 36 |
+
self._downstream.terminate()
|
| 37 |
+
|
| 38 |
+
def consume_and_terminate(self, value):
|
| 39 |
+
self._downstream.consume_and_terminate(self._transformation(value))
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class IterableConsumer(stream.Consumer):
|
| 43 |
+
"""A Consumer that when iterated over emits the values it has consumed."""
|
| 44 |
+
|
| 45 |
+
def __init__(self):
|
| 46 |
+
self._condition = threading.Condition()
|
| 47 |
+
self._values = []
|
| 48 |
+
self._active = True
|
| 49 |
+
|
| 50 |
+
def consume(self, value):
|
| 51 |
+
with self._condition:
|
| 52 |
+
if self._active:
|
| 53 |
+
self._values.append(value)
|
| 54 |
+
self._condition.notify()
|
| 55 |
+
|
| 56 |
+
def terminate(self):
|
| 57 |
+
with self._condition:
|
| 58 |
+
self._active = False
|
| 59 |
+
self._condition.notify()
|
| 60 |
+
|
| 61 |
+
def consume_and_terminate(self, value):
|
| 62 |
+
with self._condition:
|
| 63 |
+
if self._active:
|
| 64 |
+
self._values.append(value)
|
| 65 |
+
self._active = False
|
| 66 |
+
self._condition.notify()
|
| 67 |
+
|
| 68 |
+
def __iter__(self):
|
| 69 |
+
return self
|
| 70 |
+
|
| 71 |
+
def __next__(self):
|
| 72 |
+
return self.next()
|
| 73 |
+
|
| 74 |
+
def next(self):
|
| 75 |
+
with self._condition:
|
| 76 |
+
while self._active and not self._values:
|
| 77 |
+
self._condition.wait()
|
| 78 |
+
if self._values:
|
| 79 |
+
return self._values.pop(0)
|
| 80 |
+
else:
|
| 81 |
+
raise StopIteration()
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class ThreadSwitchingConsumer(stream.Consumer):
|
| 85 |
+
"""A Consumer decorator that affords serialization and asynchrony."""
|
| 86 |
+
|
| 87 |
+
def __init__(self, sink, pool):
|
| 88 |
+
self._lock = threading.Lock()
|
| 89 |
+
self._sink = sink
|
| 90 |
+
self._pool = pool
|
| 91 |
+
# True if self._spin has been submitted to the pool to be called once and
|
| 92 |
+
# that call has not yet returned, False otherwise.
|
| 93 |
+
self._spinning = False
|
| 94 |
+
self._values = []
|
| 95 |
+
self._active = True
|
| 96 |
+
|
| 97 |
+
def _spin(self, sink, value, terminate):
|
| 98 |
+
while True:
|
| 99 |
+
try:
|
| 100 |
+
if value is _NO_VALUE:
|
| 101 |
+
sink.terminate()
|
| 102 |
+
elif terminate:
|
| 103 |
+
sink.consume_and_terminate(value)
|
| 104 |
+
else:
|
| 105 |
+
sink.consume(value)
|
| 106 |
+
except Exception as e: # pylint:disable=broad-except
|
| 107 |
+
_LOGGER.exception(e)
|
| 108 |
+
|
| 109 |
+
with self._lock:
|
| 110 |
+
if terminate:
|
| 111 |
+
self._spinning = False
|
| 112 |
+
return
|
| 113 |
+
elif self._values:
|
| 114 |
+
value = self._values.pop(0)
|
| 115 |
+
terminate = not self._values and not self._active
|
| 116 |
+
elif not self._active:
|
| 117 |
+
value = _NO_VALUE
|
| 118 |
+
terminate = True
|
| 119 |
+
else:
|
| 120 |
+
self._spinning = False
|
| 121 |
+
return
|
| 122 |
+
|
| 123 |
+
def consume(self, value):
|
| 124 |
+
with self._lock:
|
| 125 |
+
if self._active:
|
| 126 |
+
if self._spinning:
|
| 127 |
+
self._values.append(value)
|
| 128 |
+
else:
|
| 129 |
+
self._pool.submit(self._spin, self._sink, value, False)
|
| 130 |
+
self._spinning = True
|
| 131 |
+
|
| 132 |
+
def terminate(self):
|
| 133 |
+
with self._lock:
|
| 134 |
+
if self._active:
|
| 135 |
+
self._active = False
|
| 136 |
+
if not self._spinning:
|
| 137 |
+
self._pool.submit(self._spin, self._sink, _NO_VALUE, True)
|
| 138 |
+
self._spinning = True
|
| 139 |
+
|
| 140 |
+
def consume_and_terminate(self, value):
|
| 141 |
+
with self._lock:
|
| 142 |
+
if self._active:
|
| 143 |
+
self._active = False
|
| 144 |
+
if self._spinning:
|
| 145 |
+
self._values.append(value)
|
| 146 |
+
else:
|
| 147 |
+
self._pool.submit(self._spin, self._sink, value, True)
|
| 148 |
+
self._spinning = True
|