ZhengyangZhang commited on
Commit
c3ae1e2
·
verified ·
1 Parent(s): e5b91e2

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/grpc/__init__.py +2347 -0
  2. lib/python3.12/site-packages/grpc/_auth.py +80 -0
  3. lib/python3.12/site-packages/grpc/_channel.py +2240 -0
  4. lib/python3.12/site-packages/grpc/_common.py +181 -0
  5. lib/python3.12/site-packages/grpc/_compression.py +71 -0
  6. lib/python3.12/site-packages/grpc/_cython/__init__.py +13 -0
  7. lib/python3.12/site-packages/grpc/_cython/__pycache__/__init__.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/grpc/_cython/_credentials/roots.pem +0 -0
  9. lib/python3.12/site-packages/grpc/_cython/_cygrpc/__init__.py +13 -0
  10. lib/python3.12/site-packages/grpc/_cython/_cygrpc/__pycache__/__init__.cpython-312.pyc +0 -0
  11. lib/python3.12/site-packages/grpc/_grpcio_metadata.py +1 -0
  12. lib/python3.12/site-packages/grpc/_interceptor.py +809 -0
  13. lib/python3.12/site-packages/grpc/_observability.py +312 -0
  14. lib/python3.12/site-packages/grpc/_plugin_wrapping.py +137 -0
  15. lib/python3.12/site-packages/grpc/_runtime_protos.py +164 -0
  16. lib/python3.12/site-packages/grpc/_server.py +1516 -0
  17. lib/python3.12/site-packages/grpc/_simple_stubs.py +588 -0
  18. lib/python3.12/site-packages/grpc/_typing.py +96 -0
  19. lib/python3.12/site-packages/grpc/_utilities.py +220 -0
  20. lib/python3.12/site-packages/grpc/beta/__init__.py +13 -0
  21. lib/python3.12/site-packages/grpc/beta/__pycache__/_server_adaptations.cpython-312.pyc +0 -0
  22. lib/python3.12/site-packages/grpc/beta/__pycache__/interfaces.cpython-312.pyc +0 -0
  23. lib/python3.12/site-packages/grpc/beta/_client_adaptations.py +1010 -0
  24. lib/python3.12/site-packages/grpc/beta/_metadata.py +54 -0
  25. lib/python3.12/site-packages/grpc/beta/_server_adaptations.py +459 -0
  26. lib/python3.12/site-packages/grpc/beta/implementations.py +345 -0
  27. lib/python3.12/site-packages/grpc/beta/interfaces.py +163 -0
  28. lib/python3.12/site-packages/grpc/beta/utilities.py +149 -0
  29. lib/python3.12/site-packages/grpc/framework/__init__.py +13 -0
  30. lib/python3.12/site-packages/grpc/framework/__pycache__/__init__.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/grpc/framework/common/__init__.py +13 -0
  32. lib/python3.12/site-packages/grpc/framework/common/__pycache__/__init__.cpython-312.pyc +0 -0
  33. lib/python3.12/site-packages/grpc/framework/common/__pycache__/cardinality.cpython-312.pyc +0 -0
  34. lib/python3.12/site-packages/grpc/framework/common/__pycache__/style.cpython-312.pyc +0 -0
  35. lib/python3.12/site-packages/grpc/framework/common/cardinality.py +26 -0
  36. lib/python3.12/site-packages/grpc/framework/common/style.py +24 -0
  37. lib/python3.12/site-packages/grpc/framework/foundation/__init__.py +13 -0
  38. lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/__init__.cpython-312.pyc +0 -0
  39. lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/abandonment.cpython-312.pyc +0 -0
  40. lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/callable_util.cpython-312.pyc +0 -0
  41. lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/future.cpython-312.pyc +0 -0
  42. lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/logging_pool.cpython-312.pyc +0 -0
  43. lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/stream.cpython-312.pyc +0 -0
  44. lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/stream_util.cpython-312.pyc +0 -0
  45. lib/python3.12/site-packages/grpc/framework/foundation/abandonment.py +22 -0
  46. lib/python3.12/site-packages/grpc/framework/foundation/callable_util.py +98 -0
  47. lib/python3.12/site-packages/grpc/framework/foundation/future.py +219 -0
  48. lib/python3.12/site-packages/grpc/framework/foundation/logging_pool.py +72 -0
  49. lib/python3.12/site-packages/grpc/framework/foundation/stream.py +43 -0
  50. lib/python3.12/site-packages/grpc/framework/foundation/stream_util.py +147 -0
lib/python3.12/site-packages/grpc/__init__.py ADDED
@@ -0,0 +1,2347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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( # noqa: B027
1458
+ self, service_name, method_handlers
1459
+ ):
1460
+ """Registers GenericRpcHandlers with this Server.
1461
+
1462
+ This method is only safe to call before the server is started.
1463
+
1464
+ If the same method have both generic and registered handler,
1465
+ registered handler will take precedence.
1466
+
1467
+ Args:
1468
+ service_name: The service name.
1469
+ method_handlers: A dictionary that maps method names to corresponding
1470
+ RpcMethodHandler.
1471
+ """
1472
+
1473
+ @abc.abstractmethod
1474
+ def add_insecure_port(self, address):
1475
+ """Opens an insecure port for accepting RPCs.
1476
+
1477
+ This method may only be called before starting the server.
1478
+
1479
+ Args:
1480
+ address: The address for which to open a port. If the port is 0,
1481
+ or not specified in the address, then gRPC runtime will choose a port.
1482
+
1483
+ Returns:
1484
+ An integer port on which server will accept RPC requests.
1485
+ """
1486
+ raise NotImplementedError()
1487
+
1488
+ @abc.abstractmethod
1489
+ def add_secure_port(self, address, server_credentials):
1490
+ """Opens a secure port for accepting RPCs.
1491
+
1492
+ This method may only be called before starting the server.
1493
+
1494
+ Args:
1495
+ address: The address for which to open a port.
1496
+ if the port is 0, or not specified in the address, then gRPC
1497
+ runtime will choose a port.
1498
+ server_credentials: A ServerCredentials object.
1499
+
1500
+ Returns:
1501
+ An integer port on which server will accept RPC requests.
1502
+ """
1503
+ raise NotImplementedError()
1504
+
1505
+ @abc.abstractmethod
1506
+ def start(self):
1507
+ """Starts this Server.
1508
+
1509
+ This method may only be called once. (i.e. it is not idempotent).
1510
+ """
1511
+ raise NotImplementedError()
1512
+
1513
+ @abc.abstractmethod
1514
+ def stop(self, grace):
1515
+ """Stops this Server.
1516
+
1517
+ This method immediately stop service of new RPCs in all cases.
1518
+
1519
+ If a grace period is specified, this method waits until all active
1520
+ RPCs are finished or until the grace period is reached. RPCs that haven't
1521
+ been terminated within the grace period are aborted.
1522
+ If a grace period is not specified (by passing None for `grace`),
1523
+ all existing RPCs are aborted immediately and this method
1524
+ blocks until the last RPC handler terminates.
1525
+
1526
+ This method is idempotent and may be called at any time.
1527
+ Passing a smaller grace value in a subsequent call will have
1528
+ the effect of stopping the Server sooner (passing None will
1529
+ have the effect of stopping the server immediately). Passing
1530
+ a larger grace value in a subsequent call *will not* have the
1531
+ effect of stopping the server later (i.e. the most restrictive
1532
+ grace value is used).
1533
+
1534
+ Args:
1535
+ grace: A duration of time in seconds or None.
1536
+
1537
+ Returns:
1538
+ A threading.Event that will be set when this Server has completely
1539
+ stopped, i.e. when running RPCs either complete or are aborted and
1540
+ all handlers have terminated.
1541
+ """
1542
+ raise NotImplementedError()
1543
+
1544
+ def wait_for_termination(self, timeout=None):
1545
+ """Block current thread until the server stops.
1546
+
1547
+ This is an EXPERIMENTAL API.
1548
+
1549
+ The wait will not consume computational resources during blocking, and
1550
+ it will block until one of the two following conditions are met:
1551
+
1552
+ 1) The server is stopped or terminated;
1553
+ 2) A timeout occurs if timeout is not `None`.
1554
+
1555
+ The timeout argument works in the same way as `threading.Event.wait()`.
1556
+ https://docs.python.org/3/library/threading.html#threading.Event.wait
1557
+
1558
+ Args:
1559
+ timeout: A floating point number specifying a timeout for the
1560
+ operation in seconds.
1561
+
1562
+ Returns:
1563
+ A bool indicates if the operation times out.
1564
+ """
1565
+ raise NotImplementedError()
1566
+
1567
+
1568
+ ################################# Functions ################################
1569
+
1570
+
1571
+ def unary_unary_rpc_method_handler(
1572
+ behavior, request_deserializer=None, response_serializer=None
1573
+ ):
1574
+ """Creates an RpcMethodHandler for a unary-unary RPC method.
1575
+
1576
+ Args:
1577
+ behavior: The implementation of an RPC that accepts one request
1578
+ and returns one response.
1579
+ request_deserializer: An optional :term:`deserializer` for request deserialization.
1580
+ response_serializer: An optional :term:`serializer` for response serialization.
1581
+
1582
+ Returns:
1583
+ An RpcMethodHandler object that is typically used by grpc.Server.
1584
+ """
1585
+ from grpc import _utilities # pylint: disable=cyclic-import
1586
+
1587
+ return _utilities.RpcMethodHandler(
1588
+ False,
1589
+ False,
1590
+ request_deserializer,
1591
+ response_serializer,
1592
+ behavior,
1593
+ None,
1594
+ None,
1595
+ None,
1596
+ )
1597
+
1598
+
1599
+ def unary_stream_rpc_method_handler(
1600
+ behavior, request_deserializer=None, response_serializer=None
1601
+ ):
1602
+ """Creates an RpcMethodHandler for a unary-stream RPC method.
1603
+
1604
+ Args:
1605
+ behavior: The implementation of an RPC that accepts one request
1606
+ and returns an iterator of response values.
1607
+ request_deserializer: An optional :term:`deserializer` for request deserialization.
1608
+ response_serializer: An optional :term:`serializer` for response serialization.
1609
+
1610
+ Returns:
1611
+ An RpcMethodHandler object that is typically used by grpc.Server.
1612
+ """
1613
+ from grpc import _utilities # pylint: disable=cyclic-import
1614
+
1615
+ return _utilities.RpcMethodHandler(
1616
+ False,
1617
+ True,
1618
+ request_deserializer,
1619
+ response_serializer,
1620
+ None,
1621
+ behavior,
1622
+ None,
1623
+ None,
1624
+ )
1625
+
1626
+
1627
+ def stream_unary_rpc_method_handler(
1628
+ behavior, request_deserializer=None, response_serializer=None
1629
+ ):
1630
+ """Creates an RpcMethodHandler for a stream-unary RPC method.
1631
+
1632
+ Args:
1633
+ behavior: The implementation of an RPC that accepts an iterator of
1634
+ request values and returns a single response value.
1635
+ request_deserializer: An optional :term:`deserializer` for request deserialization.
1636
+ response_serializer: An optional :term:`serializer` for response serialization.
1637
+
1638
+ Returns:
1639
+ An RpcMethodHandler object that is typically used by grpc.Server.
1640
+ """
1641
+ from grpc import _utilities # pylint: disable=cyclic-import
1642
+
1643
+ return _utilities.RpcMethodHandler(
1644
+ True,
1645
+ False,
1646
+ request_deserializer,
1647
+ response_serializer,
1648
+ None,
1649
+ None,
1650
+ behavior,
1651
+ None,
1652
+ )
1653
+
1654
+
1655
+ def stream_stream_rpc_method_handler(
1656
+ behavior, request_deserializer=None, response_serializer=None
1657
+ ):
1658
+ """Creates an RpcMethodHandler for a stream-stream RPC method.
1659
+
1660
+ Args:
1661
+ behavior: The implementation of an RPC that accepts an iterator of
1662
+ request values and returns an iterator of response values.
1663
+ request_deserializer: An optional :term:`deserializer` for request deserialization.
1664
+ response_serializer: An optional :term:`serializer` for response serialization.
1665
+
1666
+ Returns:
1667
+ An RpcMethodHandler object that is typically used by grpc.Server.
1668
+ """
1669
+ from grpc import _utilities # pylint: disable=cyclic-import
1670
+
1671
+ return _utilities.RpcMethodHandler(
1672
+ True,
1673
+ True,
1674
+ request_deserializer,
1675
+ response_serializer,
1676
+ None,
1677
+ None,
1678
+ None,
1679
+ behavior,
1680
+ )
1681
+
1682
+
1683
+ def method_handlers_generic_handler(service, method_handlers):
1684
+ """Creates a GenericRpcHandler from RpcMethodHandlers.
1685
+
1686
+ Args:
1687
+ service: The name of the service that is implemented by the
1688
+ method_handlers.
1689
+ method_handlers: A dictionary that maps method names to corresponding
1690
+ RpcMethodHandler.
1691
+
1692
+ Returns:
1693
+ A GenericRpcHandler. This is typically added to the grpc.Server object
1694
+ with add_generic_rpc_handlers() before starting the server.
1695
+ """
1696
+ from grpc import _utilities # pylint: disable=cyclic-import
1697
+
1698
+ return _utilities.DictionaryGenericHandler(service, method_handlers)
1699
+
1700
+
1701
+ def ssl_channel_credentials(
1702
+ root_certificates=None, private_key=None, certificate_chain=None
1703
+ ):
1704
+ """Creates a ChannelCredentials for use with an SSL-enabled Channel.
1705
+
1706
+ Args:
1707
+ root_certificates: The PEM-encoded root certificates as a byte string,
1708
+ or None to retrieve them from a default location chosen by gRPC
1709
+ runtime.
1710
+ private_key: The PEM-encoded private key as a byte string, or None if no
1711
+ private key should be used.
1712
+ certificate_chain: The PEM-encoded certificate chain as a byte string
1713
+ to use or None if no certificate chain should be used.
1714
+
1715
+ Returns:
1716
+ A ChannelCredentials for use with an SSL-enabled Channel.
1717
+ """
1718
+ return ChannelCredentials(
1719
+ _cygrpc.SSLChannelCredentials(
1720
+ root_certificates, private_key, certificate_chain
1721
+ )
1722
+ )
1723
+
1724
+
1725
+ def xds_channel_credentials(fallback_credentials=None):
1726
+ """Creates a ChannelCredentials for use with xDS. This is an EXPERIMENTAL
1727
+ API.
1728
+
1729
+ Args:
1730
+ fallback_credentials: Credentials to use in case it is not possible to
1731
+ establish a secure connection via xDS. If no fallback_credentials
1732
+ argument is supplied, a default SSLChannelCredentials is used.
1733
+ """
1734
+ fallback_credentials = (
1735
+ ssl_channel_credentials()
1736
+ if fallback_credentials is None
1737
+ else fallback_credentials
1738
+ )
1739
+ return ChannelCredentials(
1740
+ _cygrpc.XDSChannelCredentials(fallback_credentials._credentials)
1741
+ )
1742
+
1743
+
1744
+ def metadata_call_credentials(metadata_plugin, name=None):
1745
+ """Construct CallCredentials from an AuthMetadataPlugin.
1746
+
1747
+ Args:
1748
+ metadata_plugin: An AuthMetadataPlugin to use for authentication.
1749
+ name: An optional name for the plugin.
1750
+
1751
+ Returns:
1752
+ A CallCredentials.
1753
+ """
1754
+ from grpc import _plugin_wrapping # pylint: disable=cyclic-import
1755
+
1756
+ return _plugin_wrapping.metadata_plugin_call_credentials(
1757
+ metadata_plugin, name
1758
+ )
1759
+
1760
+
1761
+ def access_token_call_credentials(access_token):
1762
+ """Construct CallCredentials from an access token.
1763
+
1764
+ Args:
1765
+ access_token: A string to place directly in the http request
1766
+ authorization header, for example
1767
+ "authorization: Bearer <access_token>".
1768
+
1769
+ Returns:
1770
+ A CallCredentials.
1771
+ """
1772
+ from grpc import _auth # pylint: disable=cyclic-import
1773
+ from grpc import _plugin_wrapping # pylint: disable=cyclic-import
1774
+
1775
+ return _plugin_wrapping.metadata_plugin_call_credentials(
1776
+ _auth.AccessTokenAuthMetadataPlugin(access_token), None
1777
+ )
1778
+
1779
+
1780
+ def composite_call_credentials(*call_credentials):
1781
+ """Compose multiple CallCredentials to make a new CallCredentials.
1782
+
1783
+ Args:
1784
+ *call_credentials: At least two CallCredentials objects.
1785
+
1786
+ Returns:
1787
+ A CallCredentials object composed of the given CallCredentials objects.
1788
+ """
1789
+ return CallCredentials(
1790
+ _cygrpc.CompositeCallCredentials(
1791
+ tuple(
1792
+ single_call_credentials._credentials
1793
+ for single_call_credentials in call_credentials
1794
+ )
1795
+ )
1796
+ )
1797
+
1798
+
1799
+ def composite_channel_credentials(channel_credentials, *call_credentials):
1800
+ """Compose a ChannelCredentials and one or more CallCredentials objects.
1801
+
1802
+ Args:
1803
+ channel_credentials: A ChannelCredentials object.
1804
+ *call_credentials: One or more CallCredentials objects.
1805
+
1806
+ Returns:
1807
+ A ChannelCredentials composed of the given ChannelCredentials and
1808
+ CallCredentials objects.
1809
+ """
1810
+ return ChannelCredentials(
1811
+ _cygrpc.CompositeChannelCredentials(
1812
+ tuple(
1813
+ single_call_credentials._credentials
1814
+ for single_call_credentials in call_credentials
1815
+ ),
1816
+ channel_credentials._credentials,
1817
+ )
1818
+ )
1819
+
1820
+
1821
+ def ssl_server_credentials(
1822
+ private_key_certificate_chain_pairs,
1823
+ root_certificates=None,
1824
+ require_client_auth=False,
1825
+ ):
1826
+ """Creates a ServerCredentials for use with an SSL-enabled Server.
1827
+
1828
+ Args:
1829
+ private_key_certificate_chain_pairs: A list of pairs of the form
1830
+ [PEM-encoded private key, PEM-encoded certificate chain].
1831
+ root_certificates: An optional byte string of PEM-encoded client root
1832
+ certificates that the server will use to verify client authentication.
1833
+ If omitted, require_client_auth must also be False.
1834
+ require_client_auth: A boolean indicating whether or not to require
1835
+ clients to be authenticated. May only be True if root_certificates
1836
+ is not None.
1837
+
1838
+ Returns:
1839
+ A ServerCredentials for use with an SSL-enabled Server. Typically, this
1840
+ object is an argument to add_secure_port() method during server setup.
1841
+ """
1842
+ if not private_key_certificate_chain_pairs:
1843
+ error_msg = (
1844
+ "At least one private key-certificate chain pair is required!"
1845
+ )
1846
+ raise ValueError(error_msg)
1847
+ if require_client_auth and root_certificates is None:
1848
+ error_msg = "Illegal to require client auth without providing root certificates!"
1849
+ raise ValueError(error_msg)
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
+ error_msg = "At least one private key-certificate chain pair is required!"
1913
+ raise ValueError(error_msg)
1914
+
1915
+
1916
+ def dynamic_ssl_server_credentials(
1917
+ initial_certificate_configuration,
1918
+ certificate_configuration_fetcher,
1919
+ require_client_authentication=False,
1920
+ ):
1921
+ """Creates a ServerCredentials for use with an SSL-enabled Server.
1922
+
1923
+ Args:
1924
+ initial_certificate_configuration (ServerCertificateConfiguration): The
1925
+ certificate configuration with which the server will be initialized.
1926
+ certificate_configuration_fetcher (callable): A callable that takes no
1927
+ arguments and should return a ServerCertificateConfiguration to
1928
+ replace the server's current certificate, or None for no change
1929
+ (i.e., the server will continue its current certificate
1930
+ config). The library will call this callback on *every* new
1931
+ client connection before starting the TLS handshake with the
1932
+ client, thus allowing the user application to optionally
1933
+ return a new ServerCertificateConfiguration that the server will then
1934
+ use for the handshake.
1935
+ require_client_authentication: A boolean indicating whether or not to
1936
+ require clients to be authenticated.
1937
+
1938
+ Returns:
1939
+ A ServerCredentials.
1940
+ """
1941
+ return ServerCredentials(
1942
+ _cygrpc.server_credentials_ssl_dynamic_cert_config(
1943
+ initial_certificate_configuration,
1944
+ certificate_configuration_fetcher,
1945
+ require_client_authentication,
1946
+ )
1947
+ )
1948
+
1949
+
1950
+ @enum.unique
1951
+ class LocalConnectionType(enum.Enum):
1952
+ """Types of local connection for local credential creation.
1953
+
1954
+ Attributes:
1955
+ UDS: Unix domain socket connections
1956
+ LOCAL_TCP: Local TCP connections.
1957
+ """
1958
+
1959
+ UDS = _cygrpc.LocalConnectionType.uds
1960
+ LOCAL_TCP = _cygrpc.LocalConnectionType.local_tcp
1961
+
1962
+
1963
+ def local_channel_credentials(local_connect_type=LocalConnectionType.LOCAL_TCP):
1964
+ """Creates a local ChannelCredentials used for local connections.
1965
+
1966
+ This is an EXPERIMENTAL API.
1967
+
1968
+ Local credentials are used by local TCP endpoints (e.g. localhost:10000)
1969
+ also UDS connections.
1970
+
1971
+ The connections created by local channel credentials are not
1972
+ encrypted, but will be checked if they are local or not.
1973
+ The UDS connections are considered secure by providing peer authentication
1974
+ and data confidentiality while TCP connections are considered insecure.
1975
+
1976
+ It is allowed to transmit call credentials over connections created by
1977
+ local channel credentials.
1978
+
1979
+ Local channel credentials are useful for 1) eliminating insecure_channel usage;
1980
+ 2) enable unit testing for call credentials without setting up secrets.
1981
+
1982
+ Args:
1983
+ local_connect_type: Local connection type (either
1984
+ grpc.LocalConnectionType.UDS or grpc.LocalConnectionType.LOCAL_TCP)
1985
+
1986
+ Returns:
1987
+ A ChannelCredentials for use with a local Channel
1988
+ """
1989
+ return ChannelCredentials(
1990
+ _cygrpc.channel_credentials_local(local_connect_type.value)
1991
+ )
1992
+
1993
+
1994
+ def local_server_credentials(local_connect_type=LocalConnectionType.LOCAL_TCP):
1995
+ """Creates a local ServerCredentials used for local connections.
1996
+
1997
+ This is an EXPERIMENTAL API.
1998
+
1999
+ Local credentials are used by local TCP endpoints (e.g. localhost:10000)
2000
+ also UDS connections.
2001
+
2002
+ The connections created by local server credentials are not
2003
+ encrypted, but will be checked if they are local or not.
2004
+ The UDS connections are considered secure by providing peer authentication
2005
+ and data confidentiality while TCP connections are considered insecure.
2006
+
2007
+ It is allowed to transmit call credentials over connections created by local
2008
+ server credentials.
2009
+
2010
+ Local server credentials are useful for 1) eliminating insecure_channel usage;
2011
+ 2) enable unit testing for call credentials without setting up secrets.
2012
+
2013
+ Args:
2014
+ local_connect_type: Local connection type (either
2015
+ grpc.LocalConnectionType.UDS or grpc.LocalConnectionType.LOCAL_TCP)
2016
+
2017
+ Returns:
2018
+ A ServerCredentials for use with a local Server
2019
+ """
2020
+ return ServerCredentials(
2021
+ _cygrpc.server_credentials_local(local_connect_type.value)
2022
+ )
2023
+
2024
+
2025
+ def alts_channel_credentials(service_accounts=None):
2026
+ """Creates a ChannelCredentials for use with an ALTS-enabled Channel.
2027
+
2028
+ This is an EXPERIMENTAL API.
2029
+ ALTS credentials API can only be used in GCP environment as it relies on
2030
+ handshaker service being available. For more info about ALTS see
2031
+ https://cloud.google.com/security/encryption-in-transit/application-layer-transport-security
2032
+
2033
+ Args:
2034
+ service_accounts: A list of server identities accepted by the client.
2035
+ If target service accounts are provided and none of them matches the
2036
+ peer identity of the server, handshake will fail. The arg can be empty
2037
+ if the client does not have any information about trusted server
2038
+ identity.
2039
+
2040
+ Returns:
2041
+ A ChannelCredentials for use with an ALTS-enabled Channel
2042
+ """
2043
+ return ChannelCredentials(
2044
+ _cygrpc.channel_credentials_alts(service_accounts or [])
2045
+ )
2046
+
2047
+
2048
+ def alts_server_credentials():
2049
+ """Creates a ServerCredentials for use with an ALTS-enabled connection.
2050
+
2051
+ This is an EXPERIMENTAL API.
2052
+ ALTS credentials API can only be used in GCP environment as it relies on
2053
+ handshaker service being available. For more info about ALTS see
2054
+ https://cloud.google.com/security/encryption-in-transit/application-layer-transport-security
2055
+
2056
+ Returns:
2057
+ A ServerCredentials for use with an ALTS-enabled Server
2058
+ """
2059
+ return ServerCredentials(_cygrpc.server_credentials_alts())
2060
+
2061
+
2062
+ def compute_engine_channel_credentials(call_credentials):
2063
+ """Creates a compute engine channel credential.
2064
+
2065
+ This credential can only be used in a GCP environment as it relies on
2066
+ a handshaker service. For more info about ALTS, see
2067
+ https://cloud.google.com/security/encryption-in-transit/application-layer-transport-security
2068
+
2069
+ This channel credential is expected to be used as part of a composite
2070
+ credential in conjunction with a call credentials that authenticates the
2071
+ VM's default service account. If used with any other sort of call
2072
+ credential, the connection may suddenly and unexpectedly begin failing RPCs.
2073
+ """
2074
+ return ChannelCredentials(
2075
+ _cygrpc.channel_credentials_compute_engine(
2076
+ call_credentials._credentials
2077
+ )
2078
+ )
2079
+
2080
+
2081
+ def channel_ready_future(channel):
2082
+ """Creates a Future that tracks when a Channel is ready.
2083
+
2084
+ Cancelling the Future does not affect the channel's state machine.
2085
+ It merely decouples the Future from channel state machine.
2086
+
2087
+ Args:
2088
+ channel: A Channel object.
2089
+
2090
+ Returns:
2091
+ A Future object that matures when the channel connectivity is
2092
+ ChannelConnectivity.READY.
2093
+ """
2094
+ from grpc import _utilities # pylint: disable=cyclic-import
2095
+
2096
+ return _utilities.channel_ready_future(channel)
2097
+
2098
+
2099
+ def insecure_channel(target, options=None, compression=None):
2100
+ """Creates an insecure Channel to a server.
2101
+
2102
+ The returned Channel is thread-safe.
2103
+
2104
+ Args:
2105
+ target: The server address
2106
+ options: An optional list of key-value pairs (:term:`channel_arguments`
2107
+ in gRPC Core runtime) to configure the channel.
2108
+ compression: An optional value indicating the compression method to be
2109
+ used over the lifetime of the channel.
2110
+
2111
+ Returns:
2112
+ A Channel.
2113
+ """
2114
+ from grpc import _channel # pylint: disable=cyclic-import
2115
+
2116
+ return _channel.Channel(
2117
+ target, () if options is None else options, None, compression
2118
+ )
2119
+
2120
+
2121
+ def secure_channel(target, credentials, options=None, compression=None):
2122
+ """Creates a secure Channel to a server.
2123
+
2124
+ The returned Channel is thread-safe.
2125
+
2126
+ Args:
2127
+ target: The server address.
2128
+ credentials: A ChannelCredentials instance.
2129
+ options: An optional list of key-value pairs (:term:`channel_arguments`
2130
+ in gRPC Core runtime) to configure the channel.
2131
+ compression: An optional value indicating the compression method to be
2132
+ used over the lifetime of the channel.
2133
+
2134
+ Returns:
2135
+ A Channel.
2136
+ """
2137
+ from grpc import _channel # pylint: disable=cyclic-import
2138
+ from grpc.experimental import _insecure_channel_credentials
2139
+
2140
+ if credentials._credentials is _insecure_channel_credentials:
2141
+ raise ValueError(
2142
+ "secure_channel cannot be called with insecure credentials."
2143
+ + " Call insecure_channel instead."
2144
+ )
2145
+ return _channel.Channel(
2146
+ target,
2147
+ () if options is None else options,
2148
+ credentials._credentials,
2149
+ compression,
2150
+ )
2151
+
2152
+
2153
+ def intercept_channel(channel, *interceptors):
2154
+ """Intercepts a channel through a set of interceptors.
2155
+
2156
+ Args:
2157
+ channel: A Channel.
2158
+ interceptors: Zero or more objects of type
2159
+ UnaryUnaryClientInterceptor,
2160
+ UnaryStreamClientInterceptor,
2161
+ StreamUnaryClientInterceptor, or
2162
+ StreamStreamClientInterceptor.
2163
+ Interceptors are given control in the order they are listed.
2164
+
2165
+ Returns:
2166
+ A Channel that intercepts each invocation via the provided interceptors.
2167
+
2168
+ Raises:
2169
+ TypeError: If interceptor does not derive from any of
2170
+ UnaryUnaryClientInterceptor,
2171
+ UnaryStreamClientInterceptor,
2172
+ StreamUnaryClientInterceptor, or
2173
+ StreamStreamClientInterceptor.
2174
+ """
2175
+ from grpc import _interceptor # pylint: disable=cyclic-import
2176
+
2177
+ return _interceptor.intercept_channel(channel, *interceptors)
2178
+
2179
+
2180
+ def server(
2181
+ thread_pool,
2182
+ handlers=None,
2183
+ interceptors=None,
2184
+ options=None,
2185
+ maximum_concurrent_rpcs=None,
2186
+ compression=None,
2187
+ xds=False,
2188
+ ):
2189
+ """Creates a Server with which RPCs can be serviced.
2190
+
2191
+ Args:
2192
+ thread_pool: A futures.ThreadPoolExecutor to be used by the Server
2193
+ to execute RPC handlers.
2194
+ handlers: An optional list of GenericRpcHandlers used for executing RPCs.
2195
+ More handlers may be added by calling add_generic_rpc_handlers any time
2196
+ before the server is started.
2197
+ interceptors: An optional list of ServerInterceptor objects that observe
2198
+ and optionally manipulate the incoming RPCs before handing them over to
2199
+ handlers. The interceptors are given control in the order they are
2200
+ specified. This is an EXPERIMENTAL API.
2201
+ options: An optional list of key-value pairs (:term:`channel_arguments` in gRPC runtime)
2202
+ to configure the channel.
2203
+ maximum_concurrent_rpcs: The maximum number of concurrent RPCs this server
2204
+ will service before returning RESOURCE_EXHAUSTED status, or None to
2205
+ indicate no limit.
2206
+ compression: An element of grpc.compression, e.g.
2207
+ grpc.compression.Gzip. This compression algorithm will be used for the
2208
+ lifetime of the server unless overridden.
2209
+ xds: If set to true, retrieves server configuration via xDS. This is an
2210
+ EXPERIMENTAL option.
2211
+
2212
+ Returns:
2213
+ A Server object.
2214
+ """
2215
+ from grpc import _server # pylint: disable=cyclic-import
2216
+
2217
+ return _server.create_server(
2218
+ thread_pool,
2219
+ () if handlers is None else handlers,
2220
+ () if interceptors is None else interceptors,
2221
+ () if options is None else options,
2222
+ maximum_concurrent_rpcs,
2223
+ compression,
2224
+ xds,
2225
+ )
2226
+
2227
+
2228
+ @contextlib.contextmanager
2229
+ def _create_servicer_context(rpc_event, state, request_deserializer):
2230
+ from grpc import _server # pylint: disable=cyclic-import
2231
+
2232
+ context = _server._Context(rpc_event, state, request_deserializer)
2233
+ yield context
2234
+ context._finalize_state() # pylint: disable=protected-access
2235
+
2236
+
2237
+ @enum.unique
2238
+ class Compression(enum.IntEnum):
2239
+ """Indicates the compression method to be used for an RPC.
2240
+
2241
+ Attributes:
2242
+ NoCompression: Do not use compression algorithm.
2243
+ Deflate: Use "Deflate" compression algorithm.
2244
+ Gzip: Use "Gzip" compression algorithm.
2245
+ """
2246
+
2247
+ NoCompression = _compression.NoCompression
2248
+ Deflate = _compression.Deflate
2249
+ Gzip = _compression.Gzip
2250
+
2251
+
2252
+ ################################### __all__ #################################
2253
+
2254
+ __all__ = (
2255
+ "FutureTimeoutError",
2256
+ "FutureCancelledError",
2257
+ "Future",
2258
+ "ChannelConnectivity",
2259
+ "StatusCode",
2260
+ "Status",
2261
+ "RpcError",
2262
+ "RpcContext",
2263
+ "Call",
2264
+ "ChannelCredentials",
2265
+ "CallCredentials",
2266
+ "AuthMetadataContext",
2267
+ "AuthMetadataPluginCallback",
2268
+ "AuthMetadataPlugin",
2269
+ "Compression",
2270
+ "ClientCallDetails",
2271
+ "ServerCertificateConfiguration",
2272
+ "ServerCredentials",
2273
+ "LocalConnectionType",
2274
+ "UnaryUnaryMultiCallable",
2275
+ "UnaryStreamMultiCallable",
2276
+ "StreamUnaryMultiCallable",
2277
+ "StreamStreamMultiCallable",
2278
+ "UnaryUnaryClientInterceptor",
2279
+ "UnaryStreamClientInterceptor",
2280
+ "StreamUnaryClientInterceptor",
2281
+ "StreamStreamClientInterceptor",
2282
+ "Channel",
2283
+ "ServicerContext",
2284
+ "RpcMethodHandler",
2285
+ "HandlerCallDetails",
2286
+ "GenericRpcHandler",
2287
+ "ServiceRpcHandler",
2288
+ "Server",
2289
+ "ServerInterceptor",
2290
+ "unary_unary_rpc_method_handler",
2291
+ "unary_stream_rpc_method_handler",
2292
+ "stream_unary_rpc_method_handler",
2293
+ "stream_stream_rpc_method_handler",
2294
+ "method_handlers_generic_handler",
2295
+ "ssl_channel_credentials",
2296
+ "metadata_call_credentials",
2297
+ "access_token_call_credentials",
2298
+ "composite_call_credentials",
2299
+ "composite_channel_credentials",
2300
+ "compute_engine_channel_credentials",
2301
+ "local_channel_credentials",
2302
+ "local_server_credentials",
2303
+ "alts_channel_credentials",
2304
+ "alts_server_credentials",
2305
+ "ssl_server_credentials",
2306
+ "ssl_server_certificate_configuration",
2307
+ "dynamic_ssl_server_credentials",
2308
+ "channel_ready_future",
2309
+ "insecure_channel",
2310
+ "secure_channel",
2311
+ "intercept_channel",
2312
+ "server",
2313
+ "protos",
2314
+ "services",
2315
+ "protos_and_services",
2316
+ "xds_channel_credentials",
2317
+ "xds_server_credentials",
2318
+ "insecure_server_credentials",
2319
+ )
2320
+
2321
+ ############################### Extension Shims ################################
2322
+
2323
+ # Here to maintain backwards compatibility; avoid using these in new code!
2324
+ try:
2325
+ import grpc_tools
2326
+
2327
+ sys.modules.update({"grpc.tools": grpc_tools})
2328
+ except ImportError:
2329
+ pass
2330
+ try:
2331
+ import grpc_health
2332
+
2333
+ sys.modules.update({"grpc.health": grpc_health})
2334
+ except ImportError:
2335
+ pass
2336
+ try:
2337
+ import grpc_reflection
2338
+
2339
+ sys.modules.update({"grpc.reflection": grpc_reflection})
2340
+ except ImportError:
2341
+ pass
2342
+
2343
+ # Prevents import order issue in the case of renamed path.
2344
+ if sys.version_info >= (3, 6) and __name__ == "grpc":
2345
+ from grpc import aio # pylint: disable=ungrouped-imports
2346
+
2347
+ sys.modules.update({"grpc.aio": aio})
lib/python3.12/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 # noqa: FIX004
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)
lib/python3.12/site-packages/grpc/_channel.py ADDED
@@ -0,0 +1,2240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ _LOGGER.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
+ state.due.add(cygrpc.OperationType.send_message)
306
+ operations = (
307
+ cygrpc.SendMessageOperation(
308
+ serialized_request, _EMPTY_FLAGS
309
+ ),
310
+ )
311
+ operating = call.operate(operations, event_handler)
312
+ if not operating:
313
+ state.due.remove(cygrpc.OperationType.send_message)
314
+ return
315
+
316
+ def _done():
317
+ return (
318
+ state.code is not None
319
+ or cygrpc.OperationType.send_message
320
+ not in state.due
321
+ )
322
+
323
+ _common.wait(
324
+ state.condition.wait,
325
+ _done,
326
+ spin_cb=functools.partial(
327
+ cygrpc.block_if_fork_in_progress, state
328
+ ),
329
+ )
330
+ if state.code is not None:
331
+ return
332
+ else:
333
+ return
334
+ with state.condition:
335
+ if state.code is None:
336
+ state.due.add(cygrpc.OperationType.send_close_from_client)
337
+ operations = (
338
+ cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
339
+ )
340
+ operating = call.operate(operations, event_handler)
341
+ if not operating:
342
+ state.due.remove(
343
+ cygrpc.OperationType.send_close_from_client
344
+ )
345
+
346
+ consumption_thread = cygrpc.ForkManagedThread(
347
+ target=consume_request_iterator
348
+ )
349
+ consumption_thread.setDaemon(True)
350
+ consumption_thread.start()
351
+
352
+
353
+ def _rpc_state_string(class_name: str, rpc_state: _RPCState) -> str:
354
+ """Calculates error string for RPC."""
355
+ with rpc_state.condition:
356
+ if rpc_state.code is None:
357
+ return "<{} object>".format(class_name)
358
+ if rpc_state.code is grpc.StatusCode.OK:
359
+ return _OK_RENDEZVOUS_REPR_FORMAT.format(
360
+ class_name, rpc_state.code, rpc_state.details
361
+ )
362
+ return _NON_OK_RENDEZVOUS_REPR_FORMAT.format(
363
+ class_name,
364
+ rpc_state.code,
365
+ rpc_state.details,
366
+ rpc_state.debug_error_string,
367
+ )
368
+
369
+
370
+ class _InactiveRpcError(grpc.RpcError, grpc.Call, grpc.Future):
371
+ """An RPC error not tied to the execution of a particular RPC.
372
+
373
+ The RPC represented by the state object must not be in-progress or
374
+ cancelled.
375
+
376
+ Attributes:
377
+ _state: An instance of _RPCState.
378
+ """
379
+
380
+ _state: _RPCState
381
+
382
+ def __init__(self, state: _RPCState):
383
+ with state.condition:
384
+ self._state = _RPCState(
385
+ (),
386
+ copy.deepcopy(state.initial_metadata),
387
+ copy.deepcopy(state.trailing_metadata),
388
+ state.code,
389
+ copy.deepcopy(state.details),
390
+ )
391
+ self._state.response = copy.copy(state.response)
392
+ self._state.debug_error_string = copy.copy(state.debug_error_string)
393
+
394
+ def initial_metadata(self) -> Optional[MetadataType]:
395
+ return self._state.initial_metadata
396
+
397
+ def trailing_metadata(self) -> Optional[MetadataType]:
398
+ return self._state.trailing_metadata
399
+
400
+ def code(self) -> Optional[grpc.StatusCode]:
401
+ return self._state.code
402
+
403
+ def details(self) -> Optional[str]:
404
+ return _common.decode(self._state.details)
405
+
406
+ def debug_error_string(self) -> Optional[str]:
407
+ return _common.decode(self._state.debug_error_string)
408
+
409
+ def _repr(self) -> str:
410
+ return _rpc_state_string(self.__class__.__name__, self._state)
411
+
412
+ def __repr__(self) -> str:
413
+ return self._repr()
414
+
415
+ def __str__(self) -> str:
416
+ return self._repr()
417
+
418
+ def cancel(self) -> bool:
419
+ """See grpc.Future.cancel."""
420
+ return False
421
+
422
+ def cancelled(self) -> bool:
423
+ """See grpc.Future.cancelled."""
424
+ return False
425
+
426
+ def running(self) -> bool:
427
+ """See grpc.Future.running."""
428
+ return False
429
+
430
+ def done(self) -> bool:
431
+ """See grpc.Future.done."""
432
+ return True
433
+
434
+ def result(
435
+ self, timeout: Optional[float] = None
436
+ ) -> Any: # pylint: disable=unused-argument
437
+ """See grpc.Future.result."""
438
+ raise self
439
+
440
+ def exception(
441
+ self, timeout: Optional[float] = None # pylint: disable=unused-argument
442
+ ) -> Optional[Exception]:
443
+ """See grpc.Future.exception."""
444
+ return self
445
+
446
+ def traceback(
447
+ self, timeout: Optional[float] = None # pylint: disable=unused-argument
448
+ ) -> Optional[types.TracebackType]:
449
+ """See grpc.Future.traceback."""
450
+ try:
451
+ raise self
452
+ except grpc.RpcError:
453
+ return sys.exc_info()[2]
454
+
455
+ def add_done_callback(
456
+ self,
457
+ fn: Callable[[grpc.Future], None],
458
+ timeout: Optional[float] = None, # pylint: disable=unused-argument
459
+ ) -> None:
460
+ """See grpc.Future.add_done_callback."""
461
+ fn(self)
462
+
463
+
464
+ class _Rendezvous(grpc.RpcError, grpc.RpcContext):
465
+ """An RPC iterator.
466
+
467
+ Attributes:
468
+ _state: An instance of _RPCState.
469
+ _call: An instance of SegregatedCall or IntegratedCall.
470
+ In either case, the _call object is expected to have operate, cancel,
471
+ and next_event methods.
472
+ _response_deserializer: A callable taking bytes and return a Python
473
+ object.
474
+ _deadline: A float representing the deadline of the RPC in seconds. Or
475
+ possibly None, to represent an RPC with no deadline at all.
476
+ """
477
+
478
+ _state: _RPCState
479
+ _call: Union[cygrpc.SegregatedCall, cygrpc.IntegratedCall]
480
+ _response_deserializer: Optional[DeserializingFunction]
481
+ _deadline: Optional[float]
482
+
483
+ def __init__(
484
+ self,
485
+ state: _RPCState,
486
+ call: Union[cygrpc.SegregatedCall, cygrpc.IntegratedCall],
487
+ response_deserializer: Optional[DeserializingFunction],
488
+ deadline: Optional[float],
489
+ ):
490
+ super(_Rendezvous, self).__init__()
491
+ self._state = state
492
+ self._call = call
493
+ self._response_deserializer = response_deserializer
494
+ self._deadline = deadline
495
+
496
+ def is_active(self) -> bool:
497
+ """See grpc.RpcContext.is_active"""
498
+ with self._state.condition:
499
+ return self._state.code is None
500
+
501
+ def time_remaining(self) -> Optional[float]:
502
+ """See grpc.RpcContext.time_remaining"""
503
+ with self._state.condition:
504
+ if self._deadline is None:
505
+ return None
506
+ return max(self._deadline - time.time(), 0)
507
+
508
+ def cancel(self) -> bool:
509
+ """See grpc.RpcContext.cancel"""
510
+ with self._state.condition:
511
+ if self._state.code is None:
512
+ code = grpc.StatusCode.CANCELLED
513
+ details = "Locally cancelled by application!"
514
+ self._call.cancel(
515
+ _common.STATUS_CODE_TO_CYGRPC_STATUS_CODE[code], details
516
+ )
517
+ self._state.cancelled = True
518
+ _abort(self._state, code, details)
519
+ self._state.condition.notify_all()
520
+ return True
521
+ return False
522
+
523
+ def add_callback(self, callback: NullaryCallbackType) -> bool:
524
+ """See grpc.RpcContext.add_callback"""
525
+ with self._state.condition:
526
+ if self._state.callbacks is None:
527
+ return False
528
+ self._state.callbacks.append(callback)
529
+ return True
530
+
531
+ def __iter__(self):
532
+ return self
533
+
534
+ def next(self):
535
+ return self._next()
536
+
537
+ def __next__(self):
538
+ return self._next()
539
+
540
+ def _next(self):
541
+ raise NotImplementedError()
542
+
543
+ def debug_error_string(self) -> Optional[str]:
544
+ raise NotImplementedError()
545
+
546
+ def _repr(self) -> str:
547
+ return _rpc_state_string(self.__class__.__name__, self._state)
548
+
549
+ def __repr__(self) -> str:
550
+ return self._repr()
551
+
552
+ def __str__(self) -> str:
553
+ return self._repr()
554
+
555
+ def __del__(self) -> None:
556
+ with self._state.condition:
557
+ if self._state.code is None:
558
+ self._state.code = grpc.StatusCode.CANCELLED
559
+ self._state.details = "Cancelled upon garbage collection!"
560
+ self._state.cancelled = True
561
+ self._call.cancel(
562
+ _common.STATUS_CODE_TO_CYGRPC_STATUS_CODE[self._state.code],
563
+ self._state.details,
564
+ )
565
+ self._state.condition.notify_all()
566
+
567
+
568
+ class _SingleThreadedRendezvous(
569
+ _Rendezvous, grpc.Call, grpc.Future
570
+ ): # pylint: disable=too-many-ancestors
571
+ """An RPC iterator operating entirely on a single thread.
572
+
573
+ The __next__ method of _SingleThreadedRendezvous does not depend on the
574
+ existence of any other thread, including the "channel spin thread".
575
+ However, this means that its interface is entirely synchronous. So this
576
+ class cannot completely fulfill the grpc.Future interface. The result,
577
+ exception, and traceback methods will never block and will instead raise
578
+ an exception if calling the method would result in blocking.
579
+
580
+ This means that these methods are safe to call from add_done_callback
581
+ handlers.
582
+ """
583
+
584
+ _state: _RPCState
585
+
586
+ def _is_complete(self) -> bool:
587
+ return self._state.code is not None
588
+
589
+ def cancelled(self) -> bool:
590
+ with self._state.condition:
591
+ return self._state.cancelled
592
+
593
+ def running(self) -> bool:
594
+ with self._state.condition:
595
+ return self._state.code is None
596
+
597
+ def done(self) -> bool:
598
+ with self._state.condition:
599
+ return self._state.code is not None
600
+
601
+ def result(self, timeout: Optional[float] = None) -> Any:
602
+ """Returns the result of the computation or raises its exception.
603
+
604
+ This method will never block. Instead, it will raise an exception
605
+ if calling this method would otherwise result in blocking.
606
+
607
+ Since this method will never block, any `timeout` argument passed will
608
+ be ignored.
609
+ """
610
+ del timeout
611
+ with self._state.condition:
612
+ if not self._is_complete():
613
+ error_msg = (
614
+ "_SingleThreadedRendezvous only supports "
615
+ "result() when the RPC is complete."
616
+ )
617
+ raise grpc.experimental.UsageError(error_msg)
618
+ if self._state.code is grpc.StatusCode.OK:
619
+ return self._state.response
620
+ if self._state.cancelled:
621
+ raise grpc.FutureCancelledError()
622
+ else:
623
+ raise self
624
+
625
+ def exception(self, timeout: Optional[float] = None) -> Optional[Exception]:
626
+ """Return the exception raised by the computation.
627
+
628
+ This method will never block. Instead, it will raise an exception
629
+ if calling this method would otherwise result in blocking.
630
+
631
+ Since this method will never block, any `timeout` argument passed will
632
+ be ignored.
633
+ """
634
+ del timeout
635
+ with self._state.condition:
636
+ if not self._is_complete():
637
+ error_msg = (
638
+ "_SingleThreadedRendezvous only supports "
639
+ "exception() when the RPC is complete."
640
+ )
641
+ raise grpc.experimental.UsageError(error_msg)
642
+ if self._state.code is grpc.StatusCode.OK:
643
+ return None
644
+ if self._state.cancelled:
645
+ raise grpc.FutureCancelledError()
646
+ else:
647
+ return self
648
+
649
+ def traceback(
650
+ self, timeout: Optional[float] = None
651
+ ) -> Optional[types.TracebackType]:
652
+ """Access the traceback of the exception raised by the computation.
653
+
654
+ This method will never block. Instead, it will raise an exception
655
+ if calling this method would otherwise result in blocking.
656
+
657
+ Since this method will never block, any `timeout` argument passed will
658
+ be ignored.
659
+ """
660
+ del timeout
661
+ with self._state.condition:
662
+ if not self._is_complete():
663
+ msg = (
664
+ "_SingleThreadedRendezvous only supports "
665
+ "traceback() when the RPC is complete."
666
+ )
667
+ raise grpc.experimental.UsageError(msg)
668
+ if self._state.code is grpc.StatusCode.OK:
669
+ return None
670
+ if self._state.cancelled:
671
+ raise grpc.FutureCancelledError()
672
+ else:
673
+ try:
674
+ raise self
675
+ except grpc.RpcError:
676
+ return sys.exc_info()[2]
677
+
678
+ def add_done_callback(self, fn: Callable[[grpc.Future], None]) -> None:
679
+ with self._state.condition:
680
+ if self._state.code is None:
681
+ self._state.callbacks.append(functools.partial(fn, self))
682
+ return
683
+
684
+ fn(self)
685
+
686
+ def initial_metadata(self) -> Optional[MetadataType]:
687
+ """See grpc.Call.initial_metadata"""
688
+ with self._state.condition:
689
+ # NOTE(gnossen): Based on our initial call batch, we are guaranteed
690
+ # to receive initial metadata before any messages.
691
+ while self._state.initial_metadata is None:
692
+ self._consume_next_event()
693
+ return self._state.initial_metadata
694
+
695
+ def trailing_metadata(self) -> Optional[MetadataType]:
696
+ """See grpc.Call.trailing_metadata"""
697
+ with self._state.condition:
698
+ if self._state.trailing_metadata is None:
699
+ error_msg = (
700
+ "Cannot get trailing metadata until RPC is completed."
701
+ )
702
+ raise grpc.experimental.UsageError(error_msg)
703
+ return self._state.trailing_metadata
704
+
705
+ def code(self) -> Optional[grpc.StatusCode]:
706
+ """See grpc.Call.code"""
707
+ with self._state.condition:
708
+ if self._state.code is None:
709
+ error_msg = "Cannot get code until RPC is completed."
710
+ raise grpc.experimental.UsageError(error_msg)
711
+ return self._state.code
712
+
713
+ def details(self) -> Optional[str]:
714
+ """See grpc.Call.details"""
715
+ with self._state.condition:
716
+ if self._state.details is None:
717
+ error_msg = "Cannot get details until RPC is completed."
718
+ raise grpc.experimental.UsageError(error_msg)
719
+ return _common.decode(self._state.details)
720
+
721
+ def _consume_next_event(self) -> Optional[cygrpc.BaseEvent]:
722
+ event = self._call.next_event()
723
+ with self._state.condition:
724
+ callbacks = _handle_event(
725
+ event, self._state, self._response_deserializer
726
+ )
727
+ for callback in callbacks:
728
+ # NOTE(gnossen): We intentionally allow exceptions to bubble up
729
+ # to the user when running on a single thread.
730
+ callback()
731
+ return event
732
+
733
+ def _next_response(self) -> Any:
734
+ while True:
735
+ self._consume_next_event()
736
+ with self._state.condition:
737
+ if self._state.response is not None:
738
+ response = self._state.response
739
+ self._state.response = None
740
+ return response
741
+ if cygrpc.OperationType.receive_message not in self._state.due:
742
+ if self._state.code is grpc.StatusCode.OK:
743
+ raise StopIteration()
744
+ elif self._state.code is not None:
745
+ raise self
746
+
747
+ def _next(self) -> Any:
748
+ with self._state.condition:
749
+ if self._state.code is None:
750
+ # We tentatively add the operation as expected and remove
751
+ # it if the enqueue operation fails. This allows us to guarantee that
752
+ # if an event has been submitted to the core completion queue,
753
+ # it is in `due`. If we waited until after a successful
754
+ # enqueue operation then a signal could interrupt this
755
+ # thread between the enqueue operation and the addition of the
756
+ # operation to `due`. This would cause an exception on the
757
+ # channel spin thread when the operation completes and no
758
+ # corresponding operation would be present in state.due.
759
+ # Note that, since `condition` is held through this block, there is
760
+ # no data race on `due`.
761
+ self._state.due.add(cygrpc.OperationType.receive_message)
762
+ operating = self._call.operate(
763
+ (cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),), None
764
+ )
765
+ if not operating:
766
+ self._state.due.remove(cygrpc.OperationType.receive_message)
767
+ elif self._state.code is grpc.StatusCode.OK:
768
+ raise StopIteration()
769
+ else:
770
+ raise self
771
+ return self._next_response()
772
+
773
+ def debug_error_string(self) -> Optional[str]:
774
+ with self._state.condition:
775
+ if self._state.debug_error_string is None:
776
+ error_msg = (
777
+ "Cannot get debug error string until RPC is completed."
778
+ )
779
+ raise grpc.experimental.UsageError(error_msg)
780
+ return _common.decode(self._state.debug_error_string)
781
+
782
+
783
+ class _MultiThreadedRendezvous(
784
+ _Rendezvous, grpc.Call, grpc.Future
785
+ ): # pylint: disable=too-many-ancestors
786
+ """An RPC iterator that depends on a channel spin thread.
787
+
788
+ This iterator relies upon a per-channel thread running in the background,
789
+ dequeueing events from the completion queue, and notifying threads waiting
790
+ on the threading.Condition object in the _RPCState object.
791
+
792
+ This extra thread allows _MultiThreadedRendezvous to fulfill the grpc.Future interface
793
+ and to mediate a bidirection streaming RPC.
794
+ """
795
+
796
+ _state: _RPCState
797
+
798
+ def initial_metadata(self) -> Optional[MetadataType]:
799
+ """See grpc.Call.initial_metadata"""
800
+ with self._state.condition:
801
+
802
+ def _done():
803
+ return self._state.initial_metadata is not None
804
+
805
+ _common.wait(self._state.condition.wait, _done)
806
+ return self._state.initial_metadata
807
+
808
+ def trailing_metadata(self) -> Optional[MetadataType]:
809
+ """See grpc.Call.trailing_metadata"""
810
+ with self._state.condition:
811
+
812
+ def _done():
813
+ return self._state.trailing_metadata is not None
814
+
815
+ _common.wait(self._state.condition.wait, _done)
816
+ return self._state.trailing_metadata
817
+
818
+ def code(self) -> Optional[grpc.StatusCode]:
819
+ """See grpc.Call.code"""
820
+ with self._state.condition:
821
+
822
+ def _done():
823
+ return self._state.code is not None
824
+
825
+ _common.wait(self._state.condition.wait, _done)
826
+ return self._state.code
827
+
828
+ def details(self) -> Optional[str]:
829
+ """See grpc.Call.details"""
830
+ with self._state.condition:
831
+
832
+ def _done():
833
+ return self._state.details is not None
834
+
835
+ _common.wait(self._state.condition.wait, _done)
836
+ return _common.decode(self._state.details)
837
+
838
+ def debug_error_string(self) -> Optional[str]:
839
+ with self._state.condition:
840
+
841
+ def _done():
842
+ return self._state.debug_error_string is not None
843
+
844
+ _common.wait(self._state.condition.wait, _done)
845
+ return _common.decode(self._state.debug_error_string)
846
+
847
+ def cancelled(self) -> bool:
848
+ with self._state.condition:
849
+ return self._state.cancelled
850
+
851
+ def running(self) -> bool:
852
+ with self._state.condition:
853
+ return self._state.code is None
854
+
855
+ def done(self) -> bool:
856
+ with self._state.condition:
857
+ return self._state.code is not None
858
+
859
+ def _is_complete(self) -> bool:
860
+ return self._state.code is not None
861
+
862
+ def result(self, timeout: Optional[float] = None) -> Any:
863
+ """Returns the result of the computation or raises its exception.
864
+
865
+ See grpc.Future.result for the full API contract.
866
+ """
867
+ with self._state.condition:
868
+ timed_out = _common.wait(
869
+ self._state.condition.wait, self._is_complete, timeout=timeout
870
+ )
871
+ if timed_out:
872
+ raise grpc.FutureTimeoutError()
873
+ elif self._state.code is grpc.StatusCode.OK:
874
+ return self._state.response
875
+ elif self._state.cancelled:
876
+ raise grpc.FutureCancelledError()
877
+ else:
878
+ raise self
879
+
880
+ def exception(self, timeout: Optional[float] = None) -> Optional[Exception]:
881
+ """Return the exception raised by the computation.
882
+
883
+ See grpc.Future.exception for the full API contract.
884
+ """
885
+ with self._state.condition:
886
+ timed_out = _common.wait(
887
+ self._state.condition.wait, self._is_complete, timeout=timeout
888
+ )
889
+ if timed_out:
890
+ raise grpc.FutureTimeoutError()
891
+ elif self._state.code is grpc.StatusCode.OK:
892
+ return None
893
+ elif self._state.cancelled:
894
+ raise grpc.FutureCancelledError()
895
+ else:
896
+ return self
897
+
898
+ def traceback(
899
+ self, timeout: Optional[float] = None
900
+ ) -> Optional[types.TracebackType]:
901
+ """Access the traceback of the exception raised by the computation.
902
+
903
+ See grpc.future.traceback for the full API contract.
904
+ """
905
+ with self._state.condition:
906
+ timed_out = _common.wait(
907
+ self._state.condition.wait, self._is_complete, timeout=timeout
908
+ )
909
+ if timed_out:
910
+ raise grpc.FutureTimeoutError()
911
+ elif self._state.code is grpc.StatusCode.OK:
912
+ return None
913
+ elif self._state.cancelled:
914
+ raise grpc.FutureCancelledError()
915
+ else:
916
+ try:
917
+ raise self
918
+ except grpc.RpcError:
919
+ return sys.exc_info()[2]
920
+
921
+ def add_done_callback(self, fn: Callable[[grpc.Future], None]) -> None:
922
+ with self._state.condition:
923
+ if self._state.code is None:
924
+ self._state.callbacks.append(functools.partial(fn, self))
925
+ return
926
+
927
+ fn(self)
928
+
929
+ def _next(self) -> Any:
930
+ with self._state.condition:
931
+ if self._state.code is None:
932
+ event_handler = _event_handler(
933
+ self._state, self._response_deserializer
934
+ )
935
+ self._state.due.add(cygrpc.OperationType.receive_message)
936
+ operating = self._call.operate(
937
+ (cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),),
938
+ event_handler,
939
+ )
940
+ if not operating:
941
+ self._state.due.remove(cygrpc.OperationType.receive_message)
942
+ elif self._state.code is grpc.StatusCode.OK:
943
+ raise StopIteration()
944
+ else:
945
+ raise self
946
+
947
+ def _response_ready():
948
+ return self._state.response is not None or (
949
+ cygrpc.OperationType.receive_message not in self._state.due
950
+ and self._state.code is not None
951
+ )
952
+
953
+ _common.wait(self._state.condition.wait, _response_ready)
954
+ if self._state.response is not None:
955
+ response = self._state.response
956
+ self._state.response = None
957
+ return response
958
+ if cygrpc.OperationType.receive_message not in self._state.due:
959
+ if self._state.code is grpc.StatusCode.OK:
960
+ raise StopIteration()
961
+ elif self._state.code is not None:
962
+ raise self
963
+
964
+
965
+ def _start_unary_request(
966
+ request: Any,
967
+ timeout: Optional[float],
968
+ request_serializer: SerializingFunction,
969
+ ) -> Tuple[Optional[float], Optional[bytes], Optional[grpc.RpcError]]:
970
+ deadline = _deadline(timeout)
971
+ serialized_request = _common.serialize(request, request_serializer)
972
+ if serialized_request is None:
973
+ state = _RPCState(
974
+ (),
975
+ (),
976
+ (),
977
+ grpc.StatusCode.INTERNAL,
978
+ "Exception serializing request!",
979
+ )
980
+ error = _InactiveRpcError(state)
981
+ return deadline, None, error
982
+ return deadline, serialized_request, None
983
+
984
+
985
+ def _end_unary_response_blocking(
986
+ state: _RPCState,
987
+ call: cygrpc.SegregatedCall,
988
+ with_call: bool,
989
+ deadline: Optional[float],
990
+ ) -> Union[ResponseType, Tuple[ResponseType, grpc.Call]]:
991
+ if state.code is grpc.StatusCode.OK:
992
+ if with_call:
993
+ rendezvous = _MultiThreadedRendezvous(state, call, None, deadline)
994
+ return state.response, rendezvous
995
+ return state.response
996
+ raise _InactiveRpcError(state) # pytype: disable=not-instantiable
997
+
998
+
999
+ def _stream_unary_invocation_operations(
1000
+ metadata: Optional[MetadataType], initial_metadata_flags: int
1001
+ ) -> Sequence[Sequence[cygrpc.Operation]]:
1002
+ return (
1003
+ (
1004
+ cygrpc.SendInitialMetadataOperation(
1005
+ metadata, initial_metadata_flags
1006
+ ),
1007
+ cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),
1008
+ cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
1009
+ ),
1010
+ (cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
1011
+ )
1012
+
1013
+
1014
+ def _stream_unary_invocation_operations_and_tags(
1015
+ metadata: Optional[MetadataType], initial_metadata_flags: int
1016
+ ) -> Sequence[Tuple[Sequence[cygrpc.Operation], Optional[UserTag]]]:
1017
+ return tuple(
1018
+ (
1019
+ operations,
1020
+ None,
1021
+ )
1022
+ for operations in _stream_unary_invocation_operations(
1023
+ metadata, initial_metadata_flags
1024
+ )
1025
+ )
1026
+
1027
+
1028
+ def _determine_deadline(user_deadline: Optional[float]) -> Optional[float]:
1029
+ parent_deadline = cygrpc.get_deadline_from_context()
1030
+ if parent_deadline is None and user_deadline is None:
1031
+ return None
1032
+ if parent_deadline is not None and user_deadline is None:
1033
+ return parent_deadline
1034
+ if user_deadline is not None and parent_deadline is None:
1035
+ return user_deadline
1036
+ return min(parent_deadline, user_deadline)
1037
+
1038
+
1039
+ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
1040
+ _channel: cygrpc.Channel
1041
+ _managed_call: IntegratedCallFactory
1042
+ _method: bytes
1043
+ _target: bytes
1044
+ _request_serializer: Optional[SerializingFunction]
1045
+ _response_deserializer: Optional[DeserializingFunction]
1046
+ _context: Any
1047
+ _registered_call_handle: Optional[int]
1048
+
1049
+ __slots__ = [
1050
+ "_channel",
1051
+ "_managed_call",
1052
+ "_method",
1053
+ "_target",
1054
+ "_request_serializer",
1055
+ "_response_deserializer",
1056
+ "_context",
1057
+ ]
1058
+
1059
+ # pylint: disable=too-many-arguments
1060
+ def __init__(
1061
+ self,
1062
+ channel: cygrpc.Channel,
1063
+ managed_call: IntegratedCallFactory,
1064
+ method: bytes,
1065
+ target: bytes,
1066
+ request_serializer: Optional[SerializingFunction],
1067
+ response_deserializer: Optional[DeserializingFunction],
1068
+ _registered_call_handle: Optional[int],
1069
+ ):
1070
+ self._channel = channel
1071
+ self._managed_call = managed_call
1072
+ self._method = method
1073
+ self._target = target
1074
+ self._request_serializer = request_serializer
1075
+ self._response_deserializer = response_deserializer
1076
+ self._context = cygrpc.build_census_context()
1077
+ self._registered_call_handle = _registered_call_handle
1078
+
1079
+ def _prepare(
1080
+ self,
1081
+ request: Any,
1082
+ timeout: Optional[float],
1083
+ metadata: Optional[MetadataType],
1084
+ wait_for_ready: Optional[bool],
1085
+ compression: Optional[grpc.Compression],
1086
+ ) -> Tuple[
1087
+ Optional[_RPCState],
1088
+ Optional[Sequence[cygrpc.Operation]],
1089
+ Optional[float],
1090
+ Optional[grpc.RpcError],
1091
+ ]:
1092
+ deadline, serialized_request, rendezvous = _start_unary_request(
1093
+ request, timeout, self._request_serializer
1094
+ )
1095
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
1096
+ wait_for_ready
1097
+ )
1098
+ augmented_metadata = _compression.augment_metadata(
1099
+ metadata, compression
1100
+ )
1101
+ if serialized_request is None:
1102
+ return None, None, None, rendezvous
1103
+ state = _RPCState(_UNARY_UNARY_INITIAL_DUE, None, None, None, None)
1104
+ operations = (
1105
+ cygrpc.SendInitialMetadataOperation(
1106
+ augmented_metadata, initial_metadata_flags
1107
+ ),
1108
+ cygrpc.SendMessageOperation(serialized_request, _EMPTY_FLAGS),
1109
+ cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
1110
+ cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),
1111
+ cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),
1112
+ cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
1113
+ )
1114
+ return state, operations, deadline, None
1115
+
1116
+ def _blocking(
1117
+ self,
1118
+ request: Any,
1119
+ timeout: Optional[float] = None,
1120
+ metadata: Optional[MetadataType] = None,
1121
+ credentials: Optional[grpc.CallCredentials] = None,
1122
+ wait_for_ready: Optional[bool] = None,
1123
+ compression: Optional[grpc.Compression] = None,
1124
+ ) -> Tuple[_RPCState, cygrpc.SegregatedCall]:
1125
+ state, operations, deadline, rendezvous = self._prepare(
1126
+ request, timeout, metadata, wait_for_ready, compression
1127
+ )
1128
+ if state is None:
1129
+ raise rendezvous # pylint: disable-msg=raising-bad-type
1130
+ else:
1131
+ state.rpc_start_time = time.perf_counter()
1132
+ state.method = _common.decode(self._method)
1133
+ state.target = _common.decode(self._target)
1134
+ call = self._channel.segregated_call(
1135
+ cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
1136
+ self._method,
1137
+ None,
1138
+ _determine_deadline(deadline),
1139
+ metadata,
1140
+ None if credentials is None else credentials._credentials,
1141
+ (
1142
+ (
1143
+ operations,
1144
+ None,
1145
+ ),
1146
+ ),
1147
+ self._context,
1148
+ self._registered_call_handle,
1149
+ )
1150
+ event = call.next_event()
1151
+ _handle_event(event, state, self._response_deserializer)
1152
+ return state, call
1153
+
1154
+ def __call__(
1155
+ self,
1156
+ request: Any,
1157
+ timeout: Optional[float] = None,
1158
+ metadata: Optional[MetadataType] = None,
1159
+ credentials: Optional[grpc.CallCredentials] = None,
1160
+ wait_for_ready: Optional[bool] = None,
1161
+ compression: Optional[grpc.Compression] = None,
1162
+ ) -> Any:
1163
+ state, call = self._blocking(
1164
+ request, timeout, metadata, credentials, wait_for_ready, compression
1165
+ )
1166
+ return _end_unary_response_blocking(state, call, False, None)
1167
+
1168
+ def with_call(
1169
+ self,
1170
+ request: Any,
1171
+ timeout: Optional[float] = None,
1172
+ metadata: Optional[MetadataType] = None,
1173
+ credentials: Optional[grpc.CallCredentials] = None,
1174
+ wait_for_ready: Optional[bool] = None,
1175
+ compression: Optional[grpc.Compression] = None,
1176
+ ) -> Tuple[Any, grpc.Call]:
1177
+ state, call = self._blocking(
1178
+ request, timeout, metadata, credentials, wait_for_ready, compression
1179
+ )
1180
+ return _end_unary_response_blocking(state, call, True, None)
1181
+
1182
+ def future(
1183
+ self,
1184
+ request: Any,
1185
+ timeout: Optional[float] = None,
1186
+ metadata: Optional[MetadataType] = None,
1187
+ credentials: Optional[grpc.CallCredentials] = None,
1188
+ wait_for_ready: Optional[bool] = None,
1189
+ compression: Optional[grpc.Compression] = None,
1190
+ ) -> _MultiThreadedRendezvous:
1191
+ state, operations, deadline, rendezvous = self._prepare(
1192
+ request, timeout, metadata, wait_for_ready, compression
1193
+ )
1194
+ if state is None:
1195
+ raise rendezvous # pylint: disable-msg=raising-bad-type
1196
+ else:
1197
+ event_handler = _event_handler(state, self._response_deserializer)
1198
+ state.rpc_start_time = time.perf_counter()
1199
+ state.method = _common.decode(self._method)
1200
+ state.target = _common.decode(self._target)
1201
+ call = self._managed_call(
1202
+ cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
1203
+ self._method,
1204
+ None,
1205
+ deadline,
1206
+ metadata,
1207
+ None if credentials is None else credentials._credentials,
1208
+ (operations,),
1209
+ event_handler,
1210
+ self._context,
1211
+ self._registered_call_handle,
1212
+ )
1213
+ return _MultiThreadedRendezvous(
1214
+ state, call, self._response_deserializer, deadline
1215
+ )
1216
+
1217
+
1218
+ class _SingleThreadedUnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
1219
+ _channel: cygrpc.Channel
1220
+ _method: bytes
1221
+ _target: bytes
1222
+ _request_serializer: Optional[SerializingFunction]
1223
+ _response_deserializer: Optional[DeserializingFunction]
1224
+ _context: Any
1225
+ _registered_call_handle: Optional[int]
1226
+
1227
+ __slots__ = [
1228
+ "_channel",
1229
+ "_method",
1230
+ "_target",
1231
+ "_request_serializer",
1232
+ "_response_deserializer",
1233
+ "_context",
1234
+ ]
1235
+
1236
+ # pylint: disable=too-many-arguments
1237
+ def __init__(
1238
+ self,
1239
+ channel: cygrpc.Channel,
1240
+ method: bytes,
1241
+ target: bytes,
1242
+ request_serializer: SerializingFunction,
1243
+ response_deserializer: DeserializingFunction,
1244
+ _registered_call_handle: Optional[int],
1245
+ ):
1246
+ self._channel = channel
1247
+ self._method = method
1248
+ self._target = target
1249
+ self._request_serializer = request_serializer
1250
+ self._response_deserializer = response_deserializer
1251
+ self._context = cygrpc.build_census_context()
1252
+ self._registered_call_handle = _registered_call_handle
1253
+
1254
+ def __call__( # pylint: disable=too-many-locals
1255
+ self,
1256
+ request: Any,
1257
+ timeout: Optional[float] = None,
1258
+ metadata: Optional[MetadataType] = None,
1259
+ credentials: Optional[grpc.CallCredentials] = None,
1260
+ wait_for_ready: Optional[bool] = None,
1261
+ compression: Optional[grpc.Compression] = None,
1262
+ ) -> _SingleThreadedRendezvous:
1263
+ deadline = _deadline(timeout)
1264
+ serialized_request = _common.serialize(
1265
+ request, self._request_serializer
1266
+ )
1267
+ if serialized_request is None:
1268
+ state = _RPCState(
1269
+ (),
1270
+ (),
1271
+ (),
1272
+ grpc.StatusCode.INTERNAL,
1273
+ "Exception serializing request!",
1274
+ )
1275
+ raise _InactiveRpcError(state)
1276
+
1277
+ state = _RPCState(_UNARY_STREAM_INITIAL_DUE, None, None, None, None)
1278
+ call_credentials = (
1279
+ None if credentials is None else credentials._credentials
1280
+ )
1281
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
1282
+ wait_for_ready
1283
+ )
1284
+ augmented_metadata = _compression.augment_metadata(
1285
+ metadata, compression
1286
+ )
1287
+ operations = (
1288
+ (
1289
+ cygrpc.SendInitialMetadataOperation(
1290
+ augmented_metadata, initial_metadata_flags
1291
+ ),
1292
+ cygrpc.SendMessageOperation(serialized_request, _EMPTY_FLAGS),
1293
+ cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
1294
+ ),
1295
+ (cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),),
1296
+ (cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
1297
+ )
1298
+ operations_and_tags = tuple((ops, None) for ops in operations)
1299
+ state.rpc_start_time = time.perf_counter()
1300
+ state.method = _common.decode(self._method)
1301
+ state.target = _common.decode(self._target)
1302
+ call = self._channel.segregated_call(
1303
+ cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
1304
+ self._method,
1305
+ None,
1306
+ _determine_deadline(deadline),
1307
+ metadata,
1308
+ call_credentials,
1309
+ operations_and_tags,
1310
+ self._context,
1311
+ self._registered_call_handle,
1312
+ )
1313
+ return _SingleThreadedRendezvous(
1314
+ state, call, self._response_deserializer, deadline
1315
+ )
1316
+
1317
+
1318
+ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
1319
+ _channel: cygrpc.Channel
1320
+ _managed_call: IntegratedCallFactory
1321
+ _method: bytes
1322
+ _target: bytes
1323
+ _request_serializer: Optional[SerializingFunction]
1324
+ _response_deserializer: Optional[DeserializingFunction]
1325
+ _context: Any
1326
+ _registered_call_handle: Optional[int]
1327
+
1328
+ __slots__ = [
1329
+ "_channel",
1330
+ "_managed_call",
1331
+ "_method",
1332
+ "_target",
1333
+ "_request_serializer",
1334
+ "_response_deserializer",
1335
+ "_context",
1336
+ ]
1337
+
1338
+ # pylint: disable=too-many-arguments
1339
+ def __init__(
1340
+ self,
1341
+ channel: cygrpc.Channel,
1342
+ managed_call: IntegratedCallFactory,
1343
+ method: bytes,
1344
+ target: bytes,
1345
+ request_serializer: SerializingFunction,
1346
+ response_deserializer: DeserializingFunction,
1347
+ _registered_call_handle: Optional[int],
1348
+ ):
1349
+ self._channel = channel
1350
+ self._managed_call = managed_call
1351
+ self._method = method
1352
+ self._target = target
1353
+ self._request_serializer = request_serializer
1354
+ self._response_deserializer = response_deserializer
1355
+ self._context = cygrpc.build_census_context()
1356
+ self._registered_call_handle = _registered_call_handle
1357
+
1358
+ def __call__( # pylint: disable=too-many-locals
1359
+ self,
1360
+ request: Any,
1361
+ timeout: Optional[float] = None,
1362
+ metadata: Optional[MetadataType] = None,
1363
+ credentials: Optional[grpc.CallCredentials] = None,
1364
+ wait_for_ready: Optional[bool] = None,
1365
+ compression: Optional[grpc.Compression] = None,
1366
+ ) -> _MultiThreadedRendezvous:
1367
+ deadline, serialized_request, rendezvous = _start_unary_request(
1368
+ request, timeout, self._request_serializer
1369
+ )
1370
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
1371
+ wait_for_ready
1372
+ )
1373
+ if serialized_request is None:
1374
+ raise rendezvous # pylint: disable-msg=raising-bad-type
1375
+ else:
1376
+ augmented_metadata = _compression.augment_metadata(
1377
+ metadata, compression
1378
+ )
1379
+ state = _RPCState(_UNARY_STREAM_INITIAL_DUE, None, None, None, None)
1380
+ operations = (
1381
+ (
1382
+ cygrpc.SendInitialMetadataOperation(
1383
+ augmented_metadata, initial_metadata_flags
1384
+ ),
1385
+ cygrpc.SendMessageOperation(
1386
+ serialized_request, _EMPTY_FLAGS
1387
+ ),
1388
+ cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
1389
+ cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
1390
+ ),
1391
+ (cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
1392
+ )
1393
+ state.rpc_start_time = time.perf_counter()
1394
+ state.method = _common.decode(self._method)
1395
+ state.target = _common.decode(self._target)
1396
+ call = self._managed_call(
1397
+ cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
1398
+ self._method,
1399
+ None,
1400
+ _determine_deadline(deadline),
1401
+ metadata,
1402
+ None if credentials is None else credentials._credentials,
1403
+ operations,
1404
+ _event_handler(state, self._response_deserializer),
1405
+ self._context,
1406
+ self._registered_call_handle,
1407
+ )
1408
+ return _MultiThreadedRendezvous(
1409
+ state, call, self._response_deserializer, deadline
1410
+ )
1411
+
1412
+
1413
+ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
1414
+ _channel: cygrpc.Channel
1415
+ _managed_call: IntegratedCallFactory
1416
+ _method: bytes
1417
+ _target: bytes
1418
+ _request_serializer: Optional[SerializingFunction]
1419
+ _response_deserializer: Optional[DeserializingFunction]
1420
+ _context: Any
1421
+ _registered_call_handle: Optional[int]
1422
+
1423
+ __slots__ = [
1424
+ "_channel",
1425
+ "_managed_call",
1426
+ "_method",
1427
+ "_target",
1428
+ "_request_serializer",
1429
+ "_response_deserializer",
1430
+ "_context",
1431
+ ]
1432
+
1433
+ # pylint: disable=too-many-arguments
1434
+ def __init__(
1435
+ self,
1436
+ channel: cygrpc.Channel,
1437
+ managed_call: IntegratedCallFactory,
1438
+ method: bytes,
1439
+ target: bytes,
1440
+ request_serializer: Optional[SerializingFunction],
1441
+ response_deserializer: Optional[DeserializingFunction],
1442
+ _registered_call_handle: Optional[int],
1443
+ ):
1444
+ self._channel = channel
1445
+ self._managed_call = managed_call
1446
+ self._method = method
1447
+ self._target = target
1448
+ self._request_serializer = request_serializer
1449
+ self._response_deserializer = response_deserializer
1450
+ self._context = cygrpc.build_census_context()
1451
+ self._registered_call_handle = _registered_call_handle
1452
+
1453
+ def _blocking(
1454
+ self,
1455
+ request_iterator: Iterator,
1456
+ timeout: Optional[float],
1457
+ metadata: Optional[MetadataType],
1458
+ credentials: Optional[grpc.CallCredentials],
1459
+ wait_for_ready: Optional[bool],
1460
+ compression: Optional[grpc.Compression],
1461
+ ) -> Tuple[_RPCState, cygrpc.SegregatedCall]:
1462
+ deadline = _deadline(timeout)
1463
+ state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None)
1464
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
1465
+ wait_for_ready
1466
+ )
1467
+ augmented_metadata = _compression.augment_metadata(
1468
+ metadata, compression
1469
+ )
1470
+ state.rpc_start_time = time.perf_counter()
1471
+ state.method = _common.decode(self._method)
1472
+ state.target = _common.decode(self._target)
1473
+ call = self._channel.segregated_call(
1474
+ cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
1475
+ self._method,
1476
+ None,
1477
+ _determine_deadline(deadline),
1478
+ augmented_metadata,
1479
+ None if credentials is None else credentials._credentials,
1480
+ _stream_unary_invocation_operations_and_tags(
1481
+ augmented_metadata, initial_metadata_flags
1482
+ ),
1483
+ self._context,
1484
+ self._registered_call_handle,
1485
+ )
1486
+ _consume_request_iterator(
1487
+ request_iterator, state, call, self._request_serializer, None
1488
+ )
1489
+ while True:
1490
+ event = call.next_event()
1491
+ with state.condition:
1492
+ _handle_event(event, state, self._response_deserializer)
1493
+ state.condition.notify_all()
1494
+ if not state.due:
1495
+ break
1496
+ return state, call
1497
+
1498
+ def __call__(
1499
+ self,
1500
+ request_iterator: Iterator,
1501
+ timeout: Optional[float] = None,
1502
+ metadata: Optional[MetadataType] = None,
1503
+ credentials: Optional[grpc.CallCredentials] = None,
1504
+ wait_for_ready: Optional[bool] = None,
1505
+ compression: Optional[grpc.Compression] = None,
1506
+ ) -> Any:
1507
+ state, call = self._blocking(
1508
+ request_iterator,
1509
+ timeout,
1510
+ metadata,
1511
+ credentials,
1512
+ wait_for_ready,
1513
+ compression,
1514
+ )
1515
+ return _end_unary_response_blocking(state, call, False, None)
1516
+
1517
+ def with_call(
1518
+ self,
1519
+ request_iterator: Iterator,
1520
+ timeout: Optional[float] = None,
1521
+ metadata: Optional[MetadataType] = None,
1522
+ credentials: Optional[grpc.CallCredentials] = None,
1523
+ wait_for_ready: Optional[bool] = None,
1524
+ compression: Optional[grpc.Compression] = None,
1525
+ ) -> Tuple[Any, grpc.Call]:
1526
+ state, call = self._blocking(
1527
+ request_iterator,
1528
+ timeout,
1529
+ metadata,
1530
+ credentials,
1531
+ wait_for_ready,
1532
+ compression,
1533
+ )
1534
+ return _end_unary_response_blocking(state, call, True, None)
1535
+
1536
+ def future(
1537
+ self,
1538
+ request_iterator: Iterator,
1539
+ timeout: Optional[float] = None,
1540
+ metadata: Optional[MetadataType] = None,
1541
+ credentials: Optional[grpc.CallCredentials] = None,
1542
+ wait_for_ready: Optional[bool] = None,
1543
+ compression: Optional[grpc.Compression] = None,
1544
+ ) -> _MultiThreadedRendezvous:
1545
+ deadline = _deadline(timeout)
1546
+ state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None)
1547
+ event_handler = _event_handler(state, self._response_deserializer)
1548
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
1549
+ wait_for_ready
1550
+ )
1551
+ augmented_metadata = _compression.augment_metadata(
1552
+ metadata, compression
1553
+ )
1554
+ state.rpc_start_time = time.perf_counter()
1555
+ state.method = _common.decode(self._method)
1556
+ state.target = _common.decode(self._target)
1557
+ call = self._managed_call(
1558
+ cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
1559
+ self._method,
1560
+ None,
1561
+ deadline,
1562
+ augmented_metadata,
1563
+ None if credentials is None else credentials._credentials,
1564
+ _stream_unary_invocation_operations(
1565
+ metadata, initial_metadata_flags
1566
+ ),
1567
+ event_handler,
1568
+ self._context,
1569
+ self._registered_call_handle,
1570
+ )
1571
+ _consume_request_iterator(
1572
+ request_iterator,
1573
+ state,
1574
+ call,
1575
+ self._request_serializer,
1576
+ event_handler,
1577
+ )
1578
+ return _MultiThreadedRendezvous(
1579
+ state, call, self._response_deserializer, deadline
1580
+ )
1581
+
1582
+
1583
+ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
1584
+ _channel: cygrpc.Channel
1585
+ _managed_call: IntegratedCallFactory
1586
+ _method: bytes
1587
+ _target: bytes
1588
+ _request_serializer: Optional[SerializingFunction]
1589
+ _response_deserializer: Optional[DeserializingFunction]
1590
+ _context: Any
1591
+ _registered_call_handle: Optional[int]
1592
+
1593
+ __slots__ = [
1594
+ "_channel",
1595
+ "_managed_call",
1596
+ "_method",
1597
+ "_target",
1598
+ "_request_serializer",
1599
+ "_response_deserializer",
1600
+ "_context",
1601
+ ]
1602
+
1603
+ # pylint: disable=too-many-arguments
1604
+ def __init__(
1605
+ self,
1606
+ channel: cygrpc.Channel,
1607
+ managed_call: IntegratedCallFactory,
1608
+ method: bytes,
1609
+ target: bytes,
1610
+ request_serializer: Optional[SerializingFunction],
1611
+ response_deserializer: Optional[DeserializingFunction],
1612
+ _registered_call_handle: Optional[int],
1613
+ ):
1614
+ self._channel = channel
1615
+ self._managed_call = managed_call
1616
+ self._method = method
1617
+ self._target = target
1618
+ self._request_serializer = request_serializer
1619
+ self._response_deserializer = response_deserializer
1620
+ self._context = cygrpc.build_census_context()
1621
+ self._registered_call_handle = _registered_call_handle
1622
+
1623
+ def __call__(
1624
+ self,
1625
+ request_iterator: Iterator,
1626
+ timeout: Optional[float] = None,
1627
+ metadata: Optional[MetadataType] = None,
1628
+ credentials: Optional[grpc.CallCredentials] = None,
1629
+ wait_for_ready: Optional[bool] = None,
1630
+ compression: Optional[grpc.Compression] = None,
1631
+ ) -> _MultiThreadedRendezvous:
1632
+ deadline = _deadline(timeout)
1633
+ state = _RPCState(_STREAM_STREAM_INITIAL_DUE, None, None, None, None)
1634
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
1635
+ wait_for_ready
1636
+ )
1637
+ augmented_metadata = _compression.augment_metadata(
1638
+ metadata, compression
1639
+ )
1640
+ operations = (
1641
+ (
1642
+ cygrpc.SendInitialMetadataOperation(
1643
+ augmented_metadata, initial_metadata_flags
1644
+ ),
1645
+ cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
1646
+ ),
1647
+ (cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
1648
+ )
1649
+ event_handler = _event_handler(state, self._response_deserializer)
1650
+ state.rpc_start_time = time.perf_counter()
1651
+ state.method = _common.decode(self._method)
1652
+ state.target = _common.decode(self._target)
1653
+ call = self._managed_call(
1654
+ cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
1655
+ self._method,
1656
+ None,
1657
+ _determine_deadline(deadline),
1658
+ augmented_metadata,
1659
+ None if credentials is None else credentials._credentials,
1660
+ operations,
1661
+ event_handler,
1662
+ self._context,
1663
+ self._registered_call_handle,
1664
+ )
1665
+ _consume_request_iterator(
1666
+ request_iterator,
1667
+ state,
1668
+ call,
1669
+ self._request_serializer,
1670
+ event_handler,
1671
+ )
1672
+ return _MultiThreadedRendezvous(
1673
+ state, call, self._response_deserializer, deadline
1674
+ )
1675
+
1676
+
1677
+ class _InitialMetadataFlags(int):
1678
+ """Stores immutable initial metadata flags"""
1679
+
1680
+ def __new__(cls, value: int = _EMPTY_FLAGS):
1681
+ value &= cygrpc.InitialMetadataFlags.used_mask
1682
+ return super(_InitialMetadataFlags, cls).__new__(cls, value)
1683
+
1684
+ def with_wait_for_ready(self, wait_for_ready: Optional[bool]) -> int:
1685
+ if wait_for_ready is not None:
1686
+ if wait_for_ready:
1687
+ return self.__class__(
1688
+ self
1689
+ | cygrpc.InitialMetadataFlags.wait_for_ready
1690
+ | cygrpc.InitialMetadataFlags.wait_for_ready_explicitly_set
1691
+ )
1692
+ if not wait_for_ready:
1693
+ return self.__class__(
1694
+ self & ~cygrpc.InitialMetadataFlags.wait_for_ready
1695
+ | cygrpc.InitialMetadataFlags.wait_for_ready_explicitly_set
1696
+ )
1697
+ return self
1698
+
1699
+
1700
+ class _ChannelCallState(object):
1701
+ channel: cygrpc.Channel
1702
+ managed_calls: int
1703
+ threading: bool
1704
+
1705
+ def __init__(self, channel: cygrpc.Channel):
1706
+ self.lock = threading.Lock()
1707
+ self.channel = channel
1708
+ self.managed_calls = 0
1709
+ self.threading = False
1710
+
1711
+ def reset_postfork_child(self) -> None:
1712
+ self.managed_calls = 0
1713
+
1714
+ def __del__(self):
1715
+ try:
1716
+ self.channel.close(
1717
+ cygrpc.StatusCode.cancelled, "Channel deallocated!"
1718
+ )
1719
+ except (TypeError, AttributeError):
1720
+ pass
1721
+
1722
+
1723
+ def _run_channel_spin_thread(state: _ChannelCallState) -> None:
1724
+ def channel_spin():
1725
+ while True:
1726
+ cygrpc.block_if_fork_in_progress(state)
1727
+ event = state.channel.next_call_event()
1728
+ if event.completion_type == cygrpc.CompletionType.queue_timeout:
1729
+ continue
1730
+ call_completed = event.tag(event)
1731
+ if call_completed:
1732
+ with state.lock:
1733
+ state.managed_calls -= 1
1734
+ if state.managed_calls == 0:
1735
+ return
1736
+
1737
+ channel_spin_thread = cygrpc.ForkManagedThread(target=channel_spin)
1738
+ channel_spin_thread.setDaemon(True)
1739
+ channel_spin_thread.start()
1740
+
1741
+
1742
+ def _channel_managed_call_management(state: _ChannelCallState):
1743
+ # pylint: disable=too-many-arguments
1744
+ def create(
1745
+ flags: int,
1746
+ method: bytes,
1747
+ host: Optional[str],
1748
+ deadline: Optional[float],
1749
+ metadata: Optional[MetadataType],
1750
+ credentials: Optional[cygrpc.CallCredentials],
1751
+ operations: Sequence[Sequence[cygrpc.Operation]],
1752
+ event_handler: UserTag,
1753
+ context: Any,
1754
+ _registered_call_handle: Optional[int],
1755
+ ) -> cygrpc.IntegratedCall:
1756
+ """Creates a cygrpc.IntegratedCall.
1757
+
1758
+ Args:
1759
+ flags: An integer bitfield of call flags.
1760
+ method: The RPC method.
1761
+ host: A host string for the created call.
1762
+ deadline: A float to be the deadline of the created call or None if
1763
+ the call is to have an infinite deadline.
1764
+ metadata: The metadata for the call or None.
1765
+ credentials: A cygrpc.CallCredentials or None.
1766
+ operations: A sequence of sequences of cygrpc.Operations to be
1767
+ started on the call.
1768
+ event_handler: A behavior to call to handle the events resultant from
1769
+ the operations on the call.
1770
+ context: Context object for distributed tracing.
1771
+ _registered_call_handle: An int representing the call handle of the
1772
+ method, or None if the method is not registered.
1773
+
1774
+ Returns:
1775
+ A cygrpc.IntegratedCall with which to conduct an RPC.
1776
+ """
1777
+ operations_and_tags = tuple(
1778
+ (
1779
+ operation,
1780
+ event_handler,
1781
+ )
1782
+ for operation in operations
1783
+ )
1784
+ with state.lock:
1785
+ call = state.channel.integrated_call(
1786
+ flags,
1787
+ method,
1788
+ host,
1789
+ deadline,
1790
+ metadata,
1791
+ credentials,
1792
+ operations_and_tags,
1793
+ context,
1794
+ _registered_call_handle,
1795
+ )
1796
+ if state.managed_calls == 0:
1797
+ state.managed_calls = 1
1798
+ _run_channel_spin_thread(state)
1799
+ else:
1800
+ state.managed_calls += 1
1801
+ return call
1802
+
1803
+ return create
1804
+
1805
+
1806
+ class _ChannelConnectivityState(object):
1807
+ lock: threading.RLock
1808
+ channel: grpc.Channel
1809
+ polling: bool
1810
+ connectivity: grpc.ChannelConnectivity
1811
+ try_to_connect: bool
1812
+ # TODO(xuanwn): Refactor this: https://github.com/grpc/grpc/issues/31704
1813
+ callbacks_and_connectivities: List[
1814
+ Sequence[
1815
+ Union[
1816
+ Callable[[grpc.ChannelConnectivity], None],
1817
+ Optional[grpc.ChannelConnectivity],
1818
+ ]
1819
+ ]
1820
+ ]
1821
+ delivering: bool
1822
+
1823
+ def __init__(self, channel: grpc.Channel):
1824
+ self.lock = threading.RLock()
1825
+ self.channel = channel
1826
+ self.polling = False
1827
+ self.connectivity = None
1828
+ self.try_to_connect = False
1829
+ self.callbacks_and_connectivities = []
1830
+ self.delivering = False
1831
+
1832
+ def reset_postfork_child(self) -> None:
1833
+ self.polling = False
1834
+ self.connectivity = None
1835
+ self.try_to_connect = False
1836
+ self.callbacks_and_connectivities = []
1837
+ self.delivering = False
1838
+
1839
+
1840
+ def _deliveries(
1841
+ state: _ChannelConnectivityState,
1842
+ ) -> List[Callable[[grpc.ChannelConnectivity], None]]:
1843
+ callbacks_needing_update = []
1844
+ for callback_and_connectivity in state.callbacks_and_connectivities:
1845
+ callback, callback_connectivity = callback_and_connectivity
1846
+ if callback_connectivity is not state.connectivity:
1847
+ callbacks_needing_update.append(callback)
1848
+ callback_and_connectivity[1] = state.connectivity
1849
+ return callbacks_needing_update
1850
+
1851
+
1852
+ def _deliver(
1853
+ state: _ChannelConnectivityState,
1854
+ initial_connectivity: grpc.ChannelConnectivity,
1855
+ initial_callbacks: Sequence[Callable[[grpc.ChannelConnectivity], None]],
1856
+ ) -> None:
1857
+ connectivity = initial_connectivity
1858
+ callbacks = initial_callbacks
1859
+ while True:
1860
+ for callback in callbacks:
1861
+ cygrpc.block_if_fork_in_progress(state)
1862
+ try:
1863
+ callback(connectivity)
1864
+ except Exception: # pylint: disable=broad-except
1865
+ _LOGGER.exception(
1866
+ _CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE
1867
+ )
1868
+ with state.lock:
1869
+ callbacks = _deliveries(state)
1870
+ if callbacks:
1871
+ connectivity = state.connectivity
1872
+ else:
1873
+ state.delivering = False
1874
+ return
1875
+
1876
+
1877
+ def _spawn_delivery(
1878
+ state: _ChannelConnectivityState,
1879
+ callbacks: Sequence[Callable[[grpc.ChannelConnectivity], None]],
1880
+ ) -> None:
1881
+ delivering_thread = cygrpc.ForkManagedThread(
1882
+ target=_deliver,
1883
+ args=(
1884
+ state,
1885
+ state.connectivity,
1886
+ callbacks,
1887
+ ),
1888
+ )
1889
+ delivering_thread.setDaemon(True)
1890
+ delivering_thread.start()
1891
+ state.delivering = True
1892
+
1893
+
1894
+ # NOTE(https://github.com/grpc/grpc/issues/3064): We'd rather not poll.
1895
+ def _poll_connectivity(
1896
+ state: _ChannelConnectivityState,
1897
+ channel: grpc.Channel,
1898
+ initial_try_to_connect: bool,
1899
+ ) -> None:
1900
+ try_to_connect = initial_try_to_connect
1901
+ connectivity = channel.check_connectivity_state(try_to_connect)
1902
+ with state.lock:
1903
+ state.connectivity = (
1904
+ _common.CYGRPC_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[
1905
+ connectivity
1906
+ ]
1907
+ )
1908
+ callbacks = tuple(
1909
+ callback for callback, _ in state.callbacks_and_connectivities
1910
+ )
1911
+ for callback_and_connectivity in state.callbacks_and_connectivities:
1912
+ callback_and_connectivity[1] = state.connectivity
1913
+ if callbacks:
1914
+ _spawn_delivery(state, callbacks)
1915
+ while True:
1916
+ event = channel.watch_connectivity_state(
1917
+ connectivity, time.time() + 0.2
1918
+ )
1919
+ cygrpc.block_if_fork_in_progress(state)
1920
+ with state.lock:
1921
+ if (
1922
+ not state.callbacks_and_connectivities
1923
+ and not state.try_to_connect
1924
+ ):
1925
+ state.polling = False
1926
+ state.connectivity = None
1927
+ break
1928
+ try_to_connect = state.try_to_connect
1929
+ state.try_to_connect = False
1930
+ if event.success or try_to_connect:
1931
+ connectivity = channel.check_connectivity_state(try_to_connect)
1932
+ with state.lock:
1933
+ state.connectivity = (
1934
+ _common.CYGRPC_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[
1935
+ connectivity
1936
+ ]
1937
+ )
1938
+ if not state.delivering:
1939
+ callbacks = _deliveries(state)
1940
+ if callbacks:
1941
+ _spawn_delivery(state, callbacks)
1942
+
1943
+
1944
+ def _subscribe(
1945
+ state: _ChannelConnectivityState,
1946
+ callback: Callable[[grpc.ChannelConnectivity], None],
1947
+ try_to_connect: bool,
1948
+ ) -> None:
1949
+ with state.lock:
1950
+ if not state.callbacks_and_connectivities and not state.polling:
1951
+ polling_thread = cygrpc.ForkManagedThread(
1952
+ target=_poll_connectivity,
1953
+ args=(state, state.channel, bool(try_to_connect)),
1954
+ )
1955
+ polling_thread.setDaemon(True)
1956
+ polling_thread.start()
1957
+ state.polling = True
1958
+ state.callbacks_and_connectivities.append([callback, None])
1959
+ elif not state.delivering and state.connectivity is not None:
1960
+ _spawn_delivery(state, (callback,))
1961
+ state.try_to_connect |= bool(try_to_connect)
1962
+ state.callbacks_and_connectivities.append(
1963
+ [callback, state.connectivity]
1964
+ )
1965
+ else:
1966
+ state.try_to_connect |= bool(try_to_connect)
1967
+ state.callbacks_and_connectivities.append([callback, None])
1968
+
1969
+
1970
+ def _unsubscribe(
1971
+ state: _ChannelConnectivityState,
1972
+ callback: Callable[[grpc.ChannelConnectivity], None],
1973
+ ) -> None:
1974
+ with state.lock:
1975
+ for index, (subscribed_callback, _unused_connectivity) in enumerate(
1976
+ state.callbacks_and_connectivities
1977
+ ):
1978
+ if callback == subscribed_callback:
1979
+ state.callbacks_and_connectivities.pop(index)
1980
+ break
1981
+
1982
+
1983
+ def _augment_options(
1984
+ base_options: Sequence[ChannelArgumentType],
1985
+ compression: Optional[grpc.Compression],
1986
+ ) -> Sequence[ChannelArgumentType]:
1987
+ compression_option = _compression.create_channel_option(compression)
1988
+ return (
1989
+ tuple(base_options)
1990
+ + compression_option
1991
+ + (
1992
+ (
1993
+ cygrpc.ChannelArgKey.primary_user_agent_string,
1994
+ _USER_AGENT,
1995
+ ),
1996
+ )
1997
+ )
1998
+
1999
+
2000
+ def _separate_channel_options(
2001
+ options: Sequence[ChannelArgumentType],
2002
+ ) -> Tuple[Sequence[ChannelArgumentType], Sequence[ChannelArgumentType]]:
2003
+ """Separates core channel options from Python channel options."""
2004
+ core_options = []
2005
+ python_options = []
2006
+ for pair in options:
2007
+ if (
2008
+ pair[0]
2009
+ == grpc.experimental.ChannelOptions.SingleThreadedUnaryStream
2010
+ ):
2011
+ python_options.append(pair)
2012
+ else:
2013
+ core_options.append(pair)
2014
+ return python_options, core_options
2015
+
2016
+
2017
+ class Channel(grpc.Channel):
2018
+ """A cygrpc.Channel-backed implementation of grpc.Channel."""
2019
+
2020
+ _single_threaded_unary_stream: bool
2021
+ _channel: cygrpc.Channel
2022
+ _call_state: _ChannelCallState
2023
+ _connectivity_state: _ChannelConnectivityState
2024
+ _target: str
2025
+ _registered_call_handles: Dict[str, int]
2026
+
2027
+ def __init__(
2028
+ self,
2029
+ target: str,
2030
+ options: Sequence[ChannelArgumentType],
2031
+ credentials: Optional[grpc.ChannelCredentials],
2032
+ compression: Optional[grpc.Compression],
2033
+ ):
2034
+ """Constructor.
2035
+
2036
+ Args:
2037
+ target: The target to which to connect.
2038
+ options: Configuration options for the channel.
2039
+ credentials: A cygrpc.ChannelCredentials or None.
2040
+ compression: An optional value indicating the compression method to be
2041
+ used over the lifetime of the channel.
2042
+ """
2043
+ python_options, core_options = _separate_channel_options(options)
2044
+ self._single_threaded_unary_stream = (
2045
+ _DEFAULT_SINGLE_THREADED_UNARY_STREAM
2046
+ )
2047
+ self._process_python_options(python_options)
2048
+ self._channel = cygrpc.Channel(
2049
+ _common.encode(target),
2050
+ _augment_options(core_options, compression),
2051
+ credentials,
2052
+ )
2053
+ self._target = target
2054
+ self._call_state = _ChannelCallState(self._channel)
2055
+ self._connectivity_state = _ChannelConnectivityState(self._channel)
2056
+ cygrpc.fork_register_channel(self)
2057
+ if cygrpc.g_gevent_activated:
2058
+ cygrpc.gevent_increment_channel_count()
2059
+
2060
+ def _get_registered_call_handle(self, method: str) -> int:
2061
+ """
2062
+ Get the registered call handle for a method.
2063
+
2064
+ This is a semi-private method. It is intended for use only by gRPC generated code.
2065
+
2066
+ This method is not thread-safe.
2067
+
2068
+ Args:
2069
+ method: Required, the method name for the RPC.
2070
+
2071
+ Returns:
2072
+ The registered call handle pointer in the form of a Python Long.
2073
+ """
2074
+ return self._channel.get_registered_call_handle(_common.encode(method))
2075
+
2076
+ def _process_python_options(
2077
+ self, python_options: Sequence[ChannelArgumentType]
2078
+ ) -> None:
2079
+ """Sets channel attributes according to python-only channel options."""
2080
+ for pair in python_options:
2081
+ if (
2082
+ pair[0]
2083
+ == grpc.experimental.ChannelOptions.SingleThreadedUnaryStream
2084
+ ):
2085
+ self._single_threaded_unary_stream = True
2086
+
2087
+ def subscribe(
2088
+ self,
2089
+ callback: Callable[[grpc.ChannelConnectivity], None],
2090
+ try_to_connect: Optional[bool] = None,
2091
+ ) -> None:
2092
+ _subscribe(self._connectivity_state, callback, try_to_connect)
2093
+
2094
+ def unsubscribe(
2095
+ self, callback: Callable[[grpc.ChannelConnectivity], None]
2096
+ ) -> None:
2097
+ _unsubscribe(self._connectivity_state, callback)
2098
+
2099
+ # pylint: disable=arguments-differ
2100
+ def unary_unary(
2101
+ self,
2102
+ method: str,
2103
+ request_serializer: Optional[SerializingFunction] = None,
2104
+ response_deserializer: Optional[DeserializingFunction] = None,
2105
+ _registered_method: Optional[bool] = False,
2106
+ ) -> grpc.UnaryUnaryMultiCallable:
2107
+ _registered_call_handle = None
2108
+ if _registered_method:
2109
+ _registered_call_handle = self._get_registered_call_handle(method)
2110
+ return _UnaryUnaryMultiCallable(
2111
+ self._channel,
2112
+ _channel_managed_call_management(self._call_state),
2113
+ _common.encode(method),
2114
+ _common.encode(self._target),
2115
+ request_serializer,
2116
+ response_deserializer,
2117
+ _registered_call_handle,
2118
+ )
2119
+
2120
+ # pylint: disable=arguments-differ
2121
+ def unary_stream(
2122
+ self,
2123
+ method: str,
2124
+ request_serializer: Optional[SerializingFunction] = None,
2125
+ response_deserializer: Optional[DeserializingFunction] = None,
2126
+ _registered_method: Optional[bool] = False,
2127
+ ) -> grpc.UnaryStreamMultiCallable:
2128
+ _registered_call_handle = None
2129
+ if _registered_method:
2130
+ _registered_call_handle = self._get_registered_call_handle(method)
2131
+ # NOTE(rbellevi): Benchmarks have shown that running a unary-stream RPC
2132
+ # on a single Python thread results in an appreciable speed-up. However,
2133
+ # due to slight differences in capability, the multi-threaded variant
2134
+ # remains the default.
2135
+ if self._single_threaded_unary_stream:
2136
+ return _SingleThreadedUnaryStreamMultiCallable(
2137
+ self._channel,
2138
+ _common.encode(method),
2139
+ _common.encode(self._target),
2140
+ request_serializer,
2141
+ response_deserializer,
2142
+ _registered_call_handle,
2143
+ )
2144
+ return _UnaryStreamMultiCallable(
2145
+ self._channel,
2146
+ _channel_managed_call_management(self._call_state),
2147
+ _common.encode(method),
2148
+ _common.encode(self._target),
2149
+ request_serializer,
2150
+ response_deserializer,
2151
+ _registered_call_handle,
2152
+ )
2153
+
2154
+ # pylint: disable=arguments-differ
2155
+ def stream_unary(
2156
+ self,
2157
+ method: str,
2158
+ request_serializer: Optional[SerializingFunction] = None,
2159
+ response_deserializer: Optional[DeserializingFunction] = None,
2160
+ _registered_method: Optional[bool] = False,
2161
+ ) -> grpc.StreamUnaryMultiCallable:
2162
+ _registered_call_handle = None
2163
+ if _registered_method:
2164
+ _registered_call_handle = self._get_registered_call_handle(method)
2165
+ return _StreamUnaryMultiCallable(
2166
+ self._channel,
2167
+ _channel_managed_call_management(self._call_state),
2168
+ _common.encode(method),
2169
+ _common.encode(self._target),
2170
+ request_serializer,
2171
+ response_deserializer,
2172
+ _registered_call_handle,
2173
+ )
2174
+
2175
+ # pylint: disable=arguments-differ
2176
+ def stream_stream(
2177
+ self,
2178
+ method: str,
2179
+ request_serializer: Optional[SerializingFunction] = None,
2180
+ response_deserializer: Optional[DeserializingFunction] = None,
2181
+ _registered_method: Optional[bool] = False,
2182
+ ) -> grpc.StreamStreamMultiCallable:
2183
+ _registered_call_handle = None
2184
+ if _registered_method:
2185
+ _registered_call_handle = self._get_registered_call_handle(method)
2186
+ return _StreamStreamMultiCallable(
2187
+ self._channel,
2188
+ _channel_managed_call_management(self._call_state),
2189
+ _common.encode(method),
2190
+ _common.encode(self._target),
2191
+ request_serializer,
2192
+ response_deserializer,
2193
+ _registered_call_handle,
2194
+ )
2195
+
2196
+ def _unsubscribe_all(self) -> None:
2197
+ state = self._connectivity_state
2198
+ if state:
2199
+ with state.lock:
2200
+ del state.callbacks_and_connectivities[:]
2201
+
2202
+ def _close(self) -> None:
2203
+ self._unsubscribe_all()
2204
+ self._channel.close(cygrpc.StatusCode.cancelled, "Channel closed!")
2205
+ cygrpc.fork_unregister_channel(self)
2206
+ if cygrpc.g_gevent_activated:
2207
+ cygrpc.gevent_decrement_channel_count()
2208
+
2209
+ def _close_on_fork(self) -> None:
2210
+ self._unsubscribe_all()
2211
+ self._channel.close_on_fork(
2212
+ cygrpc.StatusCode.cancelled, "Channel closed due to fork"
2213
+ )
2214
+
2215
+ def __enter__(self):
2216
+ return self
2217
+
2218
+ def __exit__(self, exc_type, exc_val, exc_tb):
2219
+ self._close()
2220
+ return False
2221
+
2222
+ def close(self) -> None:
2223
+ self._close()
2224
+
2225
+ def __del__(self):
2226
+ # TODO(https://github.com/grpc/grpc/issues/12531): Several releases
2227
+ # after 1.12 (1.16 or thereabouts?) add a "self._channel.close" call
2228
+ # here (or more likely, call self._close() here). We don't do this today
2229
+ # because many valid use cases today allow the channel to be deleted
2230
+ # immediately after stubs are created. After a sufficient period of time
2231
+ # has passed for all users to be trusted to freeze out to their channels
2232
+ # for as long as they are in use and to close them after using them,
2233
+ # then deletion of this grpc._channel.Channel instance can be made to
2234
+ # effect closure of the underlying cygrpc.Channel instance.
2235
+ try:
2236
+ self._unsubscribe_all()
2237
+ except: # pylint: disable=bare-except # noqa: E722
2238
+ # Exceptions in __del__ are ignored by Python anyway, but they can
2239
+ # keep spamming logs. Just silence them.
2240
+ pass
lib/python3.12/site-packages/grpc/_common.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ return s.encode("utf8")
71
+
72
+
73
+ def decode(b: AnyStr) -> str:
74
+ if isinstance(b, bytes):
75
+ return b.decode("utf-8", "replace")
76
+ return b
77
+
78
+
79
+ def _transform(
80
+ message: Any,
81
+ transformer: Union[SerializingFunction, DeserializingFunction, None],
82
+ exception_message: str,
83
+ ) -> Any:
84
+ if transformer is None:
85
+ return message
86
+ try:
87
+ return transformer(message)
88
+ except Exception: # pylint: disable=broad-except
89
+ _LOGGER.exception(exception_message)
90
+ return None
91
+
92
+
93
+ def serialize(message: Any, serializer: Optional[SerializingFunction]) -> bytes:
94
+ return _transform(message, serializer, "Exception serializing message!")
95
+
96
+
97
+ def deserialize(
98
+ serialized_message: bytes, deserializer: Optional[DeserializingFunction]
99
+ ) -> Any:
100
+ return _transform(
101
+ serialized_message, deserializer, "Exception deserializing message!"
102
+ )
103
+
104
+
105
+ def fully_qualified_method(group: str, method: str) -> str:
106
+ return "/{}/{}".format(group, method)
107
+
108
+
109
+ def _wait_once(
110
+ wait_fn: Callable[..., bool],
111
+ timeout: float,
112
+ spin_cb: Optional[Callable[[], None]],
113
+ ):
114
+ wait_fn(timeout=timeout)
115
+ if spin_cb is not None:
116
+ spin_cb()
117
+
118
+
119
+ def wait(
120
+ wait_fn: Callable[..., bool],
121
+ wait_complete_fn: Callable[[], bool],
122
+ timeout: Optional[float] = None,
123
+ spin_cb: Optional[Callable[[], None]] = None,
124
+ ) -> bool:
125
+ """Blocks waiting for an event without blocking the thread indefinitely.
126
+
127
+ See https://github.com/grpc/grpc/issues/19464 for full context. CPython's
128
+ `threading.Event.wait` and `threading.Condition.wait` methods, if invoked
129
+ without a timeout kwarg, may block the calling thread indefinitely. If the
130
+ call is made from the main thread, this means that signal handlers may not
131
+ run for an arbitrarily long period of time.
132
+
133
+ This wrapper calls the supplied wait function with an arbitrary short
134
+ timeout to ensure that no signal handler has to wait longer than
135
+ MAXIMUM_WAIT_TIMEOUT before executing.
136
+
137
+ Args:
138
+ wait_fn: A callable acceptable a single float-valued kwarg named
139
+ `timeout`. This function is expected to be one of `threading.Event.wait`
140
+ or `threading.Condition.wait`.
141
+ wait_complete_fn: A callable taking no arguments and returning a bool.
142
+ When this function returns true, it indicates that waiting should cease.
143
+ timeout: An optional float-valued number of seconds after which the wait
144
+ should cease.
145
+ spin_cb: An optional Callable taking no arguments and returning nothing.
146
+ This callback will be called on each iteration of the spin. This may be
147
+ used for, e.g. work related to forking.
148
+
149
+ Returns:
150
+ True if a timeout was supplied and it was reached. False otherwise.
151
+ """
152
+ if timeout is None:
153
+ while not wait_complete_fn():
154
+ _wait_once(wait_fn, MAXIMUM_WAIT_TIMEOUT, spin_cb)
155
+ else:
156
+ end = time.time() + timeout
157
+ while not wait_complete_fn():
158
+ remaining = min(end - time.time(), MAXIMUM_WAIT_TIMEOUT)
159
+ if remaining < 0:
160
+ return True
161
+ _wait_once(wait_fn, remaining, spin_cb)
162
+ return False
163
+
164
+
165
+ def validate_port_binding_result(address: str, port: int) -> int:
166
+ """Validates if the port binding succeed.
167
+
168
+ If the port returned by Core is 0, the binding is failed. However, in that
169
+ case, the Core API doesn't return a detailed failing reason. The best we
170
+ can do is raising an exception to prevent further confusion.
171
+
172
+ Args:
173
+ address: The address string to be bound.
174
+ port: An int returned by core
175
+ """
176
+ if port == 0:
177
+ # The Core API doesn't return a failure message. The best we can do
178
+ # is raising an exception to prevent further confusion.
179
+ raise RuntimeError(_ERROR_MESSAGE_PORT_BINDING_FAILED % address)
180
+ else:
181
+ return port
lib/python3.12/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
+ )
lib/python3.12/site-packages/grpc/_cython/__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.
lib/python3.12/site-packages/grpc/_cython/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (191 Bytes). View file
 
lib/python3.12/site-packages/grpc/_cython/_credentials/roots.pem ADDED
The diff for this file is too large to render. See raw diff
 
lib/python3.12/site-packages/grpc/_cython/_cygrpc/__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.
lib/python3.12/site-packages/grpc/_cython/_cygrpc/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (199 Bytes). View file
 
lib/python3.12/site-packages/grpc/_grpcio_metadata.py ADDED
@@ -0,0 +1 @@
 
 
1
+ __version__ = """1.76.0"""
lib/python3.12/site-packages/grpc/_interceptor.py ADDED
@@ -0,0 +1,809 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ return thunk(context)
47
+
48
+ def execute(
49
+ self, thunk: Callable, context: grpc.HandlerCallDetails
50
+ ) -> grpc.RpcMethodHandler:
51
+ return self._intercept_at(thunk, 0, context)
52
+
53
+
54
+ def service_pipeline(
55
+ interceptors: Optional[Sequence[grpc.ServerInterceptor]],
56
+ ) -> Optional[_ServicePipeline]:
57
+ return _ServicePipeline(interceptors) if interceptors else None
58
+
59
+
60
+ class _ClientCallDetails(
61
+ collections.namedtuple(
62
+ "_ClientCallDetails",
63
+ (
64
+ "method",
65
+ "timeout",
66
+ "metadata",
67
+ "credentials",
68
+ "wait_for_ready",
69
+ "compression",
70
+ ),
71
+ ),
72
+ grpc.ClientCallDetails,
73
+ ):
74
+ pass
75
+
76
+
77
+ def _unwrap_client_call_details(
78
+ call_details: grpc.ClientCallDetails,
79
+ default_details: grpc.ClientCallDetails,
80
+ ) -> Tuple[
81
+ str, float, MetadataType, grpc.CallCredentials, bool, grpc.Compression
82
+ ]:
83
+ try:
84
+ method = call_details.method # pytype: disable=attribute-error
85
+ except AttributeError:
86
+ method = default_details.method # pytype: disable=attribute-error
87
+
88
+ try:
89
+ timeout = call_details.timeout # pytype: disable=attribute-error
90
+ except AttributeError:
91
+ timeout = default_details.timeout # pytype: disable=attribute-error
92
+
93
+ try:
94
+ metadata = call_details.metadata # pytype: disable=attribute-error
95
+ except AttributeError:
96
+ metadata = default_details.metadata # pytype: disable=attribute-error
97
+
98
+ try:
99
+ credentials = (
100
+ call_details.credentials
101
+ ) # pytype: disable=attribute-error
102
+ except AttributeError:
103
+ credentials = (
104
+ default_details.credentials
105
+ ) # pytype: disable=attribute-error
106
+
107
+ try:
108
+ wait_for_ready = (
109
+ call_details.wait_for_ready
110
+ ) # pytype: disable=attribute-error
111
+ except AttributeError:
112
+ wait_for_ready = (
113
+ default_details.wait_for_ready
114
+ ) # pytype: disable=attribute-error
115
+
116
+ try:
117
+ compression = (
118
+ call_details.compression
119
+ ) # pytype: disable=attribute-error
120
+ except AttributeError:
121
+ compression = (
122
+ default_details.compression
123
+ ) # pytype: disable=attribute-error
124
+
125
+ return method, timeout, metadata, credentials, wait_for_ready, compression
126
+
127
+
128
+ class _FailureOutcome(
129
+ grpc.RpcError, grpc.Future, grpc.Call
130
+ ): # pylint: disable=too-many-ancestors
131
+ _exception: Exception
132
+ _traceback: types.TracebackType
133
+
134
+ def __init__(self, exception: Exception, traceback: types.TracebackType):
135
+ super(_FailureOutcome, self).__init__()
136
+ self._exception = exception
137
+ self._traceback = traceback
138
+
139
+ def initial_metadata(self) -> Optional[MetadataType]:
140
+ return None
141
+
142
+ def trailing_metadata(self) -> Optional[MetadataType]:
143
+ return None
144
+
145
+ def code(self) -> Optional[grpc.StatusCode]:
146
+ return grpc.StatusCode.INTERNAL
147
+
148
+ def details(self) -> Optional[str]:
149
+ return "Exception raised while intercepting the RPC"
150
+
151
+ def cancel(self) -> bool:
152
+ return False
153
+
154
+ def cancelled(self) -> bool:
155
+ return False
156
+
157
+ def is_active(self) -> bool:
158
+ return False
159
+
160
+ def time_remaining(self) -> Optional[float]:
161
+ return None
162
+
163
+ def running(self) -> bool:
164
+ return False
165
+
166
+ def done(self) -> bool:
167
+ return True
168
+
169
+ def result(self, ignored_timeout: Optional[float] = None):
170
+ raise self._exception
171
+
172
+ def exception(
173
+ self, ignored_timeout: Optional[float] = None
174
+ ) -> Optional[Exception]:
175
+ return self._exception
176
+
177
+ def traceback(
178
+ self, ignored_timeout: Optional[float] = None
179
+ ) -> Optional[types.TracebackType]:
180
+ return self._traceback
181
+
182
+ def add_callback(self, unused_callback) -> bool:
183
+ return False
184
+
185
+ def add_done_callback(self, fn: DoneCallbackType) -> None:
186
+ fn(self)
187
+
188
+ def __iter__(self):
189
+ return self
190
+
191
+ def __next__(self):
192
+ raise self._exception
193
+
194
+ def next(self):
195
+ return self.__next__()
196
+
197
+
198
+ class _UnaryOutcome(grpc.Call, grpc.Future):
199
+ _response: Any
200
+ _call: grpc.Call
201
+
202
+ def __init__(self, response: Any, call: grpc.Call):
203
+ self._response = response
204
+ self._call = call
205
+
206
+ def initial_metadata(self) -> Optional[MetadataType]:
207
+ return self._call.initial_metadata()
208
+
209
+ def trailing_metadata(self) -> Optional[MetadataType]:
210
+ return self._call.trailing_metadata()
211
+
212
+ def code(self) -> Optional[grpc.StatusCode]:
213
+ return self._call.code()
214
+
215
+ def details(self) -> Optional[str]:
216
+ return self._call.details()
217
+
218
+ def is_active(self) -> bool:
219
+ return self._call.is_active()
220
+
221
+ def time_remaining(self) -> Optional[float]:
222
+ return self._call.time_remaining()
223
+
224
+ def cancel(self) -> bool:
225
+ return self._call.cancel()
226
+
227
+ def add_callback(self, callback) -> bool:
228
+ return self._call.add_callback(callback)
229
+
230
+ def cancelled(self) -> bool:
231
+ return False
232
+
233
+ def running(self) -> bool:
234
+ return False
235
+
236
+ def done(self) -> bool:
237
+ return True
238
+
239
+ def result(self, ignored_timeout: Optional[float] = None):
240
+ return self._response
241
+
242
+ def exception(self, ignored_timeout: Optional[float] = None):
243
+ return None
244
+
245
+ def traceback(self, ignored_timeout: Optional[float] = None):
246
+ return None
247
+
248
+ def add_done_callback(self, fn: DoneCallbackType) -> None:
249
+ fn(self)
250
+
251
+
252
+ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
253
+ _thunk: Callable
254
+ _method: str
255
+ _interceptor: grpc.UnaryUnaryClientInterceptor
256
+
257
+ def __init__(
258
+ self,
259
+ thunk: Callable,
260
+ method: str,
261
+ interceptor: grpc.UnaryUnaryClientInterceptor,
262
+ ):
263
+ self._thunk = thunk
264
+ self._method = method
265
+ self._interceptor = interceptor
266
+
267
+ def __call__(
268
+ self,
269
+ request: Any,
270
+ timeout: Optional[float] = None,
271
+ metadata: Optional[MetadataType] = None,
272
+ credentials: Optional[grpc.CallCredentials] = None,
273
+ wait_for_ready: Optional[bool] = None,
274
+ compression: Optional[grpc.Compression] = None,
275
+ ) -> Any:
276
+ response, ignored_call = self._with_call(
277
+ request,
278
+ timeout=timeout,
279
+ metadata=metadata,
280
+ credentials=credentials,
281
+ wait_for_ready=wait_for_ready,
282
+ compression=compression,
283
+ )
284
+ return response
285
+
286
+ def _with_call(
287
+ self,
288
+ request: Any,
289
+ timeout: Optional[float] = None,
290
+ metadata: Optional[MetadataType] = None,
291
+ credentials: Optional[grpc.CallCredentials] = None,
292
+ wait_for_ready: Optional[bool] = None,
293
+ compression: Optional[grpc.Compression] = None,
294
+ ) -> Tuple[Any, grpc.Call]:
295
+ client_call_details = _ClientCallDetails(
296
+ self._method,
297
+ timeout,
298
+ metadata,
299
+ credentials,
300
+ wait_for_ready,
301
+ compression,
302
+ )
303
+
304
+ def continuation(new_details, request):
305
+ (
306
+ new_method,
307
+ new_timeout,
308
+ new_metadata,
309
+ new_credentials,
310
+ new_wait_for_ready,
311
+ new_compression,
312
+ ) = _unwrap_client_call_details(new_details, client_call_details)
313
+ try:
314
+ response, call = self._thunk(new_method).with_call(
315
+ request,
316
+ timeout=new_timeout,
317
+ metadata=new_metadata,
318
+ credentials=new_credentials,
319
+ wait_for_ready=new_wait_for_ready,
320
+ compression=new_compression,
321
+ )
322
+ return _UnaryOutcome(response, call)
323
+ except grpc.RpcError as rpc_error:
324
+ return rpc_error
325
+ except Exception as exception: # pylint:disable=broad-except
326
+ return _FailureOutcome(exception, sys.exc_info()[2])
327
+
328
+ call = self._interceptor.intercept_unary_unary(
329
+ continuation, client_call_details, request
330
+ )
331
+ return call.result(), call
332
+
333
+ def with_call(
334
+ self,
335
+ request: Any,
336
+ timeout: Optional[float] = None,
337
+ metadata: Optional[MetadataType] = None,
338
+ credentials: Optional[grpc.CallCredentials] = None,
339
+ wait_for_ready: Optional[bool] = None,
340
+ compression: Optional[grpc.Compression] = None,
341
+ ) -> Tuple[Any, grpc.Call]:
342
+ return self._with_call(
343
+ request,
344
+ timeout=timeout,
345
+ metadata=metadata,
346
+ credentials=credentials,
347
+ wait_for_ready=wait_for_ready,
348
+ compression=compression,
349
+ )
350
+
351
+ def future(
352
+ self,
353
+ request: Any,
354
+ timeout: Optional[float] = None,
355
+ metadata: Optional[MetadataType] = None,
356
+ credentials: Optional[grpc.CallCredentials] = None,
357
+ wait_for_ready: Optional[bool] = None,
358
+ compression: Optional[grpc.Compression] = None,
359
+ ) -> Any:
360
+ client_call_details = _ClientCallDetails(
361
+ self._method,
362
+ timeout,
363
+ metadata,
364
+ credentials,
365
+ wait_for_ready,
366
+ compression,
367
+ )
368
+
369
+ def continuation(new_details, request):
370
+ (
371
+ new_method,
372
+ new_timeout,
373
+ new_metadata,
374
+ new_credentials,
375
+ new_wait_for_ready,
376
+ new_compression,
377
+ ) = _unwrap_client_call_details(new_details, client_call_details)
378
+ return self._thunk(new_method).future(
379
+ request,
380
+ timeout=new_timeout,
381
+ metadata=new_metadata,
382
+ credentials=new_credentials,
383
+ wait_for_ready=new_wait_for_ready,
384
+ compression=new_compression,
385
+ )
386
+
387
+ try:
388
+ return self._interceptor.intercept_unary_unary(
389
+ continuation, client_call_details, request
390
+ )
391
+ except Exception as exception: # pylint:disable=broad-except
392
+ return _FailureOutcome(exception, sys.exc_info()[2])
393
+
394
+
395
+ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
396
+ _thunk: Callable
397
+ _method: str
398
+ _interceptor: grpc.UnaryStreamClientInterceptor
399
+
400
+ def __init__(
401
+ self,
402
+ thunk: Callable,
403
+ method: str,
404
+ interceptor: grpc.UnaryStreamClientInterceptor,
405
+ ):
406
+ self._thunk = thunk
407
+ self._method = method
408
+ self._interceptor = interceptor
409
+
410
+ def __call__(
411
+ self,
412
+ request: Any,
413
+ timeout: Optional[float] = None,
414
+ metadata: Optional[MetadataType] = None,
415
+ credentials: Optional[grpc.CallCredentials] = None,
416
+ wait_for_ready: Optional[bool] = None,
417
+ compression: Optional[grpc.Compression] = None,
418
+ ):
419
+ client_call_details = _ClientCallDetails(
420
+ self._method,
421
+ timeout,
422
+ metadata,
423
+ credentials,
424
+ wait_for_ready,
425
+ compression,
426
+ )
427
+
428
+ def continuation(new_details, request):
429
+ (
430
+ new_method,
431
+ new_timeout,
432
+ new_metadata,
433
+ new_credentials,
434
+ new_wait_for_ready,
435
+ new_compression,
436
+ ) = _unwrap_client_call_details(new_details, client_call_details)
437
+ return self._thunk(new_method)(
438
+ request,
439
+ timeout=new_timeout,
440
+ metadata=new_metadata,
441
+ credentials=new_credentials,
442
+ wait_for_ready=new_wait_for_ready,
443
+ compression=new_compression,
444
+ )
445
+
446
+ try:
447
+ return self._interceptor.intercept_unary_stream(
448
+ continuation, client_call_details, request
449
+ )
450
+ except Exception as exception: # pylint:disable=broad-except
451
+ return _FailureOutcome(exception, sys.exc_info()[2])
452
+
453
+
454
+ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
455
+ _thunk: Callable
456
+ _method: str
457
+ _interceptor: grpc.StreamUnaryClientInterceptor
458
+
459
+ def __init__(
460
+ self,
461
+ thunk: Callable,
462
+ method: str,
463
+ interceptor: grpc.StreamUnaryClientInterceptor,
464
+ ):
465
+ self._thunk = thunk
466
+ self._method = method
467
+ self._interceptor = interceptor
468
+
469
+ def __call__(
470
+ self,
471
+ request_iterator: RequestIterableType,
472
+ timeout: Optional[float] = None,
473
+ metadata: Optional[MetadataType] = None,
474
+ credentials: Optional[grpc.CallCredentials] = None,
475
+ wait_for_ready: Optional[bool] = None,
476
+ compression: Optional[grpc.Compression] = None,
477
+ ) -> Any:
478
+ response, ignored_call = self._with_call(
479
+ request_iterator,
480
+ timeout=timeout,
481
+ metadata=metadata,
482
+ credentials=credentials,
483
+ wait_for_ready=wait_for_ready,
484
+ compression=compression,
485
+ )
486
+ return response
487
+
488
+ def _with_call(
489
+ self,
490
+ request_iterator: RequestIterableType,
491
+ timeout: Optional[float] = None,
492
+ metadata: Optional[MetadataType] = None,
493
+ credentials: Optional[grpc.CallCredentials] = None,
494
+ wait_for_ready: Optional[bool] = None,
495
+ compression: Optional[grpc.Compression] = None,
496
+ ) -> Tuple[Any, grpc.Call]:
497
+ client_call_details = _ClientCallDetails(
498
+ self._method,
499
+ timeout,
500
+ metadata,
501
+ credentials,
502
+ wait_for_ready,
503
+ compression,
504
+ )
505
+
506
+ def continuation(new_details, request_iterator):
507
+ (
508
+ new_method,
509
+ new_timeout,
510
+ new_metadata,
511
+ new_credentials,
512
+ new_wait_for_ready,
513
+ new_compression,
514
+ ) = _unwrap_client_call_details(new_details, client_call_details)
515
+ try:
516
+ response, call = self._thunk(new_method).with_call(
517
+ request_iterator,
518
+ timeout=new_timeout,
519
+ metadata=new_metadata,
520
+ credentials=new_credentials,
521
+ wait_for_ready=new_wait_for_ready,
522
+ compression=new_compression,
523
+ )
524
+ return _UnaryOutcome(response, call)
525
+ except grpc.RpcError as rpc_error:
526
+ return rpc_error
527
+ except Exception as exception: # pylint:disable=broad-except
528
+ return _FailureOutcome(exception, sys.exc_info()[2])
529
+
530
+ call = self._interceptor.intercept_stream_unary(
531
+ continuation, client_call_details, request_iterator
532
+ )
533
+ return call.result(), call
534
+
535
+ def with_call(
536
+ self,
537
+ request_iterator: RequestIterableType,
538
+ timeout: Optional[float] = None,
539
+ metadata: Optional[MetadataType] = None,
540
+ credentials: Optional[grpc.CallCredentials] = None,
541
+ wait_for_ready: Optional[bool] = None,
542
+ compression: Optional[grpc.Compression] = None,
543
+ ) -> Tuple[Any, grpc.Call]:
544
+ return self._with_call(
545
+ request_iterator,
546
+ timeout=timeout,
547
+ metadata=metadata,
548
+ credentials=credentials,
549
+ wait_for_ready=wait_for_ready,
550
+ compression=compression,
551
+ )
552
+
553
+ def future(
554
+ self,
555
+ request_iterator: RequestIterableType,
556
+ timeout: Optional[float] = None,
557
+ metadata: Optional[MetadataType] = None,
558
+ credentials: Optional[grpc.CallCredentials] = None,
559
+ wait_for_ready: Optional[bool] = None,
560
+ compression: Optional[grpc.Compression] = None,
561
+ ) -> Any:
562
+ client_call_details = _ClientCallDetails(
563
+ self._method,
564
+ timeout,
565
+ metadata,
566
+ credentials,
567
+ wait_for_ready,
568
+ compression,
569
+ )
570
+
571
+ def continuation(new_details, request_iterator):
572
+ (
573
+ new_method,
574
+ new_timeout,
575
+ new_metadata,
576
+ new_credentials,
577
+ new_wait_for_ready,
578
+ new_compression,
579
+ ) = _unwrap_client_call_details(new_details, client_call_details)
580
+ return self._thunk(new_method).future(
581
+ request_iterator,
582
+ timeout=new_timeout,
583
+ metadata=new_metadata,
584
+ credentials=new_credentials,
585
+ wait_for_ready=new_wait_for_ready,
586
+ compression=new_compression,
587
+ )
588
+
589
+ try:
590
+ return self._interceptor.intercept_stream_unary(
591
+ continuation, client_call_details, request_iterator
592
+ )
593
+ except Exception as exception: # pylint:disable=broad-except
594
+ return _FailureOutcome(exception, sys.exc_info()[2])
595
+
596
+
597
+ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
598
+ _thunk: Callable
599
+ _method: str
600
+ _interceptor: grpc.StreamStreamClientInterceptor
601
+
602
+ def __init__(
603
+ self,
604
+ thunk: Callable,
605
+ method: str,
606
+ interceptor: grpc.StreamStreamClientInterceptor,
607
+ ):
608
+ self._thunk = thunk
609
+ self._method = method
610
+ self._interceptor = interceptor
611
+
612
+ def __call__(
613
+ self,
614
+ request_iterator: RequestIterableType,
615
+ timeout: Optional[float] = None,
616
+ metadata: Optional[MetadataType] = None,
617
+ credentials: Optional[grpc.CallCredentials] = None,
618
+ wait_for_ready: Optional[bool] = None,
619
+ compression: Optional[grpc.Compression] = None,
620
+ ):
621
+ client_call_details = _ClientCallDetails(
622
+ self._method,
623
+ timeout,
624
+ metadata,
625
+ credentials,
626
+ wait_for_ready,
627
+ compression,
628
+ )
629
+
630
+ def continuation(new_details, request_iterator):
631
+ (
632
+ new_method,
633
+ new_timeout,
634
+ new_metadata,
635
+ new_credentials,
636
+ new_wait_for_ready,
637
+ new_compression,
638
+ ) = _unwrap_client_call_details(new_details, client_call_details)
639
+ return self._thunk(new_method)(
640
+ request_iterator,
641
+ timeout=new_timeout,
642
+ metadata=new_metadata,
643
+ credentials=new_credentials,
644
+ wait_for_ready=new_wait_for_ready,
645
+ compression=new_compression,
646
+ )
647
+
648
+ try:
649
+ return self._interceptor.intercept_stream_stream(
650
+ continuation, client_call_details, request_iterator
651
+ )
652
+ except Exception as exception: # pylint:disable=broad-except
653
+ return _FailureOutcome(exception, sys.exc_info()[2])
654
+
655
+
656
+ class _Channel(grpc.Channel):
657
+ _channel: grpc.Channel
658
+ _interceptor: Union[
659
+ grpc.UnaryUnaryClientInterceptor,
660
+ grpc.UnaryStreamClientInterceptor,
661
+ grpc.StreamStreamClientInterceptor,
662
+ grpc.StreamUnaryClientInterceptor,
663
+ ]
664
+
665
+ def __init__(
666
+ self,
667
+ channel: grpc.Channel,
668
+ interceptor: Union[
669
+ grpc.UnaryUnaryClientInterceptor,
670
+ grpc.UnaryStreamClientInterceptor,
671
+ grpc.StreamStreamClientInterceptor,
672
+ grpc.StreamUnaryClientInterceptor,
673
+ ],
674
+ ):
675
+ self._channel = channel
676
+ self._interceptor = interceptor
677
+
678
+ def subscribe(
679
+ self, callback: Callable, try_to_connect: Optional[bool] = False
680
+ ):
681
+ self._channel.subscribe(callback, try_to_connect=try_to_connect)
682
+
683
+ def unsubscribe(self, callback: Callable):
684
+ self._channel.unsubscribe(callback)
685
+
686
+ # pylint: disable=arguments-differ
687
+ def unary_unary(
688
+ self,
689
+ method: str,
690
+ request_serializer: Optional[SerializingFunction] = None,
691
+ response_deserializer: Optional[DeserializingFunction] = None,
692
+ _registered_method: Optional[bool] = False,
693
+ ) -> grpc.UnaryUnaryMultiCallable:
694
+ # pytype: disable=wrong-arg-count
695
+ thunk = lambda m: self._channel.unary_unary(
696
+ m,
697
+ request_serializer,
698
+ response_deserializer,
699
+ _registered_method,
700
+ )
701
+ # pytype: enable=wrong-arg-count
702
+ if isinstance(self._interceptor, grpc.UnaryUnaryClientInterceptor):
703
+ return _UnaryUnaryMultiCallable(thunk, method, self._interceptor)
704
+ return thunk(method)
705
+
706
+ # pylint: disable=arguments-differ
707
+ def unary_stream(
708
+ self,
709
+ method: str,
710
+ request_serializer: Optional[SerializingFunction] = None,
711
+ response_deserializer: Optional[DeserializingFunction] = None,
712
+ _registered_method: Optional[bool] = False,
713
+ ) -> grpc.UnaryStreamMultiCallable:
714
+ # pytype: disable=wrong-arg-count
715
+ thunk = lambda m: self._channel.unary_stream(
716
+ m,
717
+ request_serializer,
718
+ response_deserializer,
719
+ _registered_method,
720
+ )
721
+ # pytype: enable=wrong-arg-count
722
+ if isinstance(self._interceptor, grpc.UnaryStreamClientInterceptor):
723
+ return _UnaryStreamMultiCallable(thunk, method, self._interceptor)
724
+ return thunk(method)
725
+
726
+ # pylint: disable=arguments-differ
727
+ def stream_unary(
728
+ self,
729
+ method: str,
730
+ request_serializer: Optional[SerializingFunction] = None,
731
+ response_deserializer: Optional[DeserializingFunction] = None,
732
+ _registered_method: Optional[bool] = False,
733
+ ) -> grpc.StreamUnaryMultiCallable:
734
+ # pytype: disable=wrong-arg-count
735
+ thunk = lambda m: self._channel.stream_unary(
736
+ m,
737
+ request_serializer,
738
+ response_deserializer,
739
+ _registered_method,
740
+ )
741
+ # pytype: enable=wrong-arg-count
742
+ if isinstance(self._interceptor, grpc.StreamUnaryClientInterceptor):
743
+ return _StreamUnaryMultiCallable(thunk, method, self._interceptor)
744
+ return thunk(method)
745
+
746
+ # pylint: disable=arguments-differ
747
+ def stream_stream(
748
+ self,
749
+ method: str,
750
+ request_serializer: Optional[SerializingFunction] = None,
751
+ response_deserializer: Optional[DeserializingFunction] = None,
752
+ _registered_method: Optional[bool] = False,
753
+ ) -> grpc.StreamStreamMultiCallable:
754
+ # pytype: disable=wrong-arg-count
755
+ thunk = lambda m: self._channel.stream_stream(
756
+ m,
757
+ request_serializer,
758
+ response_deserializer,
759
+ _registered_method,
760
+ )
761
+ # pytype: enable=wrong-arg-count
762
+ if isinstance(self._interceptor, grpc.StreamStreamClientInterceptor):
763
+ return _StreamStreamMultiCallable(thunk, method, self._interceptor)
764
+ return thunk(method)
765
+
766
+ def _close(self):
767
+ self._channel.close()
768
+
769
+ def __enter__(self):
770
+ return self
771
+
772
+ def __exit__(self, exc_type, exc_val, exc_tb):
773
+ self._close()
774
+ return False
775
+
776
+ def close(self):
777
+ self._channel.close()
778
+
779
+
780
+ def intercept_channel(
781
+ channel: grpc.Channel,
782
+ *interceptors: Optional[
783
+ Sequence[
784
+ Union[
785
+ grpc.UnaryUnaryClientInterceptor,
786
+ grpc.UnaryStreamClientInterceptor,
787
+ grpc.StreamStreamClientInterceptor,
788
+ grpc.StreamUnaryClientInterceptor,
789
+ ]
790
+ ]
791
+ ],
792
+ ) -> grpc.Channel:
793
+ for interceptor in reversed(list(interceptors)):
794
+ if (
795
+ not isinstance(interceptor, grpc.UnaryUnaryClientInterceptor)
796
+ and not isinstance(interceptor, grpc.UnaryStreamClientInterceptor)
797
+ and not isinstance(interceptor, grpc.StreamUnaryClientInterceptor)
798
+ and not isinstance(interceptor, grpc.StreamStreamClientInterceptor)
799
+ ):
800
+ error_msg = (
801
+ "interceptor must be "
802
+ "grpc.UnaryUnaryClientInterceptor or "
803
+ "grpc.UnaryStreamClientInterceptor or "
804
+ "grpc.StreamUnaryClientInterceptor or "
805
+ "grpc.StreamStreamClientInterceptor"
806
+ )
807
+ raise TypeError(error_msg)
808
+ channel = _Channel(channel, interceptor)
809
+ return channel
lib/python3.12/site-packages/grpc/_observability.py ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 (
22
+ Any,
23
+ Generator,
24
+ Generic,
25
+ List,
26
+ Optional,
27
+ Tuple,
28
+ TypeVar,
29
+ Union,
30
+ )
31
+
32
+ from grpc._cython import cygrpc as _cygrpc
33
+ from grpc._typing import ChannelArgumentType
34
+
35
+ _LOGGER = logging.getLogger(__name__)
36
+
37
+ _channel = Any # _channel.py imports this module.
38
+ ClientCallTracerCapsule = TypeVar("ClientCallTracerCapsule")
39
+ ServerCallTracerFactoryCapsule = TypeVar("ServerCallTracerFactoryCapsule")
40
+
41
+ _plugin_lock: threading.RLock = threading.RLock()
42
+ _OBSERVABILITY_PLUGIN: Optional["ObservabilityPlugin"] = None
43
+ _SERVICES_TO_EXCLUDE: List[bytes] = [
44
+ b"google.monitoring.v3.MetricService",
45
+ b"google.devtools.cloudtrace.v2.TraceService",
46
+ ]
47
+
48
+
49
+ class ServerCallTracerFactory:
50
+ """An encapsulation of a ServerCallTracerFactory.
51
+
52
+ Instances of this class can be passed to a Channel as values for the
53
+ grpc.experimental.server_call_tracer_factory option
54
+ """
55
+
56
+ def __init__(self, address):
57
+ self._address = address
58
+
59
+ def __int__(self):
60
+ return self._address
61
+
62
+
63
+ class ObservabilityPlugin(
64
+ Generic[ClientCallTracerCapsule, ServerCallTracerFactoryCapsule],
65
+ metaclass=abc.ABCMeta,
66
+ ):
67
+ """Abstract base class for observability plugin.
68
+
69
+ *This is a semi-private class that was intended for the exclusive use of
70
+ the gRPC team.*
71
+
72
+ The ClientCallTracerCapsule and ClientCallTracerCapsule created by this
73
+ plugin should be injected to gRPC core using observability_init at the
74
+ start of a program, before any channels/servers are built.
75
+
76
+ Any future methods added to this interface cannot have the
77
+ @abc.abstractmethod annotation.
78
+
79
+ Attributes:
80
+ _stats_enabled: A bool indicates whether tracing is enabled.
81
+ _tracing_enabled: A bool indicates whether stats(metrics) is enabled.
82
+ _registered_methods: A set which stores the registered method names in
83
+ bytes.
84
+ """
85
+
86
+ _tracing_enabled: bool = False
87
+ _stats_enabled: bool = False
88
+
89
+ @abc.abstractmethod
90
+ def create_client_call_tracer(
91
+ self, method_name: bytes, target: bytes
92
+ ) -> ClientCallTracerCapsule:
93
+ """Creates a ClientCallTracerCapsule.
94
+
95
+ After register the plugin, if tracing or stats is enabled, this method
96
+ will be called after a call was created, the ClientCallTracer created
97
+ by this method will be saved to call context.
98
+
99
+ The ClientCallTracer is an object which implements `grpc_core::ClientCallTracer`
100
+ interface and wrapped in a PyCapsule using `client_call_tracer` as name.
101
+
102
+ Args:
103
+ method_name: The method name of the call in byte format.
104
+ target: The channel target of the call in byte format.
105
+ registered_method: Whether this method is pre-registered.
106
+
107
+ Returns:
108
+ A PyCapsule which stores a ClientCallTracer object.
109
+ """
110
+ raise NotImplementedError()
111
+
112
+ @abc.abstractmethod
113
+ def save_trace_context(
114
+ self, trace_id: str, span_id: str, is_sampled: bool
115
+ ) -> None:
116
+ """Saves the trace_id and span_id related to the current span.
117
+
118
+ After register the plugin, if tracing is enabled, this method will be
119
+ called after the server finished sending response.
120
+
121
+ This method can be used to propagate census context.
122
+
123
+ Args:
124
+ trace_id: The identifier for the trace associated with the span as a
125
+ 32-character hexadecimal encoded string,
126
+ e.g. 26ed0036f2eff2b7317bccce3e28d01f
127
+ span_id: The identifier for the span as a 16-character hexadecimal encoded
128
+ string. e.g. 113ec879e62583bc
129
+ is_sampled: A bool indicates whether the span is sampled.
130
+ """
131
+ raise NotImplementedError()
132
+
133
+ @abc.abstractmethod
134
+ def create_server_call_tracer_factory(
135
+ self,
136
+ *,
137
+ xds: bool = False,
138
+ ) -> Optional[ServerCallTracerFactoryCapsule]:
139
+ """Creates a ServerCallTracerFactoryCapsule.
140
+
141
+ This method will be called at server initialization time to create a
142
+ ServerCallTracerFactory, which will be registered to gRPC core.
143
+
144
+ The ServerCallTracerFactory is an object which implements
145
+ `grpc_core::ServerCallTracerFactory` interface and wrapped in a PyCapsule
146
+ using `server_call_tracer_factory` as name.
147
+
148
+ Args:
149
+ xds: Whether the server is xds server.
150
+
151
+ Returns:
152
+ A PyCapsule which stores a ServerCallTracerFactory object. Or None if
153
+ plugin decides not to create ServerCallTracerFactory.
154
+ """
155
+ raise NotImplementedError()
156
+
157
+ @abc.abstractmethod
158
+ def record_rpc_latency(
159
+ self, method: str, target: str, rpc_latency: float, status_code: Any
160
+ ) -> None:
161
+ """Record the latency of the RPC.
162
+
163
+ After register the plugin, if stats is enabled, this method will be
164
+ called at the end of each RPC.
165
+
166
+ Args:
167
+ method: The fully-qualified name of the RPC method being invoked.
168
+ target: The target name of the RPC method being invoked.
169
+ rpc_latency: The latency for the RPC in seconds, equals to the time between
170
+ when the client invokes the RPC and when the client receives the status.
171
+ status_code: An element of grpc.StatusCode in string format representing the
172
+ final status for the RPC.
173
+ """
174
+ raise NotImplementedError()
175
+
176
+ def set_tracing(self, enable: bool) -> None:
177
+ """Enable or disable tracing.
178
+
179
+ Args:
180
+ enable: A bool indicates whether tracing should be enabled.
181
+ """
182
+ self._tracing_enabled = enable
183
+
184
+ def set_stats(self, enable: bool) -> None:
185
+ """Enable or disable stats(metrics).
186
+
187
+ Args:
188
+ enable: A bool indicates whether stats should be enabled.
189
+ """
190
+ self._stats_enabled = enable
191
+
192
+ def save_registered_method(self, method_name: bytes) -> None:
193
+ """Saves the method name to registered_method list.
194
+
195
+ When exporting metrics, method name for unregistered methods will be replaced
196
+ with 'other' by default.
197
+
198
+ Args:
199
+ method_name: The method name in bytes.
200
+ """
201
+ raise NotImplementedError()
202
+
203
+ @property
204
+ def tracing_enabled(self) -> bool:
205
+ return self._tracing_enabled
206
+
207
+ @property
208
+ def stats_enabled(self) -> bool:
209
+ return self._stats_enabled
210
+
211
+ @property
212
+ def observability_enabled(self) -> bool:
213
+ return self.tracing_enabled or self.stats_enabled
214
+
215
+
216
+ @contextlib.contextmanager
217
+ def get_plugin() -> Generator[Optional[ObservabilityPlugin], None, None]:
218
+ """Get the ObservabilityPlugin in _observability module.
219
+
220
+ Returns:
221
+ The ObservabilityPlugin currently registered with the _observability
222
+ module. Or None if no plugin exists at the time of calling this method.
223
+ """
224
+ with _plugin_lock:
225
+ yield _OBSERVABILITY_PLUGIN
226
+
227
+
228
+ def set_plugin(observability_plugin: Optional[ObservabilityPlugin]) -> None:
229
+ """Save ObservabilityPlugin to _observability module.
230
+
231
+ Args:
232
+ observability_plugin: The ObservabilityPlugin to save.
233
+
234
+ Raises:
235
+ ValueError: If an ObservabilityPlugin was already registered at the
236
+ time of calling this method.
237
+ """
238
+ global _OBSERVABILITY_PLUGIN # pylint: disable=global-statement # noqa: PLW0603
239
+ with _plugin_lock:
240
+ if observability_plugin and _OBSERVABILITY_PLUGIN:
241
+ error_msg = "observability_plugin was already set!"
242
+ raise ValueError(error_msg)
243
+ _OBSERVABILITY_PLUGIN = observability_plugin
244
+
245
+
246
+ def observability_init(observability_plugin: ObservabilityPlugin) -> None:
247
+ """Initialize observability with provided ObservabilityPlugin.
248
+
249
+ This method have to be called at the start of a program, before any
250
+ channels/servers are built.
251
+
252
+ Args:
253
+ observability_plugin: The ObservabilityPlugin to use.
254
+
255
+ Raises:
256
+ ValueError: If an ObservabilityPlugin was already registered at the
257
+ time of calling this method.
258
+ """
259
+ set_plugin(observability_plugin)
260
+
261
+
262
+ def observability_deinit() -> None:
263
+ """Clear the observability context, including ObservabilityPlugin and
264
+ ServerCallTracerFactory
265
+
266
+ This method have to be called after exit observability context so that
267
+ it's possible to re-initialize again.
268
+ """
269
+ set_plugin(None)
270
+ _cygrpc.clear_server_call_tracer_factory()
271
+
272
+
273
+ def maybe_record_rpc_latency(state: "_channel._RPCState") -> None:
274
+ """Record the latency of the RPC, if the plugin is registered and stats is enabled.
275
+
276
+ This method will be called at the end of each RPC.
277
+
278
+ Args:
279
+ state: a grpc._channel._RPCState object which contains the stats related to the
280
+ RPC.
281
+ """
282
+ # TODO(xuanwn): use channel args to exclude those metrics.
283
+ for exclude_prefix in _SERVICES_TO_EXCLUDE:
284
+ if exclude_prefix in state.method.encode("utf8"):
285
+ return
286
+ with get_plugin() as plugin:
287
+ if plugin and plugin.stats_enabled:
288
+ rpc_latency_s = state.rpc_end_time - state.rpc_start_time
289
+ rpc_latency_ms = rpc_latency_s * 1000
290
+ plugin.record_rpc_latency(
291
+ state.method, state.target, rpc_latency_ms, state.code
292
+ )
293
+
294
+
295
+ def create_server_call_tracer_factory_option(
296
+ xds: bool,
297
+ ) -> Union[Tuple[ChannelArgumentType], Tuple[()]]:
298
+ with get_plugin() as plugin:
299
+ if plugin and plugin.stats_enabled:
300
+ server_call_tracer_factory_address = (
301
+ _cygrpc.get_server_call_tracer_factory_address(plugin, xds)
302
+ )
303
+ if server_call_tracer_factory_address:
304
+ return (
305
+ (
306
+ "grpc.experimental.server_call_tracer_factory",
307
+ ServerCallTracerFactory(
308
+ server_call_tracer_factory_address
309
+ ),
310
+ ),
311
+ )
312
+ return ()
lib/python3.12/site-packages/grpc/_plugin_wrapping.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ error_msg = (
63
+ "AuthMetadataPluginCallback invoked more than once!"
64
+ )
65
+ raise RuntimeError(error_msg)
66
+ else:
67
+ self._state.called = True
68
+ else:
69
+ error_msg = (
70
+ "AuthMetadataPluginCallback"
71
+ 'raised exception "{self._state.exception}"!'
72
+ )
73
+ raise RuntimeError(error_msg)
74
+ if error is None:
75
+ self._callback(metadata, cygrpc.StatusCode.ok, None)
76
+ else:
77
+ self._callback(
78
+ None, cygrpc.StatusCode.internal, _common.encode(str(error))
79
+ )
80
+
81
+
82
+ class _Plugin(object):
83
+ _metadata_plugin: grpc.AuthMetadataPlugin
84
+
85
+ def __init__(self, metadata_plugin: grpc.AuthMetadataPlugin):
86
+ self._metadata_plugin = metadata_plugin
87
+ self._stored_ctx = None
88
+
89
+ try:
90
+ import contextvars # pylint: disable=wrong-import-position
91
+
92
+ # The plugin may be invoked on a thread created by Core, which will not
93
+ # have the context propagated. This context is stored and installed in
94
+ # the thread invoking the plugin.
95
+ self._stored_ctx = contextvars.copy_context()
96
+ except ImportError:
97
+ # Support versions predating contextvars.
98
+ pass
99
+
100
+ def __call__(self, service_url: str, method_name: str, callback: Callable):
101
+ context = _AuthMetadataContext(
102
+ _common.decode(service_url), _common.decode(method_name)
103
+ )
104
+ callback_state = _CallbackState()
105
+ try:
106
+ self._metadata_plugin(
107
+ context, _AuthMetadataPluginCallback(callback_state, callback)
108
+ )
109
+ except Exception as exception: # pylint: disable=broad-except
110
+ _LOGGER.exception(
111
+ 'AuthMetadataPluginCallback "%s" raised exception!',
112
+ self._metadata_plugin,
113
+ )
114
+ with callback_state.lock:
115
+ callback_state.exception = exception
116
+ if callback_state.called:
117
+ return
118
+ callback(
119
+ None, cygrpc.StatusCode.internal, _common.encode(str(exception))
120
+ )
121
+
122
+
123
+ def metadata_plugin_call_credentials(
124
+ metadata_plugin: grpc.AuthMetadataPlugin, name: Optional[str]
125
+ ) -> grpc.CallCredentials:
126
+ if name is None:
127
+ try:
128
+ effective_name = metadata_plugin.__name__
129
+ except AttributeError:
130
+ effective_name = metadata_plugin.__class__.__name__
131
+ else:
132
+ effective_name = name
133
+ return grpc.CallCredentials(
134
+ cygrpc.MetadataPluginCallCredentials(
135
+ _Plugin(metadata_plugin), _common.encode(effective_name)
136
+ )
137
+ )
lib/python3.12/site-packages/grpc/_runtime_protos.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ raise NotImplementedError(_UNINSTALLED_TEMPLATE.format(fn_name))
70
+
71
+
72
+ def protos(protobuf_path): # pylint: disable=unused-argument
73
+ """Returns a module generated by the indicated .proto file.
74
+
75
+ THIS IS AN EXPERIMENTAL API.
76
+
77
+ Use this function to retrieve classes corresponding to message
78
+ definitions in the .proto file.
79
+
80
+ To inspect the contents of the returned module, use the dir function.
81
+ For example:
82
+
83
+ ```
84
+ protos = grpc.protos("foo.proto")
85
+ print(dir(protos))
86
+ ```
87
+
88
+ The returned module object corresponds to the _pb2.py file generated
89
+ by protoc. The path is expected to be relative to an entry on sys.path
90
+ and all transitive dependencies of the file should also be resolvable
91
+ from an entry on sys.path.
92
+
93
+ To completely disable the machinery behind this function, set the
94
+ GRPC_PYTHON_DISABLE_DYNAMIC_STUBS environment variable to "true".
95
+
96
+ Args:
97
+ protobuf_path: The path to the .proto file on the filesystem. This path
98
+ must be resolvable from an entry on sys.path and so must all of its
99
+ transitive dependencies.
100
+
101
+ Returns:
102
+ A module object corresponding to the message code for the indicated
103
+ .proto file. Equivalent to a generated _pb2.py file.
104
+ """
105
+ return _call_with_lazy_import("protos", protobuf_path)
106
+
107
+
108
+ def services(protobuf_path): # pylint: disable=unused-argument
109
+ """Returns a module generated by the indicated .proto file.
110
+
111
+ THIS IS AN EXPERIMENTAL API.
112
+
113
+ Use this function to retrieve classes and functions corresponding to
114
+ service definitions in the .proto file, including both stub and servicer
115
+ definitions.
116
+
117
+ To inspect the contents of the returned module, use the dir function.
118
+ For example:
119
+
120
+ ```
121
+ services = grpc.services("foo.proto")
122
+ print(dir(services))
123
+ ```
124
+
125
+ The returned module object corresponds to the _pb2_grpc.py file generated
126
+ by protoc. The path is expected to be relative to an entry on sys.path
127
+ and all transitive dependencies of the file should also be resolvable
128
+ from an entry on sys.path.
129
+
130
+ To completely disable the machinery behind this function, set the
131
+ GRPC_PYTHON_DISABLE_DYNAMIC_STUBS environment variable to "true".
132
+
133
+ Args:
134
+ protobuf_path: The path to the .proto file on the filesystem. This path
135
+ must be resolvable from an entry on sys.path and so must all of its
136
+ transitive dependencies.
137
+
138
+ Returns:
139
+ A module object corresponding to the stub/service code for the indicated
140
+ .proto file. Equivalent to a generated _pb2_grpc.py file.
141
+ """
142
+ return _call_with_lazy_import("services", protobuf_path)
143
+
144
+
145
+ def protos_and_services(protobuf_path): # pylint: disable=unused-argument
146
+ """Returns a 2-tuple of modules corresponding to protos and services.
147
+
148
+ THIS IS AN EXPERIMENTAL API.
149
+
150
+ The return value of this function is equivalent to a call to protos and a
151
+ call to services.
152
+
153
+ To completely disable the machinery behind this function, set the
154
+ GRPC_PYTHON_DISABLE_DYNAMIC_STUBS environment variable to "true".
155
+
156
+ Args:
157
+ protobuf_path: The path to the .proto file on the filesystem. This path
158
+ must be resolvable from an entry on sys.path and so must all of its
159
+ transitive dependencies.
160
+
161
+ Returns:
162
+ A 2-tuple of module objects corresponding to (protos(path), services(path)).
163
+ """
164
+ return _call_with_lazy_import("protos_and_services", protobuf_path)
lib/python3.12/site-packages/grpc/_server.py ADDED
@@ -0,0 +1,1516 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from typing_extensions import override
58
+
59
+ _LOGGER = logging.getLogger(__name__)
60
+
61
+ _SHUTDOWN_TAG = "shutdown"
62
+ _REQUEST_CALL_TAG = "request_call"
63
+
64
+ _RECEIVE_CLOSE_ON_SERVER_TOKEN = "receive_close_on_server"
65
+ _SEND_INITIAL_METADATA_TOKEN = "send_initial_metadata"
66
+ _RECEIVE_MESSAGE_TOKEN = "receive_message"
67
+ _SEND_MESSAGE_TOKEN = "send_message"
68
+ _SEND_INITIAL_METADATA_AND_SEND_MESSAGE_TOKEN = (
69
+ "send_initial_metadata * send_message"
70
+ )
71
+ _SEND_STATUS_FROM_SERVER_TOKEN = "send_status_from_server"
72
+ _SEND_INITIAL_METADATA_AND_SEND_STATUS_FROM_SERVER_TOKEN = (
73
+ "send_initial_metadata * send_status_from_server"
74
+ )
75
+
76
+ _OPEN = "open"
77
+ _CLOSED = "closed"
78
+ _CANCELLED = "cancelled"
79
+
80
+ _EMPTY_FLAGS = 0
81
+
82
+ _DEALLOCATED_SERVER_CHECK_PERIOD_S = 1.0
83
+ _INF_TIMEOUT = 1e9
84
+
85
+
86
+ def _serialized_request(request_event: cygrpc.BaseEvent) -> bytes:
87
+ return request_event.batch_operations[0].message()
88
+
89
+
90
+ def _application_code(code: grpc.StatusCode) -> cygrpc.StatusCode:
91
+ cygrpc_code = _common.STATUS_CODE_TO_CYGRPC_STATUS_CODE.get(code)
92
+ return cygrpc.StatusCode.unknown if cygrpc_code is None else cygrpc_code
93
+
94
+
95
+ def _completion_code(state: _RPCState) -> cygrpc.StatusCode:
96
+ if state.code is None:
97
+ return cygrpc.StatusCode.ok
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
+ return _application_code(state.code)
107
+
108
+
109
+ def _details(state: _RPCState) -> bytes:
110
+ return b"" if state.details is None else state.details
111
+
112
+
113
+ class _HandlerCallDetails(
114
+ collections.namedtuple(
115
+ "_HandlerCallDetails",
116
+ (
117
+ "method",
118
+ "invocation_metadata",
119
+ ),
120
+ ),
121
+ grpc.HandlerCallDetails,
122
+ ):
123
+ pass
124
+
125
+
126
+ class _Method(abc.ABC):
127
+ @abc.abstractmethod
128
+ def name(self) -> Optional[str]:
129
+ raise NotImplementedError()
130
+
131
+ @abc.abstractmethod
132
+ def handler(
133
+ self, handler_call_details: _HandlerCallDetails
134
+ ) -> Optional[grpc.RpcMethodHandler]:
135
+ raise NotImplementedError()
136
+
137
+
138
+ class _RegisteredMethod(_Method):
139
+ def __init__(
140
+ self,
141
+ name: str,
142
+ registered_handler: Optional[grpc.RpcMethodHandler],
143
+ ):
144
+ self._name = name
145
+ self._registered_handler = registered_handler
146
+
147
+ @override
148
+ def name(self) -> Optional[str]:
149
+ return self._name
150
+
151
+ @override
152
+ def handler(
153
+ self, handler_call_details: _HandlerCallDetails
154
+ ) -> Optional[grpc.RpcMethodHandler]:
155
+ return self._registered_handler
156
+
157
+
158
+ class _GenericMethod(_Method):
159
+ def __init__(
160
+ self,
161
+ generic_handlers: List[grpc.GenericRpcHandler],
162
+ ):
163
+ self._generic_handlers = generic_handlers
164
+
165
+ @override
166
+ def name(self) -> Optional[str]:
167
+ return None
168
+
169
+ @override
170
+ def handler(
171
+ self, handler_call_details: _HandlerCallDetails
172
+ ) -> Optional[grpc.RpcMethodHandler]:
173
+ # If the same method have both generic and registered handler,
174
+ # registered handler will take precedence.
175
+ for generic_handler in self._generic_handlers:
176
+ method_handler = generic_handler.service(handler_call_details)
177
+ if method_handler is not None:
178
+ return method_handler
179
+ return None
180
+
181
+
182
+ class _RPCState(object):
183
+ context: contextvars.Context
184
+ condition: threading.Condition
185
+ due = Set[str]
186
+ request: Any
187
+ client: str
188
+ initial_metadata_allowed: bool
189
+ compression_algorithm: Optional[grpc.Compression]
190
+ disable_next_compression: bool
191
+ trailing_metadata: Optional[MetadataType]
192
+ code: Optional[grpc.StatusCode]
193
+ details: Optional[bytes]
194
+ statused: bool
195
+ rpc_errors: List[Exception]
196
+ callbacks: Optional[List[NullaryCallbackType]]
197
+ aborted: bool
198
+
199
+ def __init__(self):
200
+ self.context = contextvars.Context()
201
+ self.condition = threading.Condition()
202
+ self.due = set()
203
+ self.request = None
204
+ self.client = _OPEN
205
+ self.initial_metadata_allowed = True
206
+ self.compression_algorithm = None
207
+ self.disable_next_compression = False
208
+ self.trailing_metadata = None
209
+ self.code = None
210
+ self.details = None
211
+ self.statused = False
212
+ self.rpc_errors = []
213
+ self.callbacks = []
214
+ self.aborted = False
215
+
216
+
217
+ def _raise_rpc_error(state: _RPCState) -> None:
218
+ rpc_error = grpc.RpcError()
219
+ state.rpc_errors.append(rpc_error)
220
+ raise rpc_error
221
+
222
+
223
+ def _possibly_finish_call(
224
+ state: _RPCState, token: str
225
+ ) -> ServerTagCallbackType:
226
+ state.due.remove(token)
227
+ if not _is_rpc_state_active(state) and not state.due:
228
+ callbacks = state.callbacks
229
+ state.callbacks = None
230
+ return state, callbacks
231
+ return None, ()
232
+
233
+
234
+ def _send_status_from_server(state: _RPCState, token: str) -> ServerCallbackTag:
235
+ def send_status_from_server(unused_send_status_from_server_event):
236
+ with state.condition:
237
+ return _possibly_finish_call(state, token)
238
+
239
+ return send_status_from_server
240
+
241
+
242
+ def _get_initial_metadata(
243
+ state: _RPCState, metadata: Optional[MetadataType]
244
+ ) -> Optional[MetadataType]:
245
+ with state.condition:
246
+ if state.compression_algorithm:
247
+ compression_metadata = (
248
+ _compression.compression_algorithm_to_metadata(
249
+ state.compression_algorithm
250
+ ),
251
+ )
252
+ if metadata is None:
253
+ return compression_metadata
254
+ return compression_metadata + tuple(metadata)
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
+ self._state.callbacks.append(callback)
394
+ return True
395
+
396
+ def disable_next_message_compression(self) -> None:
397
+ with self._state.condition:
398
+ self._state.disable_next_compression = True
399
+
400
+ def invocation_metadata(self) -> Optional[MetadataType]:
401
+ return self._rpc_event.invocation_metadata
402
+
403
+ def peer(self) -> str:
404
+ return _common.decode(self._rpc_event.call.peer())
405
+
406
+ def peer_identities(self) -> Optional[Sequence[bytes]]:
407
+ return cygrpc.peer_identities(self._rpc_event.call)
408
+
409
+ def peer_identity_key(self) -> Optional[str]:
410
+ id_key = cygrpc.peer_identity_key(self._rpc_event.call)
411
+ return id_key if id_key is None else _common.decode(id_key)
412
+
413
+ def auth_context(self) -> Mapping[str, Sequence[bytes]]:
414
+ auth_context = cygrpc.auth_context(self._rpc_event.call)
415
+ auth_context_dict = {} if auth_context is None else auth_context
416
+ return {
417
+ _common.decode(key): value
418
+ for key, value in auth_context_dict.items()
419
+ }
420
+
421
+ def set_compression(self, compression: grpc.Compression) -> None:
422
+ with self._state.condition:
423
+ self._state.compression_algorithm = compression
424
+
425
+ def send_initial_metadata(self, initial_metadata: MetadataType) -> None:
426
+ with self._state.condition:
427
+ if self._state.client is _CANCELLED:
428
+ _raise_rpc_error(self._state)
429
+ if self._state.initial_metadata_allowed:
430
+ operation = _get_initial_metadata_operation(
431
+ self._state, initial_metadata
432
+ )
433
+ self._rpc_event.call.start_server_batch(
434
+ (operation,), _send_initial_metadata(self._state)
435
+ )
436
+ self._state.initial_metadata_allowed = False
437
+ self._state.due.add(_SEND_INITIAL_METADATA_TOKEN)
438
+ else:
439
+ error_msg = "Initial metadata no longer allowed!"
440
+ raise ValueError(error_msg)
441
+
442
+ def set_trailing_metadata(self, trailing_metadata: MetadataType) -> None:
443
+ with self._state.condition:
444
+ self._state.trailing_metadata = trailing_metadata
445
+
446
+ def trailing_metadata(self) -> Optional[MetadataType]:
447
+ return self._state.trailing_metadata
448
+
449
+ def abort(self, code: grpc.StatusCode, details: str) -> None:
450
+ # treat OK like other invalid arguments: fail the RPC
451
+ if code == grpc.StatusCode.OK:
452
+ _LOGGER.error(
453
+ "abort() called with StatusCode.OK; returning UNKNOWN"
454
+ )
455
+ code = grpc.StatusCode.UNKNOWN
456
+ details = ""
457
+ with self._state.condition:
458
+ self._state.code = code
459
+ self._state.details = _common.encode(details)
460
+ self._state.aborted = True
461
+ raise Exception()
462
+
463
+ def abort_with_status(self, status: grpc.Status) -> None:
464
+ self._state.trailing_metadata = status.trailing_metadata
465
+ self.abort(status.code, status.details)
466
+
467
+ def set_code(self, code: grpc.StatusCode) -> None:
468
+ with self._state.condition:
469
+ self._state.code = code
470
+
471
+ def code(self) -> grpc.StatusCode:
472
+ return self._state.code
473
+
474
+ def set_details(self, details: str) -> None:
475
+ with self._state.condition:
476
+ self._state.details = _common.encode(details)
477
+
478
+ def details(self) -> bytes:
479
+ return self._state.details
480
+
481
+ def _finalize_state(self) -> None:
482
+ pass
483
+
484
+
485
+ class _RequestIterator(object):
486
+ _state: _RPCState
487
+ _call: cygrpc.Call
488
+ _request_deserializer: Optional[DeserializingFunction]
489
+
490
+ def __init__(
491
+ self,
492
+ state: _RPCState,
493
+ call: cygrpc.Call,
494
+ request_deserializer: Optional[DeserializingFunction],
495
+ ):
496
+ self._state = state
497
+ self._call = call
498
+ self._request_deserializer = request_deserializer
499
+
500
+ def _raise_or_start_receive_message(self) -> None:
501
+ if self._state.client is _CANCELLED:
502
+ _raise_rpc_error(self._state)
503
+ elif not _is_rpc_state_active(self._state):
504
+ raise StopIteration()
505
+ else:
506
+ self._call.start_server_batch(
507
+ (cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),),
508
+ _receive_message(
509
+ self._state, self._call, self._request_deserializer
510
+ ),
511
+ )
512
+ self._state.due.add(_RECEIVE_MESSAGE_TOKEN)
513
+
514
+ def _look_for_request(self) -> Any:
515
+ if self._state.client is _CANCELLED:
516
+ _raise_rpc_error(self._state)
517
+ elif (
518
+ self._state.request is None
519
+ and _RECEIVE_MESSAGE_TOKEN not in self._state.due
520
+ ):
521
+ raise StopIteration()
522
+ else:
523
+ request = self._state.request
524
+ self._state.request = None
525
+ return request
526
+
527
+ raise AssertionError() # should never run
528
+
529
+ def _next(self) -> Any:
530
+ with self._state.condition:
531
+ self._raise_or_start_receive_message()
532
+ while True:
533
+ self._state.condition.wait()
534
+ request = self._look_for_request()
535
+ if request is not None:
536
+ return request
537
+
538
+ def __iter__(self) -> _RequestIterator:
539
+ return self
540
+
541
+ def __next__(self) -> Any:
542
+ return self._next()
543
+
544
+ def next(self) -> Any:
545
+ return self._next()
546
+
547
+
548
+ def _unary_request(
549
+ rpc_event: cygrpc.BaseEvent,
550
+ state: _RPCState,
551
+ request_deserializer: Optional[DeserializingFunction],
552
+ ) -> Callable[[], Any]:
553
+ def unary_request():
554
+ with state.condition:
555
+ if not _is_rpc_state_active(state):
556
+ return None
557
+ rpc_event.call.start_server_batch(
558
+ (cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),),
559
+ _receive_message(state, rpc_event.call, request_deserializer),
560
+ )
561
+ state.due.add(_RECEIVE_MESSAGE_TOKEN)
562
+ while True:
563
+ state.condition.wait()
564
+ if state.request is None:
565
+ if state.client is _CLOSED:
566
+ details = (
567
+ '"{}" requires exactly one request message.'.format(
568
+ rpc_event.call_details.method
569
+ )
570
+ )
571
+ _abort(
572
+ state,
573
+ rpc_event.call,
574
+ cygrpc.StatusCode.unimplemented,
575
+ _common.encode(details),
576
+ )
577
+ return None
578
+ if state.client is _CANCELLED:
579
+ return None
580
+ else:
581
+ request = state.request
582
+ state.request = None
583
+ return request
584
+
585
+ return unary_request
586
+
587
+
588
+ def _call_behavior(
589
+ rpc_event: cygrpc.BaseEvent,
590
+ state: _RPCState,
591
+ behavior: ArityAgnosticMethodHandler,
592
+ argument: Any,
593
+ request_deserializer: Optional[DeserializingFunction],
594
+ send_response_callback: Optional[Callable[[ResponseType], None]] = None,
595
+ ) -> Tuple[Union[ResponseType, Iterator[ResponseType]], bool]:
596
+ from grpc import _create_servicer_context # pytype: disable=pyi-error
597
+
598
+ with _create_servicer_context(
599
+ rpc_event, state, request_deserializer
600
+ ) as context:
601
+ try:
602
+ response_or_iterator = None
603
+ if send_response_callback is not None:
604
+ response_or_iterator = behavior(
605
+ argument, context, send_response_callback
606
+ )
607
+ else:
608
+ response_or_iterator = behavior(argument, context)
609
+ return response_or_iterator, True
610
+ except Exception as exception: # pylint: disable=broad-except
611
+ with state.condition:
612
+ if state.aborted:
613
+ _abort(
614
+ state,
615
+ rpc_event.call,
616
+ cygrpc.StatusCode.unknown,
617
+ b"RPC Aborted",
618
+ )
619
+ elif exception not in state.rpc_errors:
620
+ try:
621
+ details = "Exception calling application: {}".format(
622
+ exception
623
+ )
624
+ except Exception: # pylint: disable=broad-except
625
+ details = (
626
+ "Calling application raised unprintable Exception!"
627
+ )
628
+ _LOGGER.exception(
629
+ traceback.format_exception(
630
+ type(exception),
631
+ exception,
632
+ exception.__traceback__,
633
+ )
634
+ )
635
+ traceback.print_exc()
636
+ _LOGGER.exception(details)
637
+ _abort(
638
+ state,
639
+ rpc_event.call,
640
+ cygrpc.StatusCode.unknown,
641
+ _common.encode(details),
642
+ )
643
+ return None, False
644
+
645
+
646
+ def _take_response_from_response_iterator(
647
+ rpc_event: cygrpc.BaseEvent,
648
+ state: _RPCState,
649
+ response_iterator: Iterator[ResponseType],
650
+ ) -> Tuple[ResponseType, bool]:
651
+ try:
652
+ return next(response_iterator), True
653
+ except StopIteration:
654
+ return None, True
655
+ except Exception as exception: # pylint: disable=broad-except
656
+ with state.condition:
657
+ if state.aborted:
658
+ _abort(
659
+ state,
660
+ rpc_event.call,
661
+ cygrpc.StatusCode.unknown,
662
+ b"RPC Aborted",
663
+ )
664
+ elif exception not in state.rpc_errors:
665
+ details = "Exception iterating responses: {}".format(exception)
666
+ _LOGGER.exception(details)
667
+ _abort(
668
+ state,
669
+ rpc_event.call,
670
+ cygrpc.StatusCode.unknown,
671
+ _common.encode(details),
672
+ )
673
+ return None, False
674
+
675
+
676
+ def _serialize_response(
677
+ rpc_event: cygrpc.BaseEvent,
678
+ state: _RPCState,
679
+ response: Any,
680
+ response_serializer: Optional[SerializingFunction],
681
+ ) -> Optional[bytes]:
682
+ serialized_response = _common.serialize(response, response_serializer)
683
+ if serialized_response is None:
684
+ with state.condition:
685
+ _abort(
686
+ state,
687
+ rpc_event.call,
688
+ cygrpc.StatusCode.internal,
689
+ b"Failed to serialize response!",
690
+ )
691
+ return None
692
+ return serialized_response
693
+
694
+
695
+ def _get_send_message_op_flags_from_state(
696
+ state: _RPCState,
697
+ ) -> Union[int, cygrpc.WriteFlag]:
698
+ if state.disable_next_compression:
699
+ return cygrpc.WriteFlag.no_compress
700
+ return _EMPTY_FLAGS
701
+
702
+
703
+ def _reset_per_message_state(state: _RPCState) -> None:
704
+ with state.condition:
705
+ state.disable_next_compression = False
706
+
707
+
708
+ def _send_response(
709
+ rpc_event: cygrpc.BaseEvent, state: _RPCState, serialized_response: bytes
710
+ ) -> bool:
711
+ with state.condition:
712
+ if not _is_rpc_state_active(state):
713
+ return False
714
+ if state.initial_metadata_allowed:
715
+ operations = (
716
+ _get_initial_metadata_operation(state, None),
717
+ cygrpc.SendMessageOperation(
718
+ serialized_response,
719
+ _get_send_message_op_flags_from_state(state),
720
+ ),
721
+ )
722
+ state.initial_metadata_allowed = False
723
+ token = _SEND_INITIAL_METADATA_AND_SEND_MESSAGE_TOKEN
724
+ else:
725
+ operations = (
726
+ cygrpc.SendMessageOperation(
727
+ serialized_response,
728
+ _get_send_message_op_flags_from_state(state),
729
+ ),
730
+ )
731
+ token = _SEND_MESSAGE_TOKEN
732
+ rpc_event.call.start_server_batch(
733
+ operations, _send_message(state, token)
734
+ )
735
+ state.due.add(token)
736
+ _reset_per_message_state(state)
737
+ while True:
738
+ state.condition.wait()
739
+ if token not in state.due:
740
+ return _is_rpc_state_active(state)
741
+
742
+
743
+ def _status(
744
+ rpc_event: cygrpc.BaseEvent,
745
+ state: _RPCState,
746
+ serialized_response: Optional[bytes],
747
+ ) -> None:
748
+ with state.condition:
749
+ if state.client is not _CANCELLED:
750
+ code = _completion_code(state)
751
+ details = _details(state)
752
+ operations = [
753
+ cygrpc.SendStatusFromServerOperation(
754
+ state.trailing_metadata, code, details, _EMPTY_FLAGS
755
+ ),
756
+ ]
757
+ if state.initial_metadata_allowed:
758
+ operations.append(_get_initial_metadata_operation(state, None))
759
+ if serialized_response is not None:
760
+ operations.append(
761
+ cygrpc.SendMessageOperation(
762
+ serialized_response,
763
+ _get_send_message_op_flags_from_state(state),
764
+ )
765
+ )
766
+ rpc_event.call.start_server_batch(
767
+ operations,
768
+ _send_status_from_server(state, _SEND_STATUS_FROM_SERVER_TOKEN),
769
+ )
770
+ state.statused = True
771
+ _reset_per_message_state(state)
772
+ state.due.add(_SEND_STATUS_FROM_SERVER_TOKEN)
773
+
774
+
775
+ def _unary_response_in_pool(
776
+ rpc_event: cygrpc.BaseEvent,
777
+ state: _RPCState,
778
+ behavior: ArityAgnosticMethodHandler,
779
+ argument_thunk: Callable[[], Any],
780
+ request_deserializer: Optional[SerializingFunction],
781
+ response_serializer: Optional[SerializingFunction],
782
+ ) -> None:
783
+ cygrpc.install_context_from_request_call_event(rpc_event)
784
+
785
+ try:
786
+ argument = argument_thunk()
787
+ if argument is not None:
788
+ response, proceed = _call_behavior(
789
+ rpc_event, state, behavior, argument, request_deserializer
790
+ )
791
+ if proceed:
792
+ serialized_response = _serialize_response(
793
+ rpc_event, state, response, response_serializer
794
+ )
795
+ if serialized_response is not None:
796
+ _status(rpc_event, state, serialized_response)
797
+ except Exception: # pylint: disable=broad-except
798
+ traceback.print_exc()
799
+ finally:
800
+ cygrpc.uninstall_context()
801
+
802
+
803
+ def _stream_response_in_pool(
804
+ rpc_event: cygrpc.BaseEvent,
805
+ state: _RPCState,
806
+ behavior: ArityAgnosticMethodHandler,
807
+ argument_thunk: Callable[[], Any],
808
+ request_deserializer: Optional[DeserializingFunction],
809
+ response_serializer: Optional[SerializingFunction],
810
+ ) -> None:
811
+ cygrpc.install_context_from_request_call_event(rpc_event)
812
+
813
+ def send_response(response: Any) -> None:
814
+ if response is None:
815
+ _status(rpc_event, state, None)
816
+ else:
817
+ serialized_response = _serialize_response(
818
+ rpc_event, state, response, response_serializer
819
+ )
820
+ if serialized_response is not None:
821
+ _send_response(rpc_event, state, serialized_response)
822
+
823
+ try:
824
+ argument = argument_thunk()
825
+ if argument is not None:
826
+ if (
827
+ hasattr(behavior, "experimental_non_blocking")
828
+ and behavior.experimental_non_blocking
829
+ ):
830
+ _call_behavior(
831
+ rpc_event,
832
+ state,
833
+ behavior,
834
+ argument,
835
+ request_deserializer,
836
+ send_response_callback=send_response,
837
+ )
838
+ else:
839
+ response_iterator, proceed = _call_behavior(
840
+ rpc_event, state, behavior, argument, request_deserializer
841
+ )
842
+ if proceed:
843
+ _send_message_callback_to_blocking_iterator_adapter(
844
+ rpc_event, state, send_response, response_iterator
845
+ )
846
+ except Exception: # pylint: disable=broad-except
847
+ traceback.print_exc()
848
+ finally:
849
+ cygrpc.uninstall_context()
850
+
851
+
852
+ def _is_rpc_state_active(state: _RPCState) -> bool:
853
+ return state.client is not _CANCELLED and not state.statused
854
+
855
+
856
+ def _send_message_callback_to_blocking_iterator_adapter(
857
+ rpc_event: cygrpc.BaseEvent,
858
+ state: _RPCState,
859
+ send_response_callback: Callable[[ResponseType], None],
860
+ response_iterator: Iterator[ResponseType],
861
+ ) -> None:
862
+ while True:
863
+ response, proceed = _take_response_from_response_iterator(
864
+ rpc_event, state, response_iterator
865
+ )
866
+ if proceed:
867
+ send_response_callback(response)
868
+ if not _is_rpc_state_active(state):
869
+ break
870
+ else:
871
+ break
872
+
873
+
874
+ def _select_thread_pool_for_behavior(
875
+ behavior: ArityAgnosticMethodHandler,
876
+ default_thread_pool: futures.ThreadPoolExecutor,
877
+ ) -> futures.ThreadPoolExecutor:
878
+ if hasattr(behavior, "experimental_thread_pool") and isinstance(
879
+ behavior.experimental_thread_pool, futures.ThreadPoolExecutor
880
+ ):
881
+ return behavior.experimental_thread_pool
882
+ return default_thread_pool
883
+
884
+
885
+ def _handle_unary_unary(
886
+ rpc_event: cygrpc.BaseEvent,
887
+ state: _RPCState,
888
+ method_handler: grpc.RpcMethodHandler,
889
+ default_thread_pool: futures.ThreadPoolExecutor,
890
+ ) -> futures.Future:
891
+ unary_request = _unary_request(
892
+ rpc_event, state, method_handler.request_deserializer
893
+ )
894
+ thread_pool = _select_thread_pool_for_behavior(
895
+ method_handler.unary_unary, default_thread_pool
896
+ )
897
+ return thread_pool.submit(
898
+ state.context.run,
899
+ _unary_response_in_pool,
900
+ rpc_event,
901
+ state,
902
+ method_handler.unary_unary,
903
+ unary_request,
904
+ method_handler.request_deserializer,
905
+ method_handler.response_serializer,
906
+ )
907
+
908
+
909
+ def _handle_unary_stream(
910
+ rpc_event: cygrpc.BaseEvent,
911
+ state: _RPCState,
912
+ method_handler: grpc.RpcMethodHandler,
913
+ default_thread_pool: futures.ThreadPoolExecutor,
914
+ ) -> futures.Future:
915
+ unary_request = _unary_request(
916
+ rpc_event, state, method_handler.request_deserializer
917
+ )
918
+ thread_pool = _select_thread_pool_for_behavior(
919
+ method_handler.unary_stream, default_thread_pool
920
+ )
921
+ return thread_pool.submit(
922
+ state.context.run,
923
+ _stream_response_in_pool,
924
+ rpc_event,
925
+ state,
926
+ method_handler.unary_stream,
927
+ unary_request,
928
+ method_handler.request_deserializer,
929
+ method_handler.response_serializer,
930
+ )
931
+
932
+
933
+ def _handle_stream_unary(
934
+ rpc_event: cygrpc.BaseEvent,
935
+ state: _RPCState,
936
+ method_handler: grpc.RpcMethodHandler,
937
+ default_thread_pool: futures.ThreadPoolExecutor,
938
+ ) -> futures.Future:
939
+ request_iterator = _RequestIterator(
940
+ state, rpc_event.call, method_handler.request_deserializer
941
+ )
942
+ thread_pool = _select_thread_pool_for_behavior(
943
+ method_handler.stream_unary, default_thread_pool
944
+ )
945
+ return thread_pool.submit(
946
+ state.context.run,
947
+ _unary_response_in_pool,
948
+ rpc_event,
949
+ state,
950
+ method_handler.stream_unary,
951
+ lambda: request_iterator,
952
+ method_handler.request_deserializer,
953
+ method_handler.response_serializer,
954
+ )
955
+
956
+
957
+ def _handle_stream_stream(
958
+ rpc_event: cygrpc.BaseEvent,
959
+ state: _RPCState,
960
+ method_handler: grpc.RpcMethodHandler,
961
+ default_thread_pool: futures.ThreadPoolExecutor,
962
+ ) -> futures.Future:
963
+ request_iterator = _RequestIterator(
964
+ state, rpc_event.call, method_handler.request_deserializer
965
+ )
966
+ thread_pool = _select_thread_pool_for_behavior(
967
+ method_handler.stream_stream, default_thread_pool
968
+ )
969
+ return thread_pool.submit(
970
+ state.context.run,
971
+ _stream_response_in_pool,
972
+ rpc_event,
973
+ state,
974
+ method_handler.stream_stream,
975
+ lambda: request_iterator,
976
+ method_handler.request_deserializer,
977
+ method_handler.response_serializer,
978
+ )
979
+
980
+
981
+ def _find_method_handler(
982
+ rpc_event: cygrpc.BaseEvent,
983
+ state: _RPCState,
984
+ method_with_handler: _Method,
985
+ interceptor_pipeline: Optional[_interceptor._ServicePipeline],
986
+ ) -> Optional[grpc.RpcMethodHandler]:
987
+ def query_handlers(
988
+ handler_call_details: _HandlerCallDetails,
989
+ ) -> Optional[grpc.RpcMethodHandler]:
990
+ return method_with_handler.handler(handler_call_details)
991
+
992
+ method_name = method_with_handler.name()
993
+ if not method_name:
994
+ method_name = _common.decode(rpc_event.call_details.method)
995
+
996
+ handler_call_details = _HandlerCallDetails(
997
+ method_name,
998
+ rpc_event.invocation_metadata,
999
+ )
1000
+
1001
+ if interceptor_pipeline is not None:
1002
+ return state.context.run(
1003
+ interceptor_pipeline.execute, query_handlers, handler_call_details
1004
+ )
1005
+ return state.context.run(query_handlers, handler_call_details)
1006
+
1007
+
1008
+ def _reject_rpc(
1009
+ rpc_event: cygrpc.BaseEvent,
1010
+ rpc_state: _RPCState,
1011
+ status: cygrpc.StatusCode,
1012
+ details: bytes,
1013
+ ):
1014
+ operations = (
1015
+ _get_initial_metadata_operation(rpc_state, None),
1016
+ cygrpc.ReceiveCloseOnServerOperation(_EMPTY_FLAGS),
1017
+ cygrpc.SendStatusFromServerOperation(
1018
+ None, status, details, _EMPTY_FLAGS
1019
+ ),
1020
+ )
1021
+ rpc_event.call.start_server_batch(
1022
+ operations,
1023
+ lambda _ignored_event: (
1024
+ rpc_state,
1025
+ (),
1026
+ ),
1027
+ )
1028
+
1029
+
1030
+ def _handle_with_method_handler(
1031
+ rpc_event: cygrpc.BaseEvent,
1032
+ state: _RPCState,
1033
+ method_handler: grpc.RpcMethodHandler,
1034
+ thread_pool: futures.ThreadPoolExecutor,
1035
+ ) -> futures.Future:
1036
+ with state.condition:
1037
+ rpc_event.call.start_server_batch(
1038
+ (cygrpc.ReceiveCloseOnServerOperation(_EMPTY_FLAGS),),
1039
+ _receive_close_on_server(state),
1040
+ )
1041
+ state.due.add(_RECEIVE_CLOSE_ON_SERVER_TOKEN)
1042
+ if method_handler.request_streaming:
1043
+ if method_handler.response_streaming:
1044
+ return _handle_stream_stream(
1045
+ rpc_event, state, method_handler, thread_pool
1046
+ )
1047
+ return _handle_stream_unary(
1048
+ rpc_event, state, method_handler, thread_pool
1049
+ )
1050
+ if method_handler.response_streaming:
1051
+ return _handle_unary_stream(
1052
+ rpc_event, state, method_handler, thread_pool
1053
+ )
1054
+ return _handle_unary_unary(
1055
+ rpc_event, state, method_handler, thread_pool
1056
+ )
1057
+
1058
+
1059
+ def _handle_call(
1060
+ rpc_event: cygrpc.BaseEvent,
1061
+ method_with_handler: _Method,
1062
+ interceptor_pipeline: Optional[_interceptor._ServicePipeline],
1063
+ thread_pool: futures.ThreadPoolExecutor,
1064
+ concurrency_exceeded: bool,
1065
+ ) -> Tuple[Optional[_RPCState], Optional[futures.Future]]:
1066
+ """Handles RPC based on provided handlers.
1067
+
1068
+ When receiving a call event from Core, registered method will have its
1069
+ name as tag, we pass the tag as registered_method_name to this method,
1070
+ then we can find the handler in registered_method_handlers based on
1071
+ the method name.
1072
+
1073
+ For call event with unregistered method, the method name will be included
1074
+ in rpc_event.call_details.method and we need to query the generics handlers
1075
+ to find the actual handler.
1076
+ """
1077
+ if not rpc_event.success:
1078
+ return None, None
1079
+ if rpc_event.call_details.method or method_with_handler.name():
1080
+ rpc_state = _RPCState()
1081
+ try:
1082
+ method_handler = _find_method_handler(
1083
+ rpc_event,
1084
+ rpc_state,
1085
+ method_with_handler,
1086
+ interceptor_pipeline,
1087
+ )
1088
+ except Exception as exception: # pylint: disable=broad-except
1089
+ details = "Exception servicing handler: {}".format(exception)
1090
+ _LOGGER.exception(details)
1091
+ _reject_rpc(
1092
+ rpc_event,
1093
+ rpc_state,
1094
+ cygrpc.StatusCode.unknown,
1095
+ b"Error in service handler!",
1096
+ )
1097
+ return rpc_state, None
1098
+ if method_handler is None:
1099
+ _reject_rpc(
1100
+ rpc_event,
1101
+ rpc_state,
1102
+ cygrpc.StatusCode.unimplemented,
1103
+ b"Method not found!",
1104
+ )
1105
+ return rpc_state, None
1106
+ if concurrency_exceeded:
1107
+ _reject_rpc(
1108
+ rpc_event,
1109
+ rpc_state,
1110
+ cygrpc.StatusCode.resource_exhausted,
1111
+ b"Concurrent RPC limit exceeded!",
1112
+ )
1113
+ return rpc_state, None
1114
+ return (
1115
+ rpc_state,
1116
+ _handle_with_method_handler(
1117
+ rpc_event, rpc_state, method_handler, thread_pool
1118
+ ),
1119
+ )
1120
+ return None, None
1121
+
1122
+
1123
+ @enum.unique
1124
+ class _ServerStage(enum.Enum):
1125
+ STOPPED = "stopped"
1126
+ STARTED = "started"
1127
+ GRACE = "grace"
1128
+
1129
+
1130
+ class _ServerState(object):
1131
+ lock: threading.RLock
1132
+ completion_queue: cygrpc.CompletionQueue
1133
+ server: cygrpc.Server
1134
+ generic_handlers: List[grpc.GenericRpcHandler]
1135
+ registered_method_handlers: Dict[str, grpc.RpcMethodHandler]
1136
+ interceptor_pipeline: Optional[_interceptor._ServicePipeline]
1137
+ thread_pool: futures.ThreadPoolExecutor
1138
+ stage: _ServerStage
1139
+ termination_event: threading.Event
1140
+ shutdown_events: List[threading.Event]
1141
+ maximum_concurrent_rpcs: Optional[int]
1142
+ active_rpc_count: int
1143
+ rpc_states: Set[_RPCState]
1144
+ due: Set[str]
1145
+ server_deallocated: bool
1146
+
1147
+ # pylint: disable=too-many-arguments
1148
+ def __init__(
1149
+ self,
1150
+ completion_queue: cygrpc.CompletionQueue,
1151
+ server: cygrpc.Server,
1152
+ generic_handlers: Sequence[grpc.GenericRpcHandler],
1153
+ interceptor_pipeline: Optional[_interceptor._ServicePipeline],
1154
+ thread_pool: futures.ThreadPoolExecutor,
1155
+ maximum_concurrent_rpcs: Optional[int],
1156
+ ):
1157
+ self.lock = threading.RLock()
1158
+ self.completion_queue = completion_queue
1159
+ self.server = server
1160
+ self.generic_handlers = list(generic_handlers)
1161
+ self.interceptor_pipeline = interceptor_pipeline
1162
+ self.thread_pool = thread_pool
1163
+ self.stage = _ServerStage.STOPPED
1164
+ self.termination_event = threading.Event()
1165
+ self.shutdown_events = [self.termination_event]
1166
+ self.maximum_concurrent_rpcs = maximum_concurrent_rpcs
1167
+ self.active_rpc_count = 0
1168
+ self.registered_method_handlers = {}
1169
+
1170
+ # TODO(https://github.com/grpc/grpc/issues/6597): eliminate these fields.
1171
+ self.rpc_states = set()
1172
+ self.due = set()
1173
+
1174
+ # A "volatile" flag to interrupt the daemon serving thread
1175
+ self.server_deallocated = False
1176
+
1177
+
1178
+ def _add_generic_handlers(
1179
+ state: _ServerState, generic_handlers: Iterable[grpc.GenericRpcHandler]
1180
+ ) -> None:
1181
+ with state.lock:
1182
+ state.generic_handlers.extend(generic_handlers)
1183
+
1184
+
1185
+ def _add_registered_method_handlers(
1186
+ state: _ServerState, method_handlers: Dict[str, grpc.RpcMethodHandler]
1187
+ ) -> None:
1188
+ with state.lock:
1189
+ state.registered_method_handlers.update(method_handlers)
1190
+
1191
+
1192
+ def _add_insecure_port(state: _ServerState, address: bytes) -> int:
1193
+ with state.lock:
1194
+ return state.server.add_http2_port(address)
1195
+
1196
+
1197
+ def _add_secure_port(
1198
+ state: _ServerState,
1199
+ address: bytes,
1200
+ server_credentials: grpc.ServerCredentials,
1201
+ ) -> int:
1202
+ with state.lock:
1203
+ return state.server.add_http2_port(
1204
+ address, server_credentials._credentials
1205
+ )
1206
+
1207
+
1208
+ def _request_call(state: _ServerState) -> None:
1209
+ state.server.request_call(
1210
+ state.completion_queue, state.completion_queue, _REQUEST_CALL_TAG
1211
+ )
1212
+ state.due.add(_REQUEST_CALL_TAG)
1213
+
1214
+
1215
+ def _request_registered_call(state: _ServerState, method: str) -> None:
1216
+ registered_call_tag = method
1217
+ state.server.request_registered_call(
1218
+ state.completion_queue,
1219
+ state.completion_queue,
1220
+ method,
1221
+ registered_call_tag,
1222
+ )
1223
+ state.due.add(registered_call_tag)
1224
+
1225
+
1226
+ # TODO(https://github.com/grpc/grpc/issues/6597): delete this function.
1227
+ def _stop_serving(state: _ServerState) -> bool:
1228
+ if not state.rpc_states and not state.due:
1229
+ state.server.destroy()
1230
+ for shutdown_event in state.shutdown_events:
1231
+ shutdown_event.set()
1232
+ state.stage = _ServerStage.STOPPED
1233
+ return True
1234
+ return False
1235
+
1236
+
1237
+ def _on_call_completed(state: _ServerState) -> None:
1238
+ with state.lock:
1239
+ state.active_rpc_count -= 1
1240
+
1241
+
1242
+ # pylint: disable=too-many-branches
1243
+ def _process_event_and_continue(
1244
+ state: _ServerState, event: cygrpc.BaseEvent
1245
+ ) -> bool:
1246
+ should_continue = True
1247
+ if event.tag is _SHUTDOWN_TAG:
1248
+ with state.lock:
1249
+ state.due.remove(_SHUTDOWN_TAG)
1250
+ if _stop_serving(state):
1251
+ should_continue = False
1252
+ elif (
1253
+ event.tag is _REQUEST_CALL_TAG
1254
+ or event.tag in state.registered_method_handlers.keys()
1255
+ ):
1256
+ registered_method_name = None
1257
+ if event.tag in state.registered_method_handlers.keys():
1258
+ registered_method_name = event.tag
1259
+ method_with_handler = _RegisteredMethod(
1260
+ registered_method_name,
1261
+ state.registered_method_handlers.get(
1262
+ registered_method_name, None
1263
+ ),
1264
+ )
1265
+ else:
1266
+ method_with_handler = _GenericMethod(
1267
+ state.generic_handlers,
1268
+ )
1269
+ with state.lock:
1270
+ state.due.remove(event.tag)
1271
+ concurrency_exceeded = (
1272
+ state.maximum_concurrent_rpcs is not None
1273
+ and state.active_rpc_count >= state.maximum_concurrent_rpcs
1274
+ )
1275
+ rpc_state, rpc_future = _handle_call(
1276
+ event,
1277
+ method_with_handler,
1278
+ state.interceptor_pipeline,
1279
+ state.thread_pool,
1280
+ concurrency_exceeded,
1281
+ )
1282
+ if rpc_state is not None:
1283
+ state.rpc_states.add(rpc_state)
1284
+ if rpc_future is not None:
1285
+ state.active_rpc_count += 1
1286
+ rpc_future.add_done_callback(
1287
+ lambda _unused_future: _on_call_completed(state)
1288
+ )
1289
+ if state.stage is _ServerStage.STARTED:
1290
+ if (
1291
+ registered_method_name
1292
+ in state.registered_method_handlers.keys()
1293
+ ):
1294
+ _request_registered_call(state, registered_method_name)
1295
+ else:
1296
+ _request_call(state)
1297
+ elif _stop_serving(state):
1298
+ should_continue = False
1299
+ else:
1300
+ rpc_state, callbacks = event.tag(event)
1301
+ for callback in callbacks:
1302
+ try:
1303
+ callback()
1304
+ except Exception: # pylint: disable=broad-except
1305
+ _LOGGER.exception("Exception calling callback!")
1306
+ if rpc_state is not None:
1307
+ with state.lock:
1308
+ state.rpc_states.remove(rpc_state)
1309
+ if _stop_serving(state):
1310
+ should_continue = False
1311
+ return should_continue
1312
+
1313
+
1314
+ def _serve(state: _ServerState) -> None:
1315
+ while True:
1316
+ timeout = time.time() + _DEALLOCATED_SERVER_CHECK_PERIOD_S
1317
+ event = state.completion_queue.poll(timeout)
1318
+ if state.server_deallocated:
1319
+ _begin_shutdown_once(state)
1320
+ if event.completion_type != cygrpc.CompletionType.queue_timeout:
1321
+ if not _process_event_and_continue(state, event):
1322
+ return
1323
+ # We want to force the deletion of the previous event
1324
+ # ~before~ we poll again; if the event has a reference
1325
+ # to a shutdown Call object, this can induce spinlock.
1326
+ event = None
1327
+
1328
+
1329
+ def _begin_shutdown_once(state: _ServerState) -> None:
1330
+ with state.lock:
1331
+ if state.stage is _ServerStage.STARTED:
1332
+ state.server.shutdown(state.completion_queue, _SHUTDOWN_TAG)
1333
+ state.stage = _ServerStage.GRACE
1334
+ state.due.add(_SHUTDOWN_TAG)
1335
+
1336
+
1337
+ def _stop(state: _ServerState, grace: Optional[float]) -> threading.Event:
1338
+ with state.lock:
1339
+ if state.stage is _ServerStage.STOPPED:
1340
+ shutdown_event = threading.Event()
1341
+ shutdown_event.set()
1342
+ return shutdown_event
1343
+ _begin_shutdown_once(state)
1344
+ shutdown_event = threading.Event()
1345
+ state.shutdown_events.append(shutdown_event)
1346
+ if grace is None:
1347
+ state.server.cancel_all_calls()
1348
+ else:
1349
+
1350
+ def cancel_all_calls_after_grace():
1351
+ shutdown_event.wait(timeout=grace)
1352
+ with state.lock:
1353
+ state.server.cancel_all_calls()
1354
+
1355
+ thread = threading.Thread(target=cancel_all_calls_after_grace)
1356
+ thread.start()
1357
+ return shutdown_event
1358
+ shutdown_event.wait()
1359
+ return shutdown_event
1360
+
1361
+
1362
+ def _start(state: _ServerState) -> None:
1363
+ with state.lock:
1364
+ if state.stage is not _ServerStage.STOPPED:
1365
+ error_msg = "Cannot start already-started server!"
1366
+ raise ValueError(error_msg)
1367
+ state.server.start()
1368
+ state.stage = _ServerStage.STARTED
1369
+ # Request a call for each registered method so we can handle any of them.
1370
+ for method in state.registered_method_handlers.keys():
1371
+ _request_registered_call(state, method)
1372
+ # Also request a call for non-registered method.
1373
+ _request_call(state)
1374
+ thread = threading.Thread(target=_serve, args=(state,))
1375
+ thread.daemon = True
1376
+ thread.start()
1377
+
1378
+
1379
+ def _validate_generic_rpc_handlers(
1380
+ generic_rpc_handlers: Iterable[grpc.GenericRpcHandler],
1381
+ ) -> None:
1382
+ for generic_rpc_handler in generic_rpc_handlers:
1383
+ service_attribute = getattr(generic_rpc_handler, "service", None)
1384
+ if service_attribute is None:
1385
+ error_msg = (
1386
+ f'"{generic_rpc_handler}" must conform to'
1387
+ 'grpc.GenericRpcHandler type but does not have "service" method!'
1388
+ )
1389
+ raise AttributeError(error_msg)
1390
+
1391
+
1392
+ def _augment_options(
1393
+ base_options: Sequence[ChannelArgumentType],
1394
+ compression: Optional[grpc.Compression],
1395
+ xds: bool,
1396
+ ) -> Sequence[ChannelArgumentType]:
1397
+ compression_option = _compression.create_channel_option(compression)
1398
+ maybe_server_call_tracer_factory_option = (
1399
+ _observability.create_server_call_tracer_factory_option(xds)
1400
+ )
1401
+ return (
1402
+ tuple(base_options)
1403
+ + compression_option
1404
+ + maybe_server_call_tracer_factory_option
1405
+ )
1406
+
1407
+
1408
+ class _Server(grpc.Server):
1409
+ _state: _ServerState
1410
+
1411
+ # pylint: disable=too-many-arguments
1412
+ def __init__(
1413
+ self,
1414
+ thread_pool: futures.ThreadPoolExecutor,
1415
+ generic_handlers: Sequence[grpc.GenericRpcHandler],
1416
+ interceptors: Sequence[grpc.ServerInterceptor],
1417
+ options: Sequence[ChannelArgumentType],
1418
+ maximum_concurrent_rpcs: Optional[int],
1419
+ compression: Optional[grpc.Compression],
1420
+ xds: bool,
1421
+ ):
1422
+ completion_queue = cygrpc.CompletionQueue()
1423
+ server = cygrpc.Server(_augment_options(options, compression, xds), xds)
1424
+ server.register_completion_queue(completion_queue)
1425
+ self._state = _ServerState(
1426
+ completion_queue,
1427
+ server,
1428
+ generic_handlers,
1429
+ _interceptor.service_pipeline(interceptors),
1430
+ thread_pool,
1431
+ maximum_concurrent_rpcs,
1432
+ )
1433
+ self._cy_server = server
1434
+
1435
+ def add_generic_rpc_handlers(
1436
+ self, generic_rpc_handlers: Iterable[grpc.GenericRpcHandler]
1437
+ ) -> None:
1438
+ _validate_generic_rpc_handlers(generic_rpc_handlers)
1439
+ _add_generic_handlers(self._state, generic_rpc_handlers)
1440
+
1441
+ def add_registered_method_handlers(
1442
+ self,
1443
+ service_name: str,
1444
+ method_handlers: Dict[str, grpc.RpcMethodHandler],
1445
+ ) -> None:
1446
+ # Can't register method once server started.
1447
+ with self._state.lock:
1448
+ if self._state.stage is _ServerStage.STARTED:
1449
+ return
1450
+
1451
+ # TODO(xuanwn): We should validate method_handlers first.
1452
+ method_to_handlers = {
1453
+ _common.fully_qualified_method(service_name, method): method_handler
1454
+ for method, method_handler in method_handlers.items()
1455
+ }
1456
+ for fully_qualified_method in method_to_handlers.keys():
1457
+ self._cy_server.register_method(fully_qualified_method)
1458
+ _add_registered_method_handlers(self._state, method_to_handlers)
1459
+
1460
+ def add_insecure_port(self, address: str) -> int:
1461
+ return _common.validate_port_binding_result(
1462
+ address, _add_insecure_port(self._state, _common.encode(address))
1463
+ )
1464
+
1465
+ def add_secure_port(
1466
+ self, address: str, server_credentials: grpc.ServerCredentials
1467
+ ) -> int:
1468
+ return _common.validate_port_binding_result(
1469
+ address,
1470
+ _add_secure_port(
1471
+ self._state, _common.encode(address), server_credentials
1472
+ ),
1473
+ )
1474
+
1475
+ def start(self) -> None:
1476
+ _start(self._state)
1477
+
1478
+ def wait_for_termination(self, timeout: Optional[float] = None) -> bool:
1479
+ # NOTE(https://bugs.python.org/issue35935)
1480
+ # Remove this workaround once threading.Event.wait() is working with
1481
+ # CTRL+C across platforms.
1482
+ return _common.wait(
1483
+ self._state.termination_event.wait,
1484
+ self._state.termination_event.is_set,
1485
+ timeout=timeout,
1486
+ )
1487
+
1488
+ def stop(self, grace: Optional[float]) -> threading.Event:
1489
+ return _stop(self._state, grace)
1490
+
1491
+ def __del__(self):
1492
+ if hasattr(self, "_state"):
1493
+ # We can not grab a lock in __del__(), so set a flag to signal the
1494
+ # serving daemon thread (if it exists) to initiate shutdown.
1495
+ self._state.server_deallocated = True
1496
+
1497
+
1498
+ def create_server(
1499
+ thread_pool: futures.ThreadPoolExecutor,
1500
+ generic_rpc_handlers: Sequence[grpc.GenericRpcHandler],
1501
+ interceptors: Sequence[grpc.ServerInterceptor],
1502
+ options: Sequence[ChannelArgumentType],
1503
+ maximum_concurrent_rpcs: Optional[int],
1504
+ compression: Optional[grpc.Compression],
1505
+ xds: bool,
1506
+ ) -> _Server:
1507
+ _validate_generic_rpc_handlers(generic_rpc_handlers)
1508
+ return _Server(
1509
+ thread_pool,
1510
+ generic_rpc_handlers,
1511
+ interceptors,
1512
+ options,
1513
+ maximum_concurrent_rpcs,
1514
+ compression,
1515
+ xds,
1516
+ )
lib/python3.12/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
+ debug_msg = (
83
+ f"Creating secure channel with credentials '{channel_credentials}', "
84
+ f"options '{options}' and compression '{compression}'"
85
+ )
86
+ _LOGGER.debug(debug_msg)
87
+ return grpc.secure_channel(
88
+ target,
89
+ credentials=channel_credentials,
90
+ options=options,
91
+ compression=compression,
92
+ )
93
+
94
+
95
+ class ChannelCache:
96
+ # NOTE(rbellevi): Untyped due to reference cycle.
97
+ _singleton = None
98
+ _lock: threading.RLock = threading.RLock()
99
+ _condition: threading.Condition = threading.Condition(lock=_lock)
100
+ _eviction_ready: threading.Event = threading.Event()
101
+
102
+ _mapping: Dict[CacheKey, Tuple[grpc.Channel, datetime.datetime]]
103
+ _eviction_thread: threading.Thread
104
+
105
+ def __init__(self):
106
+ self._mapping = collections.OrderedDict()
107
+ self._eviction_thread = threading.Thread(
108
+ target=ChannelCache._perform_evictions, daemon=True
109
+ )
110
+ self._eviction_thread.start()
111
+
112
+ @staticmethod
113
+ def get():
114
+ with ChannelCache._lock:
115
+ if ChannelCache._singleton is None:
116
+ ChannelCache._singleton = ChannelCache()
117
+ ChannelCache._eviction_ready.wait()
118
+ return ChannelCache._singleton
119
+
120
+ def _evict_locked(self, key: CacheKey):
121
+ channel, _ = self._mapping.pop(key)
122
+ _LOGGER.debug(
123
+ "Evicting channel %s with configuration %s.", channel, key
124
+ )
125
+ channel.close()
126
+ del channel
127
+
128
+ @staticmethod
129
+ def _perform_evictions():
130
+ while True:
131
+ with ChannelCache._lock:
132
+ ChannelCache._eviction_ready.set()
133
+ if not ChannelCache._singleton._mapping:
134
+ ChannelCache._condition.wait()
135
+ elif len(ChannelCache._singleton._mapping) > _MAXIMUM_CHANNELS:
136
+ key = next(iter(ChannelCache._singleton._mapping.keys()))
137
+ ChannelCache._singleton._evict_locked(key)
138
+ # And immediately reevaluate.
139
+ else:
140
+ key, (_, eviction_time) = next(
141
+ iter(ChannelCache._singleton._mapping.items())
142
+ )
143
+ now = datetime.datetime.now()
144
+ if eviction_time <= now:
145
+ ChannelCache._singleton._evict_locked(key)
146
+ continue
147
+ else:
148
+ time_to_eviction = (eviction_time - now).total_seconds()
149
+ # NOTE: We aim to *eventually* coalesce to a state in
150
+ # which no overdue channels are in the cache and the
151
+ # length of the cache is longer than _MAXIMUM_CHANNELS.
152
+ # We tolerate momentary states in which these two
153
+ # criteria are not met.
154
+ ChannelCache._condition.wait(timeout=time_to_eviction)
155
+
156
+ def get_channel(
157
+ self,
158
+ target: str,
159
+ options: Sequence[Tuple[str, str]],
160
+ channel_credentials: Optional[grpc.ChannelCredentials],
161
+ insecure: bool,
162
+ compression: Optional[grpc.Compression],
163
+ method: str,
164
+ _registered_method: bool,
165
+ ) -> Tuple[grpc.Channel, Optional[int]]:
166
+ """Get a channel from cache or creates a new channel.
167
+
168
+ This method also takes care of register method for channel,
169
+ which means we'll register a new call handle if we're calling a
170
+ non-registered method for an existing channel.
171
+
172
+ Returns:
173
+ A tuple with two items. The first item is the channel, second item is
174
+ the call handle if the method is registered, None if it's not registered.
175
+ """
176
+ if insecure and channel_credentials:
177
+ raise ValueError(
178
+ "The insecure option is mutually exclusive with "
179
+ + "the channel_credentials option. Please use one "
180
+ + "or the other."
181
+ )
182
+ if insecure:
183
+ channel_credentials = (
184
+ grpc.experimental.insecure_channel_credentials()
185
+ )
186
+ elif channel_credentials is None:
187
+ _LOGGER.debug("Defaulting to SSL channel credentials.")
188
+ channel_credentials = grpc.ssl_channel_credentials()
189
+ key = (target, options, channel_credentials, compression)
190
+ with self._lock:
191
+ channel_data = self._mapping.get(key, None)
192
+ call_handle = None
193
+ if channel_data is not None:
194
+ channel = channel_data[0]
195
+ # Register a new call handle if we're calling a registered method for an
196
+ # existing channel and this method is not registered.
197
+ if _registered_method:
198
+ call_handle = channel._get_registered_call_handle(method)
199
+ self._mapping.pop(key)
200
+ self._mapping[key] = (
201
+ channel,
202
+ datetime.datetime.now() + _EVICTION_PERIOD,
203
+ )
204
+ return channel, call_handle
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
+ )
lib/python3.12/site-packages/grpc/_typing.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ Optional[str],
51
+ Optional[float],
52
+ Optional[MetadataType],
53
+ Optional[cygrpc.CallCredentials],
54
+ Sequence[Sequence[cygrpc.Operation]],
55
+ UserTag,
56
+ Any,
57
+ Optional[int],
58
+ ],
59
+ cygrpc.IntegratedCall,
60
+ ]
61
+ ServerTagCallbackType = Tuple[
62
+ Optional["_RPCState"], Sequence[NullaryCallbackType]
63
+ ]
64
+ ServerCallbackTag = Callable[[cygrpc.BaseEvent], ServerTagCallbackType]
65
+ ArityAgnosticMethodHandler = Union[
66
+ Callable[
67
+ [RequestType, "ServicerContext", Callable[[ResponseType], None]],
68
+ ResponseType,
69
+ ],
70
+ Callable[
71
+ [RequestType, "ServicerContext", Callable[[ResponseType], None]],
72
+ Iterator[ResponseType],
73
+ ],
74
+ Callable[
75
+ [
76
+ Iterator[RequestType],
77
+ "ServicerContext",
78
+ Callable[[ResponseType], None],
79
+ ],
80
+ ResponseType,
81
+ ],
82
+ Callable[
83
+ [
84
+ Iterator[RequestType],
85
+ "ServicerContext",
86
+ Callable[[ResponseType], None],
87
+ ],
88
+ Iterator[ResponseType],
89
+ ],
90
+ Callable[[RequestType, "ServicerContext"], ResponseType],
91
+ Callable[[RequestType, "ServicerContext"], Iterator[ResponseType]],
92
+ Callable[[Iterator[RequestType], "ServicerContext"], ResponseType],
93
+ Callable[
94
+ [Iterator[RequestType], "ServicerContext"], Iterator[ResponseType]
95
+ ],
96
+ ]
lib/python3.12/site-packages/grpc/_utilities.py ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ if self._matured:
99
+ return
100
+ if until is None:
101
+ self._condition.wait()
102
+ else:
103
+ remaining = until - time.time()
104
+ if remaining < 0:
105
+ raise grpc.FutureTimeoutError()
106
+ self._condition.wait(timeout=remaining)
107
+
108
+ def _update(self, connectivity: Optional[grpc.ChannelConnectivity]) -> None:
109
+ with self._condition:
110
+ if (
111
+ not self._cancelled
112
+ and connectivity is grpc.ChannelConnectivity.READY
113
+ ):
114
+ self._matured = True
115
+ self._channel.unsubscribe(self._update)
116
+ self._condition.notify_all()
117
+ done_callbacks = tuple(self._done_callbacks)
118
+ self._done_callbacks = None
119
+ else:
120
+ return
121
+
122
+ for done_callback in done_callbacks:
123
+ try:
124
+ done_callback(self)
125
+ except Exception: # pylint: disable=broad-except
126
+ _LOGGER.exception(_DONE_CALLBACK_EXCEPTION_LOG_MESSAGE)
127
+
128
+ def cancel(self) -> bool:
129
+ with self._condition:
130
+ if not self._matured:
131
+ self._cancelled = True
132
+ self._channel.unsubscribe(self._update)
133
+ self._condition.notify_all()
134
+ done_callbacks = tuple(self._done_callbacks)
135
+ self._done_callbacks = None
136
+ else:
137
+ return False
138
+
139
+ for done_callback in done_callbacks:
140
+ try:
141
+ done_callback(self)
142
+ except Exception: # pylint: disable=broad-except
143
+ _LOGGER.exception(_DONE_CALLBACK_EXCEPTION_LOG_MESSAGE)
144
+
145
+ return True
146
+
147
+ def cancelled(self) -> bool:
148
+ with self._condition:
149
+ return self._cancelled
150
+
151
+ def running(self) -> bool:
152
+ with self._condition:
153
+ return not self._cancelled and not self._matured
154
+
155
+ def done(self) -> bool:
156
+ with self._condition:
157
+ return self._cancelled or self._matured
158
+
159
+ def result(self, timeout: Optional[float] = None) -> None:
160
+ self._block(timeout)
161
+
162
+ def exception(self, timeout: Optional[float] = None) -> None:
163
+ self._block(timeout)
164
+
165
+ def traceback(self, timeout: Optional[float] = None) -> None:
166
+ self._block(timeout)
167
+
168
+ def add_done_callback(self, fn: DoneCallbackType):
169
+ with self._condition:
170
+ if not self._cancelled and not self._matured:
171
+ self._done_callbacks.append(fn)
172
+ return
173
+
174
+ fn(self)
175
+
176
+ def start(self):
177
+ with self._condition:
178
+ self._channel.subscribe(self._update, try_to_connect=True)
179
+
180
+ def __del__(self):
181
+ with self._condition:
182
+ if not self._cancelled and not self._matured:
183
+ self._channel.unsubscribe(self._update)
184
+
185
+
186
+ def channel_ready_future(channel: grpc.Channel) -> _ChannelReadyFuture:
187
+ ready_future = _ChannelReadyFuture(channel)
188
+ ready_future.start()
189
+ return ready_future
190
+
191
+
192
+ def first_version_is_lower(version1: str, version2: str) -> bool:
193
+ """
194
+ Compares two versions in the format '1.60.1' or '1.60.1.dev0'.
195
+
196
+ This method will be used in all stubs generated by grpcio-tools to check whether
197
+ the stub version is compatible with the runtime grpcio.
198
+
199
+ Args:
200
+ version1: The first version string.
201
+ version2: The second version string.
202
+
203
+ Returns:
204
+ True if version1 is lower, False otherwise.
205
+ """
206
+ version1_list = version1.split(".")
207
+ version2_list = version2.split(".")
208
+
209
+ try:
210
+ for i in range(3):
211
+ if int(version1_list[i]) < int(version2_list[i]):
212
+ return True
213
+ if int(version1_list[i]) > int(version2_list[i]):
214
+ return False
215
+ except ValueError:
216
+ # Return false in case we can't convert version to int.
217
+ return False
218
+
219
+ # The version without dev0 will be considered lower.
220
+ return len(version1_list) < len(version2_list)
lib/python3.12/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.
lib/python3.12/site-packages/grpc/beta/__pycache__/_server_adaptations.cpython-312.pyc ADDED
Binary file (18.9 kB). View file
 
lib/python3.12/site-packages/grpc/beta/__pycache__/interfaces.cpython-312.pyc ADDED
Binary file (7.29 kB). View file
 
lib/python3.12/site-packages/grpc/beta/_client_adaptations.py ADDED
@@ -0,0 +1,1010 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ return metadata_transformer(non_none_metadata)
51
+
52
+
53
+ def _credentials(grpc_call_options):
54
+ return None if grpc_call_options is None else grpc_call_options.credentials
55
+
56
+
57
+ def _abortion(rpc_error_call):
58
+ code = rpc_error_call.code()
59
+ pair = _STATUS_CODE_TO_ABORTION_KIND_AND_ABORTION_ERROR_CLASS.get(code)
60
+ error_kind = face.Abortion.Kind.LOCAL_FAILURE if pair is None else pair[0]
61
+ return face.Abortion(
62
+ error_kind,
63
+ rpc_error_call.initial_metadata(),
64
+ rpc_error_call.trailing_metadata(),
65
+ code,
66
+ rpc_error_call.details(),
67
+ )
68
+
69
+
70
+ def _abortion_error(rpc_error_call):
71
+ code = rpc_error_call.code()
72
+ pair = _STATUS_CODE_TO_ABORTION_KIND_AND_ABORTION_ERROR_CLASS.get(code)
73
+ exception_class = face.AbortionError if pair is None else pair[1]
74
+ return exception_class(
75
+ rpc_error_call.initial_metadata(),
76
+ rpc_error_call.trailing_metadata(),
77
+ code,
78
+ rpc_error_call.details(),
79
+ )
80
+
81
+
82
+ class _InvocationProtocolContext(interfaces.GRPCInvocationContext):
83
+ def disable_next_request_compression(self):
84
+ pass # TODO(https://github.com/grpc/grpc/issues/4078): design, implement.
85
+
86
+
87
+ class _Rendezvous(future.Future, face.Call):
88
+ def __init__(self, response_future, response_iterator, call):
89
+ self._future = response_future
90
+ self._iterator = response_iterator
91
+ self._call = call
92
+
93
+ def cancel(self):
94
+ return self._call.cancel()
95
+
96
+ def cancelled(self):
97
+ return self._future.cancelled()
98
+
99
+ def running(self):
100
+ return self._future.running()
101
+
102
+ def done(self):
103
+ return self._future.done()
104
+
105
+ def result(self, timeout=None):
106
+ try:
107
+ return self._future.result(timeout=timeout)
108
+ except grpc.RpcError as rpc_error_call:
109
+ raise _abortion_error(rpc_error_call)
110
+ except grpc.FutureTimeoutError:
111
+ raise future.TimeoutError()
112
+ except grpc.FutureCancelledError:
113
+ raise future.CancelledError()
114
+
115
+ def exception(self, timeout=None):
116
+ try:
117
+ rpc_error_call = self._future.exception(timeout=timeout)
118
+ if rpc_error_call is None:
119
+ return None
120
+ return _abortion_error(rpc_error_call)
121
+ except grpc.FutureTimeoutError:
122
+ raise future.TimeoutError()
123
+ except grpc.FutureCancelledError:
124
+ raise future.CancelledError()
125
+
126
+ def traceback(self, timeout=None):
127
+ try:
128
+ return self._future.traceback(timeout=timeout)
129
+ except grpc.FutureTimeoutError:
130
+ raise future.TimeoutError()
131
+ except grpc.FutureCancelledError:
132
+ raise future.CancelledError()
133
+
134
+ def add_done_callback(self, fn):
135
+ self._future.add_done_callback(lambda _ignored_callback: fn(self))
136
+
137
+ def __iter__(self):
138
+ return self
139
+
140
+ def _next(self):
141
+ try:
142
+ return next(self._iterator)
143
+ except grpc.RpcError as rpc_error_call:
144
+ raise _abortion_error(rpc_error_call)
145
+
146
+ def __next__(self):
147
+ return self._next()
148
+
149
+ def next(self):
150
+ return self._next()
151
+
152
+ def is_active(self):
153
+ return self._call.is_active()
154
+
155
+ def time_remaining(self):
156
+ return self._call.time_remaining()
157
+
158
+ def add_abortion_callback(self, abortion_callback):
159
+ def done_callback():
160
+ if self.code() is not grpc.StatusCode.OK:
161
+ abortion_callback(_abortion(self._call))
162
+
163
+ registered = self._call.add_callback(done_callback)
164
+ return None if registered else done_callback()
165
+
166
+ def protocol_context(self):
167
+ return _InvocationProtocolContext()
168
+
169
+ def initial_metadata(self):
170
+ return _metadata.beta(self._call.initial_metadata())
171
+
172
+ def terminal_metadata(self):
173
+ return _metadata.beta(self._call.terminal_metadata())
174
+
175
+ def code(self):
176
+ return self._call.code()
177
+
178
+ def details(self):
179
+ return self._call.details()
180
+
181
+
182
+ def _blocking_unary_unary(
183
+ channel,
184
+ group,
185
+ method,
186
+ timeout,
187
+ with_call,
188
+ protocol_options,
189
+ metadata,
190
+ metadata_transformer,
191
+ request,
192
+ request_serializer,
193
+ response_deserializer,
194
+ ):
195
+ try:
196
+ multi_callable = channel.unary_unary(
197
+ _common.fully_qualified_method(group, method),
198
+ request_serializer=request_serializer,
199
+ response_deserializer=response_deserializer,
200
+ )
201
+ effective_metadata = _effective_metadata(metadata, metadata_transformer)
202
+ if with_call:
203
+ response, call = multi_callable.with_call(
204
+ request,
205
+ timeout=timeout,
206
+ metadata=_metadata.unbeta(effective_metadata),
207
+ credentials=_credentials(protocol_options),
208
+ )
209
+ return response, _Rendezvous(None, None, call)
210
+ return multi_callable(
211
+ request,
212
+ timeout=timeout,
213
+ metadata=_metadata.unbeta(effective_metadata),
214
+ credentials=_credentials(protocol_options),
215
+ )
216
+ except grpc.RpcError as rpc_error_call:
217
+ raise _abortion_error(rpc_error_call)
218
+
219
+
220
+ def _future_unary_unary(
221
+ channel,
222
+ group,
223
+ method,
224
+ timeout,
225
+ protocol_options,
226
+ metadata,
227
+ metadata_transformer,
228
+ request,
229
+ request_serializer,
230
+ response_deserializer,
231
+ ):
232
+ multi_callable = channel.unary_unary(
233
+ _common.fully_qualified_method(group, method),
234
+ request_serializer=request_serializer,
235
+ response_deserializer=response_deserializer,
236
+ )
237
+ effective_metadata = _effective_metadata(metadata, metadata_transformer)
238
+ response_future = multi_callable.future(
239
+ request,
240
+ timeout=timeout,
241
+ metadata=_metadata.unbeta(effective_metadata),
242
+ credentials=_credentials(protocol_options),
243
+ )
244
+ return _Rendezvous(response_future, None, response_future)
245
+
246
+
247
+ def _unary_stream(
248
+ channel,
249
+ group,
250
+ method,
251
+ timeout,
252
+ protocol_options,
253
+ metadata,
254
+ metadata_transformer,
255
+ request,
256
+ request_serializer,
257
+ response_deserializer,
258
+ ):
259
+ multi_callable = channel.unary_stream(
260
+ _common.fully_qualified_method(group, method),
261
+ request_serializer=request_serializer,
262
+ response_deserializer=response_deserializer,
263
+ )
264
+ effective_metadata = _effective_metadata(metadata, metadata_transformer)
265
+ response_iterator = multi_callable(
266
+ request,
267
+ timeout=timeout,
268
+ metadata=_metadata.unbeta(effective_metadata),
269
+ credentials=_credentials(protocol_options),
270
+ )
271
+ return _Rendezvous(None, response_iterator, response_iterator)
272
+
273
+
274
+ def _blocking_stream_unary(
275
+ channel,
276
+ group,
277
+ method,
278
+ timeout,
279
+ with_call,
280
+ protocol_options,
281
+ metadata,
282
+ metadata_transformer,
283
+ request_iterator,
284
+ request_serializer,
285
+ response_deserializer,
286
+ ):
287
+ try:
288
+ multi_callable = channel.stream_unary(
289
+ _common.fully_qualified_method(group, method),
290
+ request_serializer=request_serializer,
291
+ response_deserializer=response_deserializer,
292
+ )
293
+ effective_metadata = _effective_metadata(metadata, metadata_transformer)
294
+ if with_call:
295
+ response, call = multi_callable.with_call(
296
+ request_iterator,
297
+ timeout=timeout,
298
+ metadata=_metadata.unbeta(effective_metadata),
299
+ credentials=_credentials(protocol_options),
300
+ )
301
+ return response, _Rendezvous(None, None, call)
302
+ return multi_callable(
303
+ request_iterator,
304
+ timeout=timeout,
305
+ metadata=_metadata.unbeta(effective_metadata),
306
+ credentials=_credentials(protocol_options),
307
+ )
308
+ except grpc.RpcError as rpc_error_call:
309
+ raise _abortion_error(rpc_error_call)
310
+
311
+
312
+ def _future_stream_unary(
313
+ channel,
314
+ group,
315
+ method,
316
+ timeout,
317
+ protocol_options,
318
+ metadata,
319
+ metadata_transformer,
320
+ request_iterator,
321
+ request_serializer,
322
+ response_deserializer,
323
+ ):
324
+ multi_callable = channel.stream_unary(
325
+ _common.fully_qualified_method(group, method),
326
+ request_serializer=request_serializer,
327
+ response_deserializer=response_deserializer,
328
+ )
329
+ effective_metadata = _effective_metadata(metadata, metadata_transformer)
330
+ response_future = multi_callable.future(
331
+ request_iterator,
332
+ timeout=timeout,
333
+ metadata=_metadata.unbeta(effective_metadata),
334
+ credentials=_credentials(protocol_options),
335
+ )
336
+ return _Rendezvous(response_future, None, response_future)
337
+
338
+
339
+ def _stream_stream(
340
+ channel,
341
+ group,
342
+ method,
343
+ timeout,
344
+ protocol_options,
345
+ metadata,
346
+ metadata_transformer,
347
+ request_iterator,
348
+ request_serializer,
349
+ response_deserializer,
350
+ ):
351
+ multi_callable = channel.stream_stream(
352
+ _common.fully_qualified_method(group, method),
353
+ request_serializer=request_serializer,
354
+ response_deserializer=response_deserializer,
355
+ )
356
+ effective_metadata = _effective_metadata(metadata, metadata_transformer)
357
+ response_iterator = multi_callable(
358
+ request_iterator,
359
+ timeout=timeout,
360
+ metadata=_metadata.unbeta(effective_metadata),
361
+ credentials=_credentials(protocol_options),
362
+ )
363
+ return _Rendezvous(None, response_iterator, response_iterator)
364
+
365
+
366
+ class _UnaryUnaryMultiCallable(face.UnaryUnaryMultiCallable):
367
+ def __init__(
368
+ self,
369
+ channel,
370
+ group,
371
+ method,
372
+ metadata_transformer,
373
+ request_serializer,
374
+ response_deserializer,
375
+ ):
376
+ self._channel = channel
377
+ self._group = group
378
+ self._method = method
379
+ self._metadata_transformer = metadata_transformer
380
+ self._request_serializer = request_serializer
381
+ self._response_deserializer = response_deserializer
382
+
383
+ def __call__(
384
+ self,
385
+ request,
386
+ timeout,
387
+ metadata=None,
388
+ with_call=False,
389
+ protocol_options=None,
390
+ ):
391
+ return _blocking_unary_unary(
392
+ self._channel,
393
+ self._group,
394
+ self._method,
395
+ timeout,
396
+ with_call,
397
+ protocol_options,
398
+ metadata,
399
+ self._metadata_transformer,
400
+ request,
401
+ self._request_serializer,
402
+ self._response_deserializer,
403
+ )
404
+
405
+ def future(self, request, timeout, metadata=None, protocol_options=None):
406
+ return _future_unary_unary(
407
+ self._channel,
408
+ self._group,
409
+ self._method,
410
+ timeout,
411
+ protocol_options,
412
+ metadata,
413
+ self._metadata_transformer,
414
+ request,
415
+ self._request_serializer,
416
+ self._response_deserializer,
417
+ )
418
+
419
+ def event(
420
+ self,
421
+ request,
422
+ receiver,
423
+ abortion_callback,
424
+ timeout,
425
+ metadata=None,
426
+ protocol_options=None,
427
+ ):
428
+ raise NotImplementedError()
429
+
430
+
431
+ class _UnaryStreamMultiCallable(face.UnaryStreamMultiCallable):
432
+ def __init__(
433
+ self,
434
+ channel,
435
+ group,
436
+ method,
437
+ metadata_transformer,
438
+ request_serializer,
439
+ response_deserializer,
440
+ ):
441
+ self._channel = channel
442
+ self._group = group
443
+ self._method = method
444
+ self._metadata_transformer = metadata_transformer
445
+ self._request_serializer = request_serializer
446
+ self._response_deserializer = response_deserializer
447
+
448
+ def __call__(self, request, timeout, metadata=None, protocol_options=None):
449
+ return _unary_stream(
450
+ self._channel,
451
+ self._group,
452
+ self._method,
453
+ timeout,
454
+ protocol_options,
455
+ metadata,
456
+ self._metadata_transformer,
457
+ request,
458
+ self._request_serializer,
459
+ self._response_deserializer,
460
+ )
461
+
462
+ def event(
463
+ self,
464
+ request,
465
+ receiver,
466
+ abortion_callback,
467
+ timeout,
468
+ metadata=None,
469
+ protocol_options=None,
470
+ ):
471
+ raise NotImplementedError()
472
+
473
+
474
+ class _StreamUnaryMultiCallable(face.StreamUnaryMultiCallable):
475
+ def __init__(
476
+ self,
477
+ channel,
478
+ group,
479
+ method,
480
+ metadata_transformer,
481
+ request_serializer,
482
+ response_deserializer,
483
+ ):
484
+ self._channel = channel
485
+ self._group = group
486
+ self._method = method
487
+ self._metadata_transformer = metadata_transformer
488
+ self._request_serializer = request_serializer
489
+ self._response_deserializer = response_deserializer
490
+
491
+ def __call__(
492
+ self,
493
+ request_iterator,
494
+ timeout,
495
+ metadata=None,
496
+ with_call=False,
497
+ protocol_options=None,
498
+ ):
499
+ return _blocking_stream_unary(
500
+ self._channel,
501
+ self._group,
502
+ self._method,
503
+ timeout,
504
+ with_call,
505
+ protocol_options,
506
+ metadata,
507
+ self._metadata_transformer,
508
+ request_iterator,
509
+ self._request_serializer,
510
+ self._response_deserializer,
511
+ )
512
+
513
+ def future(
514
+ self, request_iterator, timeout, metadata=None, protocol_options=None
515
+ ):
516
+ return _future_stream_unary(
517
+ self._channel,
518
+ self._group,
519
+ self._method,
520
+ timeout,
521
+ protocol_options,
522
+ metadata,
523
+ self._metadata_transformer,
524
+ request_iterator,
525
+ self._request_serializer,
526
+ self._response_deserializer,
527
+ )
528
+
529
+ def event(
530
+ self,
531
+ receiver,
532
+ abortion_callback,
533
+ timeout,
534
+ metadata=None,
535
+ protocol_options=None,
536
+ ):
537
+ raise NotImplementedError()
538
+
539
+
540
+ class _StreamStreamMultiCallable(face.StreamStreamMultiCallable):
541
+ def __init__(
542
+ self,
543
+ channel,
544
+ group,
545
+ method,
546
+ metadata_transformer,
547
+ request_serializer,
548
+ response_deserializer,
549
+ ):
550
+ self._channel = channel
551
+ self._group = group
552
+ self._method = method
553
+ self._metadata_transformer = metadata_transformer
554
+ self._request_serializer = request_serializer
555
+ self._response_deserializer = response_deserializer
556
+
557
+ def __call__(
558
+ self, request_iterator, timeout, metadata=None, protocol_options=None
559
+ ):
560
+ return _stream_stream(
561
+ self._channel,
562
+ self._group,
563
+ self._method,
564
+ timeout,
565
+ protocol_options,
566
+ metadata,
567
+ self._metadata_transformer,
568
+ request_iterator,
569
+ self._request_serializer,
570
+ self._response_deserializer,
571
+ )
572
+
573
+ def event(
574
+ self,
575
+ receiver,
576
+ abortion_callback,
577
+ timeout,
578
+ metadata=None,
579
+ protocol_options=None,
580
+ ):
581
+ raise NotImplementedError()
582
+
583
+
584
+ class _GenericStub(face.GenericStub):
585
+ def __init__(
586
+ self,
587
+ channel,
588
+ metadata_transformer,
589
+ request_serializers,
590
+ response_deserializers,
591
+ ):
592
+ self._channel = channel
593
+ self._metadata_transformer = metadata_transformer
594
+ self._request_serializers = request_serializers or {}
595
+ self._response_deserializers = response_deserializers or {}
596
+
597
+ def blocking_unary_unary(
598
+ self,
599
+ group,
600
+ method,
601
+ request,
602
+ timeout,
603
+ metadata=None,
604
+ with_call=None,
605
+ protocol_options=None,
606
+ ):
607
+ request_serializer = self._request_serializers.get(
608
+ (
609
+ group,
610
+ method,
611
+ )
612
+ )
613
+ response_deserializer = self._response_deserializers.get(
614
+ (
615
+ group,
616
+ method,
617
+ )
618
+ )
619
+ return _blocking_unary_unary(
620
+ self._channel,
621
+ group,
622
+ method,
623
+ timeout,
624
+ with_call,
625
+ protocol_options,
626
+ metadata,
627
+ self._metadata_transformer,
628
+ request,
629
+ request_serializer,
630
+ response_deserializer,
631
+ )
632
+
633
+ def future_unary_unary(
634
+ self,
635
+ group,
636
+ method,
637
+ request,
638
+ timeout,
639
+ metadata=None,
640
+ protocol_options=None,
641
+ ):
642
+ request_serializer = self._request_serializers.get(
643
+ (
644
+ group,
645
+ method,
646
+ )
647
+ )
648
+ response_deserializer = self._response_deserializers.get(
649
+ (
650
+ group,
651
+ method,
652
+ )
653
+ )
654
+ return _future_unary_unary(
655
+ self._channel,
656
+ group,
657
+ method,
658
+ timeout,
659
+ protocol_options,
660
+ metadata,
661
+ self._metadata_transformer,
662
+ request,
663
+ request_serializer,
664
+ response_deserializer,
665
+ )
666
+
667
+ def inline_unary_stream(
668
+ self,
669
+ group,
670
+ method,
671
+ request,
672
+ timeout,
673
+ metadata=None,
674
+ protocol_options=None,
675
+ ):
676
+ request_serializer = self._request_serializers.get(
677
+ (
678
+ group,
679
+ method,
680
+ )
681
+ )
682
+ response_deserializer = self._response_deserializers.get(
683
+ (
684
+ group,
685
+ method,
686
+ )
687
+ )
688
+ return _unary_stream(
689
+ self._channel,
690
+ group,
691
+ method,
692
+ timeout,
693
+ protocol_options,
694
+ metadata,
695
+ self._metadata_transformer,
696
+ request,
697
+ request_serializer,
698
+ response_deserializer,
699
+ )
700
+
701
+ def blocking_stream_unary(
702
+ self,
703
+ group,
704
+ method,
705
+ request_iterator,
706
+ timeout,
707
+ metadata=None,
708
+ with_call=None,
709
+ protocol_options=None,
710
+ ):
711
+ request_serializer = self._request_serializers.get(
712
+ (
713
+ group,
714
+ method,
715
+ )
716
+ )
717
+ response_deserializer = self._response_deserializers.get(
718
+ (
719
+ group,
720
+ method,
721
+ )
722
+ )
723
+ return _blocking_stream_unary(
724
+ self._channel,
725
+ group,
726
+ method,
727
+ timeout,
728
+ with_call,
729
+ protocol_options,
730
+ metadata,
731
+ self._metadata_transformer,
732
+ request_iterator,
733
+ request_serializer,
734
+ response_deserializer,
735
+ )
736
+
737
+ def future_stream_unary(
738
+ self,
739
+ group,
740
+ method,
741
+ request_iterator,
742
+ timeout,
743
+ metadata=None,
744
+ protocol_options=None,
745
+ ):
746
+ request_serializer = self._request_serializers.get(
747
+ (
748
+ group,
749
+ method,
750
+ )
751
+ )
752
+ response_deserializer = self._response_deserializers.get(
753
+ (
754
+ group,
755
+ method,
756
+ )
757
+ )
758
+ return _future_stream_unary(
759
+ self._channel,
760
+ group,
761
+ method,
762
+ timeout,
763
+ protocol_options,
764
+ metadata,
765
+ self._metadata_transformer,
766
+ request_iterator,
767
+ request_serializer,
768
+ response_deserializer,
769
+ )
770
+
771
+ def inline_stream_stream(
772
+ self,
773
+ group,
774
+ method,
775
+ request_iterator,
776
+ timeout,
777
+ metadata=None,
778
+ protocol_options=None,
779
+ ):
780
+ request_serializer = self._request_serializers.get(
781
+ (
782
+ group,
783
+ method,
784
+ )
785
+ )
786
+ response_deserializer = self._response_deserializers.get(
787
+ (
788
+ group,
789
+ method,
790
+ )
791
+ )
792
+ return _stream_stream(
793
+ self._channel,
794
+ group,
795
+ method,
796
+ timeout,
797
+ protocol_options,
798
+ metadata,
799
+ self._metadata_transformer,
800
+ request_iterator,
801
+ request_serializer,
802
+ response_deserializer,
803
+ )
804
+
805
+ def event_unary_unary(
806
+ self,
807
+ group,
808
+ method,
809
+ request,
810
+ receiver,
811
+ abortion_callback,
812
+ timeout,
813
+ metadata=None,
814
+ protocol_options=None,
815
+ ):
816
+ raise NotImplementedError()
817
+
818
+ def event_unary_stream(
819
+ self,
820
+ group,
821
+ method,
822
+ request,
823
+ receiver,
824
+ abortion_callback,
825
+ timeout,
826
+ metadata=None,
827
+ protocol_options=None,
828
+ ):
829
+ raise NotImplementedError()
830
+
831
+ def event_stream_unary(
832
+ self,
833
+ group,
834
+ method,
835
+ receiver,
836
+ abortion_callback,
837
+ timeout,
838
+ metadata=None,
839
+ protocol_options=None,
840
+ ):
841
+ raise NotImplementedError()
842
+
843
+ def event_stream_stream(
844
+ self,
845
+ group,
846
+ method,
847
+ receiver,
848
+ abortion_callback,
849
+ timeout,
850
+ metadata=None,
851
+ protocol_options=None,
852
+ ):
853
+ raise NotImplementedError()
854
+
855
+ def unary_unary(self, group, method):
856
+ request_serializer = self._request_serializers.get(
857
+ (
858
+ group,
859
+ method,
860
+ )
861
+ )
862
+ response_deserializer = self._response_deserializers.get(
863
+ (
864
+ group,
865
+ method,
866
+ )
867
+ )
868
+ return _UnaryUnaryMultiCallable(
869
+ self._channel,
870
+ group,
871
+ method,
872
+ self._metadata_transformer,
873
+ request_serializer,
874
+ response_deserializer,
875
+ )
876
+
877
+ def unary_stream(self, group, method):
878
+ request_serializer = self._request_serializers.get(
879
+ (
880
+ group,
881
+ method,
882
+ )
883
+ )
884
+ response_deserializer = self._response_deserializers.get(
885
+ (
886
+ group,
887
+ method,
888
+ )
889
+ )
890
+ return _UnaryStreamMultiCallable(
891
+ self._channel,
892
+ group,
893
+ method,
894
+ self._metadata_transformer,
895
+ request_serializer,
896
+ response_deserializer,
897
+ )
898
+
899
+ def stream_unary(self, group, method):
900
+ request_serializer = self._request_serializers.get(
901
+ (
902
+ group,
903
+ method,
904
+ )
905
+ )
906
+ response_deserializer = self._response_deserializers.get(
907
+ (
908
+ group,
909
+ method,
910
+ )
911
+ )
912
+ return _StreamUnaryMultiCallable(
913
+ self._channel,
914
+ group,
915
+ method,
916
+ self._metadata_transformer,
917
+ request_serializer,
918
+ response_deserializer,
919
+ )
920
+
921
+ def stream_stream(self, group, method):
922
+ request_serializer = self._request_serializers.get(
923
+ (
924
+ group,
925
+ method,
926
+ )
927
+ )
928
+ response_deserializer = self._response_deserializers.get(
929
+ (
930
+ group,
931
+ method,
932
+ )
933
+ )
934
+ return _StreamStreamMultiCallable(
935
+ self._channel,
936
+ group,
937
+ method,
938
+ self._metadata_transformer,
939
+ request_serializer,
940
+ response_deserializer,
941
+ )
942
+
943
+ def __enter__(self):
944
+ return self
945
+
946
+ def __exit__(self, exc_type, exc_val, exc_tb):
947
+ return False
948
+
949
+
950
+ class _DynamicStub(face.DynamicStub):
951
+ def __init__(self, backing_generic_stub, group, cardinalities):
952
+ self._generic_stub = backing_generic_stub
953
+ self._group = group
954
+ self._cardinalities = cardinalities
955
+
956
+ def __getattr__(self, attr):
957
+ method_cardinality = self._cardinalities.get(attr)
958
+ if method_cardinality is cardinality.Cardinality.UNARY_UNARY:
959
+ return self._generic_stub.unary_unary(self._group, attr)
960
+ if method_cardinality is cardinality.Cardinality.UNARY_STREAM:
961
+ return self._generic_stub.unary_stream(self._group, attr)
962
+ if method_cardinality is cardinality.Cardinality.STREAM_UNARY:
963
+ return self._generic_stub.stream_unary(self._group, attr)
964
+ if method_cardinality is cardinality.Cardinality.STREAM_STREAM:
965
+ return self._generic_stub.stream_stream(self._group, attr)
966
+ raise AttributeError(
967
+ '_DynamicStub object has no attribute "%s"!' % attr
968
+ )
969
+
970
+ def __enter__(self):
971
+ return self
972
+
973
+ def __exit__(self, exc_type, exc_val, exc_tb):
974
+ return False
975
+
976
+
977
+ def generic_stub(
978
+ channel,
979
+ host,
980
+ metadata_transformer,
981
+ request_serializers,
982
+ response_deserializers,
983
+ ):
984
+ return _GenericStub(
985
+ channel,
986
+ metadata_transformer,
987
+ request_serializers,
988
+ response_deserializers,
989
+ )
990
+
991
+
992
+ def dynamic_stub(
993
+ channel,
994
+ service,
995
+ cardinalities,
996
+ host,
997
+ metadata_transformer,
998
+ request_serializers,
999
+ response_deserializers,
1000
+ ):
1001
+ return _DynamicStub(
1002
+ _GenericStub(
1003
+ channel,
1004
+ metadata_transformer,
1005
+ request_serializers,
1006
+ response_deserializers,
1007
+ ),
1008
+ service,
1009
+ cardinalities,
1010
+ )
lib/python3.12/site-packages/grpc/beta/_metadata.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ return tuple(_beta_metadatum(key, value) for key, value in metadata)
46
+
47
+
48
+ def unbeta(beta_metadata):
49
+ if beta_metadata is None:
50
+ return ()
51
+ return tuple(
52
+ _metadatum(beta_key, beta_value)
53
+ for beta_key, beta_value in beta_metadata
54
+ )
lib/python3.12/site-packages/grpc/beta/_server_adaptations.py ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ error_msg = "add_abortion_callback no longer supported server-side!"
58
+ raise NotImplementedError(error_msg)
59
+
60
+ def cancel(self):
61
+ self._servicer_context.cancel()
62
+
63
+ def protocol_context(self):
64
+ return _ServerProtocolContext(self._servicer_context)
65
+
66
+ def invocation_metadata(self):
67
+ return _metadata.beta(self._servicer_context.invocation_metadata())
68
+
69
+ def initial_metadata(self, initial_metadata):
70
+ self._servicer_context.send_initial_metadata(
71
+ _metadata.unbeta(initial_metadata)
72
+ )
73
+
74
+ def terminal_metadata(self, terminal_metadata):
75
+ self._servicer_context.set_terminal_metadata(
76
+ _metadata.unbeta(terminal_metadata)
77
+ )
78
+
79
+ def code(self, code):
80
+ self._servicer_context.set_code(code)
81
+
82
+ def details(self, details):
83
+ self._servicer_context.set_details(details)
84
+
85
+
86
+ def _adapt_unary_request_inline(unary_request_inline):
87
+ def adaptation(request, servicer_context):
88
+ return unary_request_inline(
89
+ request, _FaceServicerContext(servicer_context)
90
+ )
91
+
92
+ return adaptation
93
+
94
+
95
+ def _adapt_stream_request_inline(stream_request_inline):
96
+ def adaptation(request_iterator, servicer_context):
97
+ return stream_request_inline(
98
+ request_iterator, _FaceServicerContext(servicer_context)
99
+ )
100
+
101
+ return adaptation
102
+
103
+
104
+ class _Callback(stream.Consumer):
105
+ def __init__(self):
106
+ self._condition = threading.Condition()
107
+ self._values = []
108
+ self._terminated = False
109
+ self._cancelled = False
110
+
111
+ def consume(self, value):
112
+ with self._condition:
113
+ self._values.append(value)
114
+ self._condition.notify_all()
115
+
116
+ def terminate(self):
117
+ with self._condition:
118
+ self._terminated = True
119
+ self._condition.notify_all()
120
+
121
+ def consume_and_terminate(self, value):
122
+ with self._condition:
123
+ self._values.append(value)
124
+ self._terminated = True
125
+ self._condition.notify_all()
126
+
127
+ def cancel(self):
128
+ with self._condition:
129
+ self._cancelled = True
130
+ self._condition.notify_all()
131
+
132
+ def draw_one_value(self):
133
+ with self._condition:
134
+ while True:
135
+ if self._cancelled:
136
+ raise abandonment.Abandoned()
137
+ elif self._values:
138
+ return self._values.pop(0)
139
+ elif self._terminated:
140
+ return None
141
+ else:
142
+ self._condition.wait()
143
+
144
+ def draw_all_values(self):
145
+ with self._condition:
146
+ while True:
147
+ if self._cancelled:
148
+ raise abandonment.Abandoned()
149
+ elif self._terminated:
150
+ all_values = tuple(self._values)
151
+ self._values = None
152
+ return all_values
153
+ else:
154
+ self._condition.wait()
155
+
156
+
157
+ def _run_request_pipe_thread(
158
+ request_iterator, request_consumer, servicer_context
159
+ ):
160
+ thread_joined = threading.Event()
161
+
162
+ def pipe_requests():
163
+ for request in request_iterator:
164
+ if not servicer_context.is_active() or thread_joined.is_set():
165
+ return
166
+ request_consumer.consume(request)
167
+ if not servicer_context.is_active() or thread_joined.is_set():
168
+ return
169
+ request_consumer.terminate()
170
+
171
+ request_pipe_thread = threading.Thread(target=pipe_requests)
172
+ request_pipe_thread.daemon = True
173
+ request_pipe_thread.start()
174
+
175
+
176
+ def _adapt_unary_unary_event(unary_unary_event):
177
+ def adaptation(request, servicer_context):
178
+ callback = _Callback()
179
+ if not servicer_context.add_callback(callback.cancel):
180
+ raise abandonment.Abandoned()
181
+ unary_unary_event(
182
+ request,
183
+ callback.consume_and_terminate,
184
+ _FaceServicerContext(servicer_context),
185
+ )
186
+ return callback.draw_all_values()[0]
187
+
188
+ return adaptation
189
+
190
+
191
+ def _adapt_unary_stream_event(unary_stream_event):
192
+ def adaptation(request, servicer_context):
193
+ callback = _Callback()
194
+ if not servicer_context.add_callback(callback.cancel):
195
+ raise abandonment.Abandoned()
196
+ unary_stream_event(
197
+ request, callback, _FaceServicerContext(servicer_context)
198
+ )
199
+ while True:
200
+ response = callback.draw_one_value()
201
+ if response is None:
202
+ return
203
+ else:
204
+ yield response
205
+
206
+ return adaptation
207
+
208
+
209
+ def _adapt_stream_unary_event(stream_unary_event):
210
+ def adaptation(request_iterator, servicer_context):
211
+ callback = _Callback()
212
+ if not servicer_context.add_callback(callback.cancel):
213
+ raise abandonment.Abandoned()
214
+ request_consumer = stream_unary_event(
215
+ callback.consume_and_terminate,
216
+ _FaceServicerContext(servicer_context),
217
+ )
218
+ _run_request_pipe_thread(
219
+ request_iterator, request_consumer, servicer_context
220
+ )
221
+ return callback.draw_all_values()[0]
222
+
223
+ return adaptation
224
+
225
+
226
+ def _adapt_stream_stream_event(stream_stream_event):
227
+ def adaptation(request_iterator, servicer_context):
228
+ callback = _Callback()
229
+ if not servicer_context.add_callback(callback.cancel):
230
+ raise abandonment.Abandoned()
231
+ request_consumer = stream_stream_event(
232
+ callback, _FaceServicerContext(servicer_context)
233
+ )
234
+ _run_request_pipe_thread(
235
+ request_iterator, request_consumer, servicer_context
236
+ )
237
+ while True:
238
+ response = callback.draw_one_value()
239
+ if response is None:
240
+ return
241
+ else:
242
+ yield response
243
+
244
+ return adaptation
245
+
246
+
247
+ class _SimpleMethodHandler(
248
+ collections.namedtuple(
249
+ "_MethodHandler",
250
+ (
251
+ "request_streaming",
252
+ "response_streaming",
253
+ "request_deserializer",
254
+ "response_serializer",
255
+ "unary_unary",
256
+ "unary_stream",
257
+ "stream_unary",
258
+ "stream_stream",
259
+ ),
260
+ ),
261
+ grpc.RpcMethodHandler,
262
+ ):
263
+ pass
264
+
265
+
266
+ def _simple_method_handler( # noqa: PLR0911
267
+ implementation, request_deserializer, response_serializer
268
+ ):
269
+ if implementation.style is style.Service.INLINE:
270
+ if implementation.cardinality is cardinality.Cardinality.UNARY_UNARY:
271
+ return _SimpleMethodHandler(
272
+ False,
273
+ False,
274
+ request_deserializer,
275
+ response_serializer,
276
+ _adapt_unary_request_inline(implementation.unary_unary_inline),
277
+ None,
278
+ None,
279
+ None,
280
+ )
281
+ if implementation.cardinality is cardinality.Cardinality.UNARY_STREAM:
282
+ return _SimpleMethodHandler(
283
+ False,
284
+ True,
285
+ request_deserializer,
286
+ response_serializer,
287
+ None,
288
+ _adapt_unary_request_inline(implementation.unary_stream_inline),
289
+ None,
290
+ None,
291
+ )
292
+ if implementation.cardinality is cardinality.Cardinality.STREAM_UNARY:
293
+ return _SimpleMethodHandler(
294
+ True,
295
+ False,
296
+ request_deserializer,
297
+ response_serializer,
298
+ None,
299
+ None,
300
+ _adapt_stream_request_inline(
301
+ implementation.stream_unary_inline
302
+ ),
303
+ None,
304
+ )
305
+ if implementation.cardinality is cardinality.Cardinality.STREAM_STREAM:
306
+ return _SimpleMethodHandler(
307
+ True,
308
+ True,
309
+ request_deserializer,
310
+ response_serializer,
311
+ None,
312
+ None,
313
+ None,
314
+ _adapt_stream_request_inline(
315
+ implementation.stream_stream_inline
316
+ ),
317
+ )
318
+ elif implementation.style is style.Service.EVENT:
319
+ if implementation.cardinality is cardinality.Cardinality.UNARY_UNARY:
320
+ return _SimpleMethodHandler(
321
+ False,
322
+ False,
323
+ request_deserializer,
324
+ response_serializer,
325
+ _adapt_unary_unary_event(implementation.unary_unary_event),
326
+ None,
327
+ None,
328
+ None,
329
+ )
330
+ if implementation.cardinality is cardinality.Cardinality.UNARY_STREAM:
331
+ return _SimpleMethodHandler(
332
+ False,
333
+ True,
334
+ request_deserializer,
335
+ response_serializer,
336
+ None,
337
+ _adapt_unary_stream_event(implementation.unary_stream_event),
338
+ None,
339
+ None,
340
+ )
341
+ if implementation.cardinality is cardinality.Cardinality.STREAM_UNARY:
342
+ return _SimpleMethodHandler(
343
+ True,
344
+ False,
345
+ request_deserializer,
346
+ response_serializer,
347
+ None,
348
+ None,
349
+ _adapt_stream_unary_event(implementation.stream_unary_event),
350
+ None,
351
+ )
352
+ if implementation.cardinality is cardinality.Cardinality.STREAM_STREAM:
353
+ return _SimpleMethodHandler(
354
+ True,
355
+ True,
356
+ request_deserializer,
357
+ response_serializer,
358
+ None,
359
+ None,
360
+ None,
361
+ _adapt_stream_stream_event(implementation.stream_stream_event),
362
+ )
363
+ raise ValueError()
364
+
365
+
366
+ def _flatten_method_pair_map(method_pair_map):
367
+ method_pair_map = method_pair_map or {}
368
+ flat_map = {}
369
+ for method_pair in method_pair_map:
370
+ method = _common.fully_qualified_method(method_pair[0], method_pair[1])
371
+ flat_map[method] = method_pair_map[method_pair]
372
+ return flat_map
373
+
374
+
375
+ class _GenericRpcHandler(grpc.GenericRpcHandler):
376
+ def __init__(
377
+ self,
378
+ method_implementations,
379
+ multi_method_implementation,
380
+ request_deserializers,
381
+ response_serializers,
382
+ ):
383
+ self._method_implementations = _flatten_method_pair_map(
384
+ method_implementations
385
+ )
386
+ self._request_deserializers = _flatten_method_pair_map(
387
+ request_deserializers
388
+ )
389
+ self._response_serializers = _flatten_method_pair_map(
390
+ response_serializers
391
+ )
392
+ self._multi_method_implementation = multi_method_implementation
393
+
394
+ def service(self, handler_call_details):
395
+ method_implementation = self._method_implementations.get(
396
+ handler_call_details.method
397
+ )
398
+ if method_implementation is not None:
399
+ return _simple_method_handler(
400
+ method_implementation,
401
+ self._request_deserializers.get(handler_call_details.method),
402
+ self._response_serializers.get(handler_call_details.method),
403
+ )
404
+ if self._multi_method_implementation is None:
405
+ return None
406
+ try:
407
+ return None # TODO(nathaniel): call the multimethod.
408
+ except face.NoSuchMethodError:
409
+ return None
410
+
411
+
412
+ class _Server(interfaces.Server):
413
+ def __init__(self, grpc_server):
414
+ self._grpc_server = grpc_server
415
+
416
+ def add_insecure_port(self, address):
417
+ return self._grpc_server.add_insecure_port(address)
418
+
419
+ def add_secure_port(self, address, server_credentials):
420
+ return self._grpc_server.add_secure_port(address, server_credentials)
421
+
422
+ def start(self):
423
+ self._grpc_server.start()
424
+
425
+ def stop(self, grace):
426
+ return self._grpc_server.stop(grace)
427
+
428
+ def __enter__(self):
429
+ self._grpc_server.start()
430
+ return self
431
+
432
+ def __exit__(self, exc_type, exc_val, exc_tb):
433
+ self._grpc_server.stop(None)
434
+ return False
435
+
436
+
437
+ def server(
438
+ service_implementations,
439
+ multi_method_implementation,
440
+ request_deserializers,
441
+ response_serializers,
442
+ thread_pool,
443
+ thread_pool_size,
444
+ ):
445
+ generic_rpc_handler = _GenericRpcHandler(
446
+ service_implementations,
447
+ multi_method_implementation,
448
+ request_deserializers,
449
+ response_serializers,
450
+ )
451
+ if thread_pool is None:
452
+ effective_thread_pool = logging_pool.pool(
453
+ _DEFAULT_POOL_SIZE if thread_pool_size is None else thread_pool_size
454
+ )
455
+ else:
456
+ effective_thread_pool = thread_pool
457
+ return _Server(
458
+ grpc.server(effective_thread_pool, handlers=(generic_rpc_handler,))
459
+ )
lib/python3.12/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
+ )
lib/python3.12/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()
lib/python3.12/site-packages/grpc/beta/utilities.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ elif until is None:
48
+ self._condition.wait()
49
+ else:
50
+ remaining = until - time.time()
51
+ if remaining < 0:
52
+ raise future.TimeoutError()
53
+ else:
54
+ self._condition.wait(timeout=remaining)
55
+
56
+ def _update(self, connectivity):
57
+ with self._condition:
58
+ if (
59
+ not self._cancelled
60
+ and connectivity is interfaces.ChannelConnectivity.READY
61
+ ):
62
+ self._matured = True
63
+ self._channel.unsubscribe(self._update)
64
+ self._condition.notify_all()
65
+ done_callbacks = tuple(self._done_callbacks)
66
+ self._done_callbacks = None
67
+ else:
68
+ return
69
+
70
+ for done_callback in done_callbacks:
71
+ callable_util.call_logging_exceptions(
72
+ done_callback, _DONE_CALLBACK_EXCEPTION_LOG_MESSAGE, self
73
+ )
74
+
75
+ def cancel(self):
76
+ with self._condition:
77
+ if not self._matured:
78
+ self._cancelled = True
79
+ self._channel.unsubscribe(self._update)
80
+ self._condition.notify_all()
81
+ done_callbacks = tuple(self._done_callbacks)
82
+ self._done_callbacks = None
83
+ else:
84
+ return False
85
+
86
+ for done_callback in done_callbacks:
87
+ callable_util.call_logging_exceptions(
88
+ done_callback, _DONE_CALLBACK_EXCEPTION_LOG_MESSAGE, self
89
+ )
90
+
91
+ return True
92
+
93
+ def cancelled(self):
94
+ with self._condition:
95
+ return self._cancelled
96
+
97
+ def running(self):
98
+ with self._condition:
99
+ return not self._cancelled and not self._matured
100
+
101
+ def done(self):
102
+ with self._condition:
103
+ return self._cancelled or self._matured
104
+
105
+ def result(self, timeout=None):
106
+ self._block(timeout)
107
+
108
+ def exception(self, timeout=None):
109
+ self._block(timeout)
110
+
111
+ def traceback(self, timeout=None):
112
+ self._block(timeout)
113
+
114
+ def add_done_callback(self, fn):
115
+ with self._condition:
116
+ if not self._cancelled and not self._matured:
117
+ self._done_callbacks.append(fn)
118
+ return
119
+
120
+ fn(self)
121
+
122
+ def start(self):
123
+ with self._condition:
124
+ self._channel.subscribe(self._update, try_to_connect=True)
125
+
126
+ def __del__(self):
127
+ with self._condition:
128
+ if not self._cancelled and not self._matured:
129
+ self._channel.unsubscribe(self._update)
130
+
131
+
132
+ def channel_ready_future(channel):
133
+ """Creates a future.Future tracking when an implementations.Channel is ready.
134
+
135
+ Cancelling the returned future.Future does not tell the given
136
+ implementations.Channel to abandon attempts it may have been making to
137
+ connect; cancelling merely deactivates the return future.Future's
138
+ subscription to the given implementations.Channel's connectivity.
139
+
140
+ Args:
141
+ channel: An implementations.Channel.
142
+
143
+ Returns:
144
+ A future.Future that matures when the given Channel has connectivity
145
+ interfaces.ChannelConnectivity.READY.
146
+ """
147
+ ready_future = _ChannelReadyFuture(channel)
148
+ ready_future.start()
149
+ return ready_future
lib/python3.12/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.
lib/python3.12/site-packages/grpc/framework/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (193 Bytes). View file
 
lib/python3.12/site-packages/grpc/framework/common/__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.
lib/python3.12/site-packages/grpc/framework/common/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (200 Bytes). View file
 
lib/python3.12/site-packages/grpc/framework/common/__pycache__/cardinality.cpython-312.pyc ADDED
Binary file (867 Bytes). View file
 
lib/python3.12/site-packages/grpc/framework/common/__pycache__/style.cpython-312.pyc ADDED
Binary file (691 Bytes). View file
 
lib/python3.12/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"
lib/python3.12/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"
lib/python3.12/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.
lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (204 Bytes). View file
 
lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/abandonment.cpython-312.pyc ADDED
Binary file (679 Bytes). View file
 
lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/callable_util.cpython-312.pyc ADDED
Binary file (3.95 kB). View file
 
lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/future.cpython-312.pyc ADDED
Binary file (7.97 kB). View file
 
lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/logging_pool.cpython-312.pyc ADDED
Binary file (3.15 kB). View file
 
lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/stream.cpython-312.pyc ADDED
Binary file (1.53 kB). View file
 
lib/python3.12/site-packages/grpc/framework/foundation/__pycache__/stream_util.cpython-312.pyc ADDED
Binary file (7.63 kB). View file
 
lib/python3.12/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
+ """
lib/python3.12/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 = enum.auto()
42
+ RAISED = enum.auto()
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)
lib/python3.12/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()
lib/python3.12/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))
lib/python3.12/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()
lib/python3.12/site-packages/grpc/framework/foundation/stream_util.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ raise StopIteration()
81
+
82
+
83
+ class ThreadSwitchingConsumer(stream.Consumer):
84
+ """A Consumer decorator that affords serialization and asynchrony."""
85
+
86
+ def __init__(self, sink, pool):
87
+ self._lock = threading.Lock()
88
+ self._sink = sink
89
+ self._pool = pool
90
+ # True if self._spin has been submitted to the pool to be called once and
91
+ # that call has not yet returned, False otherwise.
92
+ self._spinning = False
93
+ self._values = []
94
+ self._active = True
95
+
96
+ def _spin(self, sink, value, terminate):
97
+ while True:
98
+ try:
99
+ if value is _NO_VALUE:
100
+ sink.terminate()
101
+ elif terminate:
102
+ sink.consume_and_terminate(value)
103
+ else:
104
+ sink.consume(value)
105
+ except Exception as e: # pylint:disable=broad-except
106
+ _LOGGER.exception(e)
107
+
108
+ with self._lock:
109
+ if terminate:
110
+ self._spinning = False
111
+ return
112
+ if self._values:
113
+ value = self._values.pop(0)
114
+ terminate = not self._values and not self._active
115
+ elif not self._active:
116
+ value = _NO_VALUE
117
+ terminate = True
118
+ else:
119
+ self._spinning = False
120
+ return
121
+
122
+ def consume(self, value):
123
+ with self._lock:
124
+ if self._active:
125
+ if self._spinning:
126
+ self._values.append(value)
127
+ else:
128
+ self._pool.submit(self._spin, self._sink, value, False)
129
+ self._spinning = True
130
+
131
+ def terminate(self):
132
+ with self._lock:
133
+ if self._active:
134
+ self._active = False
135
+ if not self._spinning:
136
+ self._pool.submit(self._spin, self._sink, _NO_VALUE, True)
137
+ self._spinning = True
138
+
139
+ def consume_and_terminate(self, value):
140
+ with self._lock:
141
+ if self._active:
142
+ self._active = False
143
+ if self._spinning:
144
+ self._values.append(value)
145
+ else:
146
+ self._pool.submit(self._spin, self._sink, value, True)
147
+ self._spinning = True