protoplasm6 6.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. protoplasm6/__init__.py +5 -0
  2. protoplasm6/bases/__init__.py +3 -0
  3. protoplasm6/bases/_client.py +83 -0
  4. protoplasm6/bases/_exwrap.py +113 -0
  5. protoplasm6/bases/_metadata.py +89 -0
  6. protoplasm6/bases/_methwrap.py +220 -0
  7. protoplasm6/bases/_remotewrap.py +268 -0
  8. protoplasm6/bases/_servicer.py +46 -0
  9. protoplasm6/bases/dataclass_bases.py +2 -0
  10. protoplasm6/bases/grpc_bases.py +4 -0
  11. protoplasm6/casting/__init__.py +5 -0
  12. protoplasm6/casting/casters.py +89 -0
  13. protoplasm6/casting/castutils.py +379 -0
  14. protoplasm6/casting/converters/__init__.py +128 -0
  15. protoplasm6/casting/converters/_base.py +39 -0
  16. protoplasm6/casting/converters/_dict.py +39 -0
  17. protoplasm6/casting/converters/_direct.py +461 -0
  18. protoplasm6/casting/dictator.py +85 -0
  19. protoplasm6/casting/dictators/__init__.py +375 -0
  20. protoplasm6/casting/objectifier.py +100 -0
  21. protoplasm6/decorators/__init__.py +1 -0
  22. protoplasm6/decorators/apihelpers.py +100 -0
  23. protoplasm6/errors/__init__.py +4 -0
  24. protoplasm6/errors/_api.py +238 -0
  25. protoplasm6/errors/_base.py +31 -0
  26. protoplasm6/grpcserver.py +53 -0
  27. protoplasm6/loader/__init__.py +17 -0
  28. protoplasm6/plasm/__init__.py +7 -0
  29. protoplasm6/structs/__init__.py +8 -0
  30. protoplasm6/structs/_base.py +4 -0
  31. protoplasm6/structs/_ctx/__init__.py +2 -0
  32. protoplasm6/structs/_ctx/_basectx.py +15 -0
  33. protoplasm6/structs/_ctx/_grpcctx.py +53 -0
  34. protoplasm6/structs/_methodtype.py +33 -0
  35. protoplasm6/structs/_oneofs.py +95 -0
  36. protoplasm6/structs/_reqestsiter.py +32 -0
  37. protoplasm6/structs/_responseiter.py +172 -0
  38. protoplasm6/structs/dataclassbase.py +228 -0
  39. protoplasm6-6.1.0.dist-info/METADATA +402 -0
  40. protoplasm6-6.1.0.dist-info/RECORD +43 -0
  41. protoplasm6-6.1.0.dist-info/WHEEL +5 -0
  42. protoplasm6-6.1.0.dist-info/licenses/LICENSE +22 -0
  43. protoplasm6-6.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,5 @@
1
+ __version__ = '6.1.0'
2
+
3
+ __author__ = 'Thordur Matthiasson <doddi79@gmail.com>'
4
+ __license__ = 'MIT License'
5
+ __copyright__ = 'Copyright 2026 - Thordur Matthiasson, Copyright 2019-2024 - CCP Games ehf'
@@ -0,0 +1,3 @@
1
+ from .dataclass_bases import *
2
+ from .grpc_bases import *
3
+ from ._exwrap import *
@@ -0,0 +1,83 @@
1
+ __all__ = [
2
+ 'BaseGrpcClientImplementation',
3
+ ]
4
+ import grpc # noqa
5
+
6
+ from protoplasm6.structs import *
7
+ from ._remotewrap import *
8
+ from ._metadata import *
9
+ from .dataclass_bases import *
10
+
11
+ import logging
12
+ log = logging.getLogger(__name__)
13
+
14
+
15
+ class BaseGrpcClientImplementation:
16
+ def __init__(self, service_stub_cls: Type, grpc_host='localhost:50051',
17
+ credentials: Optional[Union[bool, grpc.ChannelCredentials]] = None,
18
+ options: Optional[Dict] = None, metadata: MetadataSpec = None, *args, **kwargs):
19
+ """
20
+ credentials:
21
+ - None -> Automatic... will use `grpc.ssl_channel_credentials()` and secure_channel if port is 443
22
+ - True -> Will use `grpc.ssl_channel_credentials()` and secure_channel
23
+ - False -> Will use insecure_channel and no creds
24
+ - grpc.ChannelCredentials -> Will use given creds and secure_channel
25
+
26
+ metadata:
27
+ - Metadata (i.e. headers, e.g. for authorization) added to every call
28
+ made by this client, either as a dict, an iterable of (key, value)
29
+ pairs or a callable returning either of those (which gets called for
30
+ every call, e.g. to fetch a fresh access token). It can also be set
31
+ (or changed) later via the `metadata` attribute.
32
+ - See also `call_metadata()` for adding metadata to calls within a
33
+ `with` block.
34
+ """
35
+ self.metadata: MetadataSpec = metadata
36
+ options = options or {}
37
+ if 'grpc.enable_http_proxy' not in options:
38
+ options['grpc.enable_http_proxy'] = 0
39
+ option_tuple = tuple(options.items())
40
+
41
+ # Backwards compatibility
42
+ if kwargs:
43
+ if not credentials and 'credential' in kwargs:
44
+ credentials = kwargs.pop('credential')
45
+ log.warning('Deprecated keyword "credential" used when initiating BaseGrpcClient.'
46
+ ' Please use "credentials" instead!')
47
+
48
+ # This keeps happening...!
49
+ if ':' not in grpc_host:
50
+ if credentials:
51
+ log.warning('No port used in host... adding 443 by default (cause this is a secure connection)!')
52
+ grpc_host = f'{grpc_host}:443'
53
+ else:
54
+ log.warning('No port used in host... adding 50051 by default!')
55
+ grpc_host = f'{grpc_host}:50051'
56
+
57
+ if credentials is None:
58
+ if grpc_host.endswith(':443'):
59
+ credentials = grpc.ssl_channel_credentials()
60
+ elif credentials is True:
61
+ credentials = grpc.ssl_channel_credentials()
62
+
63
+ if credentials:
64
+ self.grpc_channel: grpc.Channel = grpc.secure_channel(grpc_host, credentials, options=option_tuple)
65
+ else:
66
+ self.grpc_channel: grpc.Channel = grpc.insecure_channel(grpc_host, options=option_tuple)
67
+
68
+ self.stub = service_stub_cls(self.grpc_channel)
69
+
70
+ def call_metadata(self, metadata: MetadataSpec = None, **kwargs: Union[str, bytes]):
71
+ """Context manager that adds the given metadata to every call made by
72
+ this client (only) within its block (in the current thread/context).
73
+
74
+ ```python
75
+ with orders.call_metadata({'authorization': f'Bearer {token}'}):
76
+ orders.get_order('o1')
77
+ ```
78
+ """
79
+ return call_metadata(metadata, _client=self, **kwargs)
80
+
81
+ def _forward_to_grpc(self, request_cls: Type[T_DCB], stub_method: Callable,
82
+ *args, **kwargs) -> Optional[Union[Any, Tuple[Any], Iterable[T_DCB]]]:
83
+ return RemoteMethodWrapper(self, request_cls, stub_method, *args, **kwargs).call()
@@ -0,0 +1,113 @@
1
+ __all__ = [
2
+ 'GrpcExceptionWrapper',
3
+ ]
4
+
5
+ import grpc
6
+ from typing import *
7
+ from protoplasm6 import errors
8
+
9
+ _STATUS_CODE_TO_EXCEPTION_MAP = {
10
+ grpc.StatusCode.UNKNOWN: errors.api.Unknown,
11
+ grpc.StatusCode.NOT_FOUND: errors.api.NotFound,
12
+ grpc.StatusCode.INVALID_ARGUMENT: errors.api.InvalidArgument,
13
+ grpc.StatusCode.UNIMPLEMENTED: errors.api.Unimplemented,
14
+ grpc.StatusCode.PERMISSION_DENIED: errors.api.PermissionDenied,
15
+ grpc.StatusCode.UNAUTHENTICATED: errors.api.Unauthenticated,
16
+ grpc.StatusCode.UNAVAILABLE: errors.api.Unavailable,
17
+ grpc.StatusCode.DEADLINE_EXCEEDED: errors.api.DeadlineExceeded,
18
+ grpc.StatusCode.ALREADY_EXISTS: errors.api.AlreadyExists,
19
+ grpc.StatusCode.FAILED_PRECONDITION: errors.api.FailedPrecondition,
20
+ grpc.StatusCode.DATA_LOSS: errors.api.DataLoss,
21
+ grpc.StatusCode.RESOURCE_EXHAUSTED: errors.api.ResourceExhausted,
22
+ grpc.StatusCode.OUT_OF_RANGE: errors.api.OutOfRange,
23
+ grpc.StatusCode.INTERNAL: errors.api.Internal,
24
+ grpc.StatusCode.CANCELLED: errors.api.Cancelled,
25
+ grpc.StatusCode.ABORTED: errors.api.Aborted,
26
+ }
27
+
28
+
29
+ class GrpcExceptionWrapper(object):
30
+ def __init__(self, ex: grpc.RpcError):
31
+ self.ex = ex
32
+ self._code = False
33
+ self._debug_error_string = False
34
+ self._details = False
35
+ self._initial_metadata = False
36
+ self._traceback = False
37
+ self._trailing_metadata = False
38
+
39
+ @property
40
+ def code(self) -> Optional[grpc.StatusCode]:
41
+ if self._code is False:
42
+ self._code = None
43
+ code_call = getattr(self.ex, 'code', None)
44
+ if code_call and hasattr(code_call, '__call__'):
45
+ self._code = code_call()
46
+ return self._code
47
+
48
+ @property
49
+ def debug_error_string(self) -> str:
50
+ if self._debug_error_string is False:
51
+ self._debug_error_string = ''
52
+ code_call = getattr(self.ex, 'debug_error_string', None)
53
+ if code_call and hasattr(code_call, '__call__'):
54
+ self._debug_error_string = code_call()
55
+ return self._debug_error_string
56
+
57
+ @property
58
+ def details(self) -> str:
59
+ if self._details is False:
60
+ self._details = ''
61
+ code_call = getattr(self.ex, 'details', None)
62
+ if code_call and hasattr(code_call, '__call__'):
63
+ self._details = code_call()
64
+ return self._details
65
+
66
+ @property
67
+ def initial_metadata(self) -> dict:
68
+ if self._initial_metadata is False:
69
+ self._initial_metadata = {}
70
+ code_call = getattr(self.ex, 'initial_metadata', None)
71
+ if code_call and hasattr(code_call, '__call__'):
72
+ for k, v in code_call():
73
+ k = k.lower()
74
+ if k not in self._initial_metadata:
75
+ self._initial_metadata[k] = v
76
+ else:
77
+ if not isinstance(self._initial_metadata[k], list):
78
+ self._initial_metadata[k] = [self._initial_metadata[k]]
79
+ self._initial_metadata[k].append(v)
80
+ return self._initial_metadata
81
+
82
+ @property
83
+ def traceback(self):
84
+ if self._traceback is False:
85
+ self._traceback = None
86
+ code_call = getattr(self.ex, 'traceback', None)
87
+ if code_call and hasattr(code_call, '__call__'):
88
+ self._traceback = code_call()
89
+ return self._traceback
90
+
91
+ @property
92
+ def trailing_metadata(self) -> dict:
93
+ if self._trailing_metadata is False:
94
+ self._trailing_metadata = {}
95
+ code_call = getattr(self.ex, 'trailing_metadata', None)
96
+ if code_call and hasattr(code_call, '__call__'):
97
+ for k, v in code_call():
98
+ k = k.lower()
99
+ if k not in self._trailing_metadata:
100
+ self._trailing_metadata[k] = v
101
+ else:
102
+ if not isinstance(self._trailing_metadata[k], list):
103
+ self._trailing_metadata[k] = [self._trailing_metadata[k]]
104
+ self._trailing_metadata[k].append(v)
105
+ return self._trailing_metadata
106
+
107
+ def get_reraise(self):
108
+ return _STATUS_CODE_TO_EXCEPTION_MAP.get(self.code, errors.api.Unknown)(self.details,
109
+ ref_code=self.trailing_metadata.get('ref_code', None))
110
+
111
+
112
+
113
+
@@ -0,0 +1,89 @@
1
+ """Custom call metadata (i.e. HTTP/2 headers, e.g. for authorization) for gRPC
2
+ calls made by protoplasm6 clients.
3
+ """
4
+ __all__ = [
5
+ 'MetadataSpec',
6
+ 'call_metadata',
7
+ 'normalize_metadata',
8
+ 'resolve_metadata',
9
+ ]
10
+ import contextlib
11
+ import contextvars
12
+
13
+ from protoplasm6.structs import *
14
+
15
+ MetadataValue = Union[str, bytes]
16
+ MetadataPairs = Union[Mapping[str, MetadataValue], Iterable[Tuple[str, MetadataValue]]]
17
+ MetadataSpec = Union[MetadataPairs, Callable[[], Optional[MetadataPairs]], None]
18
+ """Metadata can be given as a dict, an iterable of (key, value) pairs or a
19
+ callable (taking no arguments) returning either of those, which gets called
20
+ for every single call (e.g. to fetch a fresh access token)."""
21
+
22
+ # Stack of (client or None for "all clients", metadata spec)
23
+ _CONTEXT: contextvars.ContextVar[Tuple[Tuple[Any, MetadataSpec], ...]] = contextvars.ContextVar(
24
+ 'protoplasm6_call_metadata', default=())
25
+
26
+
27
+ def normalize_metadata(metadata: MetadataSpec) -> List[Tuple[str, MetadataValue]]:
28
+ """Turns a metadata spec into a list of (key, value) tuples with lower case
29
+ keys (as gRPC requires).
30
+ """
31
+ if metadata is None:
32
+ return []
33
+ if callable(metadata):
34
+ metadata = metadata()
35
+ if metadata is None:
36
+ return []
37
+ if isinstance(metadata, Mapping):
38
+ metadata = metadata.items()
39
+ pairs = []
40
+ for key, value in metadata:
41
+ key = str(key).lower()
42
+ if not key.endswith('-bin') and not isinstance(value, str):
43
+ value = str(value)
44
+ pairs.append((key, value))
45
+ return pairs
46
+
47
+
48
+ @contextlib.contextmanager
49
+ def call_metadata(metadata: MetadataSpec = None, *, _client: Any = None, **kwargs: MetadataValue):
50
+ """Context manager that adds the given metadata (e.g. authorization
51
+ headers) to every gRPC call made by protoplasm6 clients within its block
52
+ (in the current thread/context only).
53
+
54
+ ```python
55
+ from protoplasm6 import plasm
56
+
57
+ with plasm.call_metadata({'authorization': f'Bearer {token}'}):
58
+ orders.get_order('o1')
59
+
60
+ with plasm.call_metadata(x_request_id='abc123'): # Underscores become dashes in keywords
61
+ orders.get_order('o1')
62
+ ```
63
+
64
+ These can be nested and combined with the client's own metadata (see
65
+ `BaseGrpcClientImplementation`). Use `client.call_metadata(...)` to only
66
+ add metadata to the calls of a single client.
67
+ """
68
+ entries = []
69
+ if metadata is not None:
70
+ entries.append((_client, metadata))
71
+ if kwargs:
72
+ entries.append((_client, {k.replace('_', '-'): v for k, v in kwargs.items()}))
73
+ token = _CONTEXT.set(_CONTEXT.get() + tuple(entries))
74
+ try:
75
+ yield
76
+ finally:
77
+ _CONTEXT.reset(token)
78
+
79
+
80
+ def resolve_metadata(client: Any) -> Optional[List[Tuple[str, MetadataValue]]]:
81
+ """Returns all the metadata for a call made by the given client: The
82
+ client's own metadata followed by any metadata added via `call_metadata()`
83
+ (for all clients or for this one) in the order it was added.
84
+ """
85
+ pairs = normalize_metadata(getattr(client, 'metadata', None))
86
+ for target, spec in _CONTEXT.get():
87
+ if target is None or target is client:
88
+ pairs.extend(normalize_metadata(spec))
89
+ return pairs or None
@@ -0,0 +1,220 @@
1
+ __all__ = [
2
+ 'PlasmMethodWrapper',
3
+ ]
4
+
5
+ import grpc
6
+ import json
7
+ import uuid
8
+
9
+ from batutils.tpu import casting as typeutils_casting
10
+
11
+ from protoplasm6 import casting
12
+ from protoplasm6 import errors
13
+ from protoplasm6.bases.dataclass_bases import *
14
+ from protoplasm6.structs import *
15
+
16
+ if TYPE_CHECKING:
17
+ from .grpc_bases import BaseGrpcServicer
18
+
19
+ import logging
20
+ log = logging.getLogger(__name__)
21
+
22
+
23
+ class PlasmMethodWrapper:
24
+ JSON_SERIALIZER = typeutils_casting.JsonSafeSerializer()
25
+
26
+ __slots__ = ('servicer', 'data_cls', 'impl_method', 'request', 'request_iterator', 'context', 'request_dc',
27
+ 'unpacked_args', 'raw_response_iterable', 'raw_response', 'response_dc', 'response_proto',
28
+ '_ref_code', 'method_io')
29
+
30
+ def __init__(self, servicer: 'BaseGrpcServicer', data_cls: Type[T_DCB], impl_method: Callable,
31
+ request_or_iterator: Union[GeneratedProtocolMessageType, Iterable[GeneratedProtocolMessageType]],
32
+ context: grpc.ServicerContext, method_io: MethodIoType):
33
+ """Container and worker for processing Servicer (i.e. Server Side)
34
+ requests and responses, packing, unpacking, casting and so on as well as
35
+ handling errors and log-dumping and all that bulky noisy jass that just
36
+ clutters up the actual servicer code.
37
+ """
38
+ self.servicer: 'BaseGrpcServicer' = servicer
39
+ self.data_cls: Type[DataclassBase] = data_cls
40
+ self.impl_method: Callable = impl_method
41
+
42
+ self.request: Optional[GeneratedProtocolMessageType] = None
43
+ self.request_iterator: Optional[Iterable[GeneratedProtocolMessageType]] = None
44
+
45
+ self.context: grpc.ServicerContext = context
46
+ self.method_io: MethodIoType = method_io
47
+
48
+ if self.method_io.is_input_stream():
49
+ self.request_iterator = request_or_iterator
50
+ else:
51
+ self.request = request_or_iterator
52
+
53
+ self.request_dc: Optional[DataclassBase] = None
54
+ self.unpacked_args: Optional[Dict] = None
55
+ self.raw_response_iterable: Optional[Iterable[Any]] = None
56
+ self.raw_response: Any = None
57
+ self.response_dc: Optional[DataclassBase] = None
58
+ self.response_proto: Optional[GeneratedProtocolMessageType] = None
59
+ self._ref_code: str = ''
60
+
61
+ @property
62
+ def request_dc_iterator(self) -> Iterable[T_DCB]:
63
+ for raw_req in self.request_iterator:
64
+ self.request = raw_req
65
+ self._p2d()
66
+ yield self.request_dc
67
+
68
+ @property
69
+ def ref_code(self) -> str:
70
+ if not self._ref_code:
71
+ self._ref_code = str(uuid.uuid4())
72
+ return self._ref_code
73
+
74
+ def _get_dump(self, **kwargs) -> dict:
75
+ d = {
76
+ 'ref_code': self.ref_code,
77
+ 'class': str(self.servicer.__class__),
78
+ 'data_cls': str(self.data_cls),
79
+ 'impl_method': str(self.impl_method),
80
+ 'request': str(self.request),
81
+ 'method_io': self.method_io,
82
+ }
83
+
84
+ if self.context:
85
+ try:
86
+ d['invocation_metadata'] = dict(self.context.invocation_metadata()) # noqa
87
+ except Exception:
88
+ d['invocation_metadata'] = 'FAILED TO FETCH METADATA!'
89
+
90
+ if self.request_dc:
91
+ d['request_dc'] = repr(self.request_dc)
92
+ if self.unpacked_args:
93
+ d['unpacked_args'] = self.unpacked_args
94
+ if self.raw_response:
95
+ d['raw_response'] = repr(self.raw_response)
96
+ if self.response_dc:
97
+ d['response_dc'] = repr(self.response_dc)
98
+ if self.response_proto:
99
+ d['response_proto'] = repr(self.response_proto)
100
+
101
+ if kwargs:
102
+ d.update(kwargs)
103
+
104
+ return d
105
+
106
+ def _get_json_dump(self, **kwargs) -> str:
107
+ return json.dumps(self.JSON_SERIALIZER.serialize(self._get_dump(**kwargs)), indent=4)
108
+
109
+ def _abort(self, message: str, error_code, ex):
110
+ self.context.set_trailing_metadata([('ref_code', self.ref_code)])
111
+ log.exception(f'{message}::{ex!r}\n---\n{self._get_json_dump(details=repr(ex))}')
112
+ return self.context.abort(error_code, f'{ex!r};\nref_code={self.ref_code}')
113
+
114
+ def _abort_internal(self, ex: errors.ServiceApiException):
115
+ details = [ex.details]
116
+ if ex.should_log:
117
+ details.append(f'ref_code={self.ref_code}')
118
+ self.context.set_trailing_metadata([('ref_code', self.ref_code)])
119
+ log.exception(f'Logging ServiceApiException::{ex!r}\n---\n'
120
+ f'{self._get_json_dump(status_code=ex.status_code, details=ex.details)}')
121
+ return self.context.abort(ex.status_code, ';\n'.join(details))
122
+
123
+ def _p2d(self):
124
+ try:
125
+ self.request_dc = casting.proto_to_dataclass(self.request)
126
+ except Exception as ex:
127
+ return self._abort('pre_request_error;Error while casting proto to dataclass',
128
+ grpc.StatusCode.FAILED_PRECONDITION, ex)
129
+
130
+ def _unpack(self):
131
+ try:
132
+ if self.method_io.is_input_stream():
133
+ self.unpacked_args = {'request_iterator': self.request_dc_iterator}
134
+ else:
135
+ self.unpacked_args = self.request_dc.unpack()
136
+ except Exception as ex:
137
+ return self._abort('pre_request_error;Error while unpacking arguments',
138
+ grpc.StatusCode.FAILED_PRECONDITION, ex)
139
+
140
+ def _add_ctx(self):
141
+ try:
142
+ ctx_name = getattr(self.impl_method, '__takes_context_as__', False)
143
+ if ctx_name:
144
+ self.unpacked_args[ctx_name] = GrpcContext(self.context, self.method_io)
145
+ except Exception as ex:
146
+ return self._abort('pre_request_error;Error while adding context to parameters',
147
+ grpc.StatusCode.FAILED_PRECONDITION, ex)
148
+
149
+ def prepare(self):
150
+ if not self.method_io.is_input_stream():
151
+ self._p2d()
152
+ self._unpack()
153
+ self._add_ctx()
154
+
155
+ def _pack(self):
156
+ self.response_dc = None
157
+ try:
158
+ self.response_dc = casting.pack_dataclass_response(self.data_cls, self.raw_response)
159
+ except Exception as ex:
160
+ return self._abort('post_request_error;Error while packing raw response to dataclass',
161
+ grpc.StatusCode.CANCELLED, ex)
162
+
163
+ def _d2p(self):
164
+ self.response_proto = None
165
+ try:
166
+ self.response_proto = casting.dataclass_to_proto(self.response_dc)
167
+ except Exception as ex:
168
+ return self._abort('post_request_error;Error while casting dataclass to proto',
169
+ grpc.StatusCode.CANCELLED, ex)
170
+
171
+ def call(self) -> Union[GeneratedProtocolMessageType,
172
+ Iterable[GeneratedProtocolMessageType]]:
173
+ # Prepare the call
174
+ self.prepare()
175
+
176
+ try:
177
+ if self.method_io.is_output_stream():
178
+ self.raw_response_iterable = self.impl_method(**self.unpacked_args)
179
+
180
+ else:
181
+ self.raw_response = self.impl_method(**self.unpacked_args)
182
+
183
+ except errors.ServiceApiException as ex:
184
+ return self._abort_internal(ex)
185
+
186
+ except Exception as ex:
187
+ return self._abort('Unexpected exception in implementation method',
188
+ grpc.StatusCode.UNKNOWN, ex)
189
+
190
+ if not self.method_io.is_output_stream():
191
+ # Pack up results for unary results
192
+ self._pack()
193
+ self._d2p()
194
+ return self.response_proto
195
+
196
+ else:
197
+ return self.iterable_response_proto
198
+
199
+ @property
200
+ def wrapped_iterable_raw_response(self) -> Iterable[Any]:
201
+ try:
202
+ for res in self.raw_response_iterable:
203
+ yield res
204
+ except errors.ServiceApiException as ex:
205
+ return self._abort_internal(ex)
206
+ except Exception as ex:
207
+ return self._abort('Unexpected exception in implementation method',
208
+ grpc.StatusCode.UNKNOWN, ex)
209
+
210
+ @property
211
+ def iterable_response_proto(self) -> Iterable[GeneratedProtocolMessageType]:
212
+ for res in self.wrapped_iterable_raw_response:
213
+ self.raw_response = res
214
+ self._pack()
215
+ self._d2p()
216
+ yield self.response_proto
217
+
218
+ self.raw_response = None
219
+ self.response_dc = None
220
+ self.response_proto = None