dex-python-sdk 0.2.10__tar.gz → 0.3.1__tar.gz
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.
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/PKG-INFO +3 -2
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/README.md +2 -1
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/__init__.py +6 -2
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_async_value_hydrator.py +28 -2
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_async_worker_dispatcher.py +61 -8
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_invocation_context.py +32 -28
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_value_hydrator.py +28 -2
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_worker_dispatcher.py +205 -6
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/async_client.py +26 -1
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/channel.py +2 -2
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/client.py +26 -1
- dex_python_sdk-0.3.1/dex/dexpb/dex_pb2.py +495 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/dexpb/dex_pb2.pyi +124 -22
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/flow.py +35 -4
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/flow_options.py +69 -3
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/runtime_errors.py +8 -2
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/step.py +26 -2
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/pyproject.toml +1 -1
- dex_python_sdk-0.2.10/dex/dexpb/dex_pb2.py +0 -477
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/LEGACY_NOTICES.md +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/LICENSE +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_async_worker_service.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_grpc_errors.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_native.pyi +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_utils.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_value_mapper.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/_worker_service.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/async_worker.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/attribute.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/blob_cache.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/client_options.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/codec.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/condition.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/context.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/dexpb/__init__.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/dexpb/dex_pb2_grpc.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/flow_config.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/flow_info.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/flow_result.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/py.typed +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/step_execution.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/stream.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/subflow.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/timer.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/wait.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/worker.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/dex/worker_options.py +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/Cargo.lock +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/Cargo.toml +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache/Cargo.toml +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache/LICENSE +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache/src/config.rs +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache/src/entry.rs +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache/src/error.rs +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache/src/format.rs +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache/src/lib.rs +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache/src/policy.rs +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache/src/store.rs +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache/tests/blob_cache_integration.rs +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache-python/Cargo.toml +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache-python/LICENSE +0 -0
- {dex_python_sdk-0.2.10 → dex_python_sdk-0.3.1}/sdk-rust/crates/dex-blob-cache-python/src/lib.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dex-python-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Requires-Dist: grpcio>=1.83.0
|
|
5
5
|
Requires-Dist: grpcio-status>=1.83.0
|
|
6
6
|
Requires-Dist: protobuf>=7.35.1
|
|
@@ -49,7 +49,8 @@ def snapshot(self, context: dex.Context) -> dex.RPCResult[Snapshot]:
|
|
|
49
49
|
Put an AttributeMap or ChannelMap directly in its plural load option when every
|
|
50
50
|
current instance is needed. Use the singular instance options for the less common
|
|
51
51
|
exact-instance case. A selected empty queue returns an empty tuple; reading an
|
|
52
|
-
unselected map entry
|
|
52
|
+
unselected map entry raises **AttributeMapNotLoadedError**. An unselected pending-message
|
|
53
|
+
snapshot raises **ChannelMessagesNotLoadedError**.
|
|
53
54
|
Pending messages preserve FIFO order and include the server-assigned message ID.
|
|
54
55
|
The snapshot does not change after the handler stages a publish or deletion.
|
|
55
56
|
|
|
@@ -33,7 +33,8 @@ def snapshot(self, context: dex.Context) -> dex.RPCResult[Snapshot]:
|
|
|
33
33
|
Put an AttributeMap or ChannelMap directly in its plural load option when every
|
|
34
34
|
current instance is needed. Use the singular instance options for the less common
|
|
35
35
|
exact-instance case. A selected empty queue returns an empty tuple; reading an
|
|
36
|
-
unselected map entry
|
|
36
|
+
unselected map entry raises **AttributeMapNotLoadedError**. An unselected pending-message
|
|
37
|
+
snapshot raises **ChannelMessagesNotLoadedError**.
|
|
37
38
|
Pending messages preserve FIFO order and include the server-assigned message ID.
|
|
38
39
|
The snapshot does not change after the handler stages a publish or deletion.
|
|
39
40
|
|
|
@@ -47,6 +47,7 @@ from dex.flow_info import (
|
|
|
47
47
|
SearchFlowsPage,
|
|
48
48
|
)
|
|
49
49
|
from dex.flow_options import (
|
|
50
|
+
FlowTimeoutHandlerOptions,
|
|
50
51
|
FlowTimeoutPolicy,
|
|
51
52
|
IdReusePolicy,
|
|
52
53
|
StartFlowOptions,
|
|
@@ -62,6 +63,8 @@ from dex.flow_result import FlowResult, StepCompletion
|
|
|
62
63
|
from dex.runtime_errors import (
|
|
63
64
|
DexServiceError,
|
|
64
65
|
ErrorSubStatus,
|
|
66
|
+
AttributeMapNotLoadedError,
|
|
67
|
+
ChannelMessagesNotLoadedError,
|
|
65
68
|
FlowAlreadyStartedError,
|
|
66
69
|
FlowDefinitionError,
|
|
67
70
|
FlowErrorType,
|
|
@@ -71,7 +74,6 @@ from dex.runtime_errors import (
|
|
|
71
74
|
LongPollTimeoutError,
|
|
72
75
|
ChannelMessageNotFoundError,
|
|
73
76
|
RpcLockConflictError,
|
|
74
|
-
StateNotLoadedError,
|
|
75
77
|
ValueMappingError,
|
|
76
78
|
WorkerInvocationError,
|
|
77
79
|
)
|
|
@@ -119,6 +121,7 @@ __all__ = [
|
|
|
119
121
|
"AttributeLock",
|
|
120
122
|
"AttributeMap",
|
|
121
123
|
"AttributeMapLoad",
|
|
124
|
+
"AttributeMapNotLoadedError",
|
|
122
125
|
"AsyncContext",
|
|
123
126
|
"AsyncClient",
|
|
124
127
|
"AsyncBufferedTextStream",
|
|
@@ -131,6 +134,7 @@ __all__ = [
|
|
|
131
134
|
"ChannelMapLoad",
|
|
132
135
|
"ChannelMessage",
|
|
133
136
|
"ChannelMessageNotFoundError",
|
|
137
|
+
"ChannelMessagesNotLoadedError",
|
|
134
138
|
"Client",
|
|
135
139
|
"ClientOptions",
|
|
136
140
|
"Codec",
|
|
@@ -149,6 +153,7 @@ __all__ = [
|
|
|
149
153
|
"FlowNotActiveError",
|
|
150
154
|
"FlowNotFoundError",
|
|
151
155
|
"FlowStatus",
|
|
156
|
+
"FlowTimeoutHandlerOptions",
|
|
152
157
|
"FlowTimeoutPolicy",
|
|
153
158
|
"HealthInfo",
|
|
154
159
|
"IdReusePolicy",
|
|
@@ -160,7 +165,6 @@ __all__ = [
|
|
|
160
165
|
"RPCResult",
|
|
161
166
|
"RetryAfterError",
|
|
162
167
|
"RpcLockConflictError",
|
|
163
|
-
"StateNotLoadedError",
|
|
164
168
|
"Registry",
|
|
165
169
|
"TimeTravelOptions",
|
|
166
170
|
"TimeTravelStepMethod",
|
|
@@ -80,12 +80,25 @@ class AsyncValueHydrator:
|
|
|
80
80
|
if has_heartbeat:
|
|
81
81
|
values.append(request.context.last_heartbeat_value)
|
|
82
82
|
values.extend(entry.value for entry in request.attributes)
|
|
83
|
+
channel_messages = [
|
|
84
|
+
message
|
|
85
|
+
for channel_values in request.loaded_channel_messages.values()
|
|
86
|
+
for message in channel_values.messages
|
|
87
|
+
]
|
|
88
|
+
values.extend(message.value for message in channel_messages)
|
|
83
89
|
hydrated = iter(await self.hydrate_all(values))
|
|
84
90
|
result.step_input.CopyFrom(next(hydrated))
|
|
85
91
|
if has_heartbeat:
|
|
86
92
|
result.context.last_heartbeat_value.CopyFrom(next(hydrated))
|
|
87
|
-
for entry
|
|
88
|
-
entry.value.CopyFrom(
|
|
93
|
+
for entry in result.attributes:
|
|
94
|
+
entry.value.CopyFrom(next(hydrated))
|
|
95
|
+
result_messages = [
|
|
96
|
+
message
|
|
97
|
+
for channel_values in result.loaded_channel_messages.values()
|
|
98
|
+
for message in channel_values.messages
|
|
99
|
+
]
|
|
100
|
+
for message in result_messages:
|
|
101
|
+
message.value.CopyFrom(next(hydrated))
|
|
89
102
|
return result
|
|
90
103
|
|
|
91
104
|
async def execute_request(
|
|
@@ -101,6 +114,12 @@ class AsyncValueHydrator:
|
|
|
101
114
|
values.append(request.context.last_heartbeat_value)
|
|
102
115
|
values.extend(entry.value for entry in request.attributes)
|
|
103
116
|
values.extend(entry.value for entry in request.step_exe_locals)
|
|
117
|
+
channel_messages = [
|
|
118
|
+
message
|
|
119
|
+
for channel_values in request.loaded_channel_messages.values()
|
|
120
|
+
for message in channel_values.messages
|
|
121
|
+
]
|
|
122
|
+
values.extend(message.value for message in channel_messages)
|
|
104
123
|
for channel_result in request.condition_results.channel_results:
|
|
105
124
|
values.extend(channel_result.values)
|
|
106
125
|
hydrated = iter(await self.hydrate_all(values))
|
|
@@ -112,6 +131,13 @@ class AsyncValueHydrator:
|
|
|
112
131
|
entry.value.CopyFrom(next(hydrated))
|
|
113
132
|
for entry in result.step_exe_locals:
|
|
114
133
|
entry.value.CopyFrom(next(hydrated))
|
|
134
|
+
result_messages = [
|
|
135
|
+
message
|
|
136
|
+
for channel_values in result.loaded_channel_messages.values()
|
|
137
|
+
for message in channel_values.messages
|
|
138
|
+
]
|
|
139
|
+
for message in result_messages:
|
|
140
|
+
message.value.CopyFrom(next(hydrated))
|
|
115
141
|
for channel_result in result.condition_results.channel_results:
|
|
116
142
|
for value in channel_result.values:
|
|
117
143
|
value.CopyFrom(next(hydrated))
|
|
@@ -100,6 +100,11 @@ class AsyncWorkerDispatcher(WorkerDispatcher):
|
|
|
100
100
|
request.context,
|
|
101
101
|
self._values,
|
|
102
102
|
request.attributes,
|
|
103
|
+
channel_infos=dict(request.channel_infos),
|
|
104
|
+
loaded_channel_messages=dict(request.loaded_channel_messages),
|
|
105
|
+
loaded_attribute_map_instances=request.loaded_attribute_map_instances,
|
|
106
|
+
loaded_channel_names=request.loaded_channel_names,
|
|
107
|
+
loaded_channel_map_instances=request.loaded_channel_map_instances,
|
|
103
108
|
is_active=is_active,
|
|
104
109
|
output_emitter=emitter,
|
|
105
110
|
)
|
|
@@ -126,6 +131,7 @@ class AsyncWorkerDispatcher(WorkerDispatcher):
|
|
|
126
131
|
upsert_step_exe_locals=list(context.local_writes.values()),
|
|
127
132
|
record_events=context.events,
|
|
128
133
|
publish_to_channel=context.publications,
|
|
134
|
+
delete_from_channel=context.channel_deletions,
|
|
129
135
|
)
|
|
130
136
|
waiting = self._map_wait(flow, wait)
|
|
131
137
|
if waiting is not None:
|
|
@@ -153,8 +159,16 @@ class AsyncWorkerDispatcher(WorkerDispatcher):
|
|
|
153
159
|
is_active: Callable[[], bool] | None = None,
|
|
154
160
|
) -> AsyncGenerator[pb.InvokeExecuteMethodOutput, None]:
|
|
155
161
|
if original.step_type == _TIMEOUT_HANDLER_STEP_TYPE:
|
|
156
|
-
|
|
157
|
-
|
|
162
|
+
emitter = _AsyncStepOutputEmitter()
|
|
163
|
+
handler = asyncio.create_task(
|
|
164
|
+
self._invoke_timeout_handler_async(original, emitter, is_active)
|
|
165
|
+
)
|
|
166
|
+
try:
|
|
167
|
+
async for output in self._drain_outputs(emitter, handler):
|
|
168
|
+
yield self._map_execute_output(output)
|
|
169
|
+
yield pb.InvokeExecuteMethodOutput(result=await handler)
|
|
170
|
+
finally:
|
|
171
|
+
await self._close_invocation(emitter, handler)
|
|
158
172
|
return
|
|
159
173
|
emitter = _AsyncStepOutputEmitter()
|
|
160
174
|
handler = asyncio.create_task(
|
|
@@ -187,6 +201,11 @@ class AsyncWorkerDispatcher(WorkerDispatcher):
|
|
|
187
201
|
request.attributes,
|
|
188
202
|
request.step_exe_locals,
|
|
189
203
|
condition_results,
|
|
204
|
+
channel_infos=dict(request.channel_infos),
|
|
205
|
+
loaded_channel_messages=dict(request.loaded_channel_messages),
|
|
206
|
+
loaded_attribute_map_instances=request.loaded_attribute_map_instances,
|
|
207
|
+
loaded_channel_names=request.loaded_channel_names,
|
|
208
|
+
loaded_channel_map_instances=request.loaded_channel_map_instances,
|
|
190
209
|
is_active=is_active,
|
|
191
210
|
output_emitter=emitter,
|
|
192
211
|
)
|
|
@@ -214,6 +233,7 @@ class AsyncWorkerDispatcher(WorkerDispatcher):
|
|
|
214
233
|
record_events=context.events,
|
|
215
234
|
upsert_step_exe_locals=list(context.local_writes.values()),
|
|
216
235
|
publish_to_channel=context.publications,
|
|
236
|
+
delete_from_channel=context.channel_deletions,
|
|
217
237
|
)
|
|
218
238
|
except InvalidStepResultError as error:
|
|
219
239
|
failure = error
|
|
@@ -235,6 +255,8 @@ class AsyncWorkerDispatcher(WorkerDispatcher):
|
|
|
235
255
|
async def _invoke_timeout_handler_async(
|
|
236
256
|
self,
|
|
237
257
|
original: pb.InvokeExecuteMethodRequest,
|
|
258
|
+
emitter: _AsyncStepOutputEmitter,
|
|
259
|
+
is_active: Callable[[], bool] | None,
|
|
238
260
|
) -> pb.InvokeExecuteMethodResponse:
|
|
239
261
|
request = await self._async_hydrator.execute_request(original)
|
|
240
262
|
flow = self._registry._flow_by_type(request.flow_type)
|
|
@@ -260,24 +282,55 @@ class AsyncWorkerDispatcher(WorkerDispatcher):
|
|
|
260
282
|
request.attributes,
|
|
261
283
|
request.step_exe_locals,
|
|
262
284
|
condition_results,
|
|
285
|
+
channel_infos=dict(request.channel_infos),
|
|
286
|
+
loaded_channel_messages=dict(request.loaded_channel_messages),
|
|
287
|
+
loaded_attribute_map_instances=request.loaded_attribute_map_instances,
|
|
288
|
+
loaded_channel_names=request.loaded_channel_names,
|
|
289
|
+
loaded_channel_map_instances=request.loaded_channel_map_instances,
|
|
290
|
+
is_active=is_active,
|
|
291
|
+
output_emitter=emitter,
|
|
263
292
|
)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
293
|
+
response: pb.InvokeExecuteMethodResponse | None = None
|
|
294
|
+
failure: BaseException | None = None
|
|
295
|
+
cause: BaseException | None = None
|
|
267
296
|
try:
|
|
297
|
+
decision: Any = flow.flow.handle_timeout(context)
|
|
298
|
+
if isgenerator(decision):
|
|
299
|
+
decision.close()
|
|
300
|
+
raise InvalidStepResultError(
|
|
301
|
+
flow.name,
|
|
302
|
+
_TIMEOUT_HANDLER_STEP_TYPE,
|
|
303
|
+
"execute",
|
|
304
|
+
"synchronous generators require Worker",
|
|
305
|
+
)
|
|
306
|
+
if isawaitable(decision):
|
|
307
|
+
decision = await decision
|
|
268
308
|
if not isinstance(decision, StepDecision):
|
|
269
309
|
raise TypeError("handle_timeout must return StepDecision")
|
|
270
|
-
|
|
310
|
+
response = pb.InvokeExecuteMethodResponse(
|
|
271
311
|
step_decision=self._map_decision(flow, decision),
|
|
272
312
|
upsert_attributes=list(context.attribute_writes.values()),
|
|
273
313
|
record_events=context.events,
|
|
274
314
|
upsert_step_exe_locals=list(context.local_writes.values()),
|
|
275
315
|
publish_to_channel=context.publications,
|
|
316
|
+
delete_from_channel=context.channel_deletions,
|
|
276
317
|
)
|
|
318
|
+
except InvalidStepResultError as error:
|
|
319
|
+
failure = error
|
|
277
320
|
except (TypeError, ValueError) as error:
|
|
278
|
-
|
|
321
|
+
failure = InvalidStepResultError(
|
|
279
322
|
flow.name, _TIMEOUT_HANDLER_STEP_TYPE, "execute", str(error)
|
|
280
|
-
)
|
|
323
|
+
)
|
|
324
|
+
cause = error
|
|
325
|
+
except BaseException as error:
|
|
326
|
+
failure = error
|
|
327
|
+
combined = context._finalize_step_outputs(failure)
|
|
328
|
+
if combined is not None:
|
|
329
|
+
if cause is not None:
|
|
330
|
+
raise combined from cause
|
|
331
|
+
raise combined
|
|
332
|
+
assert response is not None
|
|
333
|
+
return response
|
|
281
334
|
|
|
282
335
|
async def invoke_rpc( # type: ignore[override]
|
|
283
336
|
self,
|
|
@@ -20,7 +20,10 @@ from dex.codec import Codec
|
|
|
20
20
|
from dex.dexpb import dex_pb2 as pb
|
|
21
21
|
from dex.flow import Registry, _RegisteredFlow
|
|
22
22
|
from dex.flow_result import FlowResult, flow_result_from_proto
|
|
23
|
-
from dex.runtime_errors import
|
|
23
|
+
from dex.runtime_errors import (
|
|
24
|
+
AttributeMapNotLoadedError,
|
|
25
|
+
ChannelMessagesNotLoadedError,
|
|
26
|
+
)
|
|
24
27
|
from dex.step import (
|
|
25
28
|
_NO_HEARTBEAT_VALUE,
|
|
26
29
|
StepOutput,
|
|
@@ -136,7 +139,12 @@ class InvocationContext:
|
|
|
136
139
|
|
|
137
140
|
async def heartbeat(self, value: object = _NO_HEARTBEAT_VALUE) -> None:
|
|
138
141
|
if (
|
|
139
|
-
self._method
|
|
142
|
+
self._method
|
|
143
|
+
not in (
|
|
144
|
+
InvocationMethod.WAIT_FOR,
|
|
145
|
+
InvocationMethod.EXECUTE,
|
|
146
|
+
InvocationMethod.TIMEOUT,
|
|
147
|
+
)
|
|
140
148
|
or self._output_emitter is None
|
|
141
149
|
):
|
|
142
150
|
raise ValueError("heartbeat requires an asynchronous Step Context")
|
|
@@ -223,7 +231,11 @@ class InvocationContext:
|
|
|
223
231
|
definition: Stream[ValueT],
|
|
224
232
|
value: ValueT,
|
|
225
233
|
) -> StepOutput | None:
|
|
226
|
-
if self._method not in (
|
|
234
|
+
if self._method not in (
|
|
235
|
+
InvocationMethod.WAIT_FOR,
|
|
236
|
+
InvocationMethod.EXECUTE,
|
|
237
|
+
InvocationMethod.TIMEOUT,
|
|
238
|
+
):
|
|
227
239
|
raise ValueError("Stream writes require a Step Context")
|
|
228
240
|
self._require_registered(definition)
|
|
229
241
|
output = _StreamStepOutput(
|
|
@@ -242,7 +254,11 @@ class InvocationContext:
|
|
|
242
254
|
return None
|
|
243
255
|
|
|
244
256
|
def _prepare_buffered_stream(self, definition: Stream[object]) -> bool:
|
|
245
|
-
if self._method not in (
|
|
257
|
+
if self._method not in (
|
|
258
|
+
InvocationMethod.WAIT_FOR,
|
|
259
|
+
InvocationMethod.EXECUTE,
|
|
260
|
+
InvocationMethod.TIMEOUT,
|
|
261
|
+
):
|
|
246
262
|
raise ValueError("Buffered Streams require a Step Context")
|
|
247
263
|
self._require_registered(definition)
|
|
248
264
|
return self._output_emitter is not None
|
|
@@ -354,11 +370,10 @@ class InvocationContext:
|
|
|
354
370
|
),
|
|
355
371
|
)
|
|
356
372
|
)
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
)
|
|
373
|
+
current = self._channel_infos.get(name)
|
|
374
|
+
self._channel_infos[name] = pb.ChannelInfo(
|
|
375
|
+
size=(current.size if current is not None else 0) + 1
|
|
376
|
+
)
|
|
362
377
|
|
|
363
378
|
def _delete_channel_message(
|
|
364
379
|
self,
|
|
@@ -366,8 +381,6 @@ class InvocationContext:
|
|
|
366
381
|
instance: str | None,
|
|
367
382
|
message_id: str,
|
|
368
383
|
) -> None:
|
|
369
|
-
if self._method is not InvocationMethod.RPC:
|
|
370
|
-
raise ValueError("Channel message deletion requires an RPC Context")
|
|
371
384
|
self._require_registered(definition)
|
|
372
385
|
name = self._physical_name(definition, instance)
|
|
373
386
|
self.channel_deletions.append(
|
|
@@ -407,8 +420,6 @@ class InvocationContext:
|
|
|
407
420
|
definition: ChannelMap[object],
|
|
408
421
|
) -> tuple[str, ...]:
|
|
409
422
|
self._require_registered(definition)
|
|
410
|
-
if self._method is not InvocationMethod.RPC:
|
|
411
|
-
raise ValueError("ChannelMap introspection requires an RPC invocation")
|
|
412
423
|
prefix = f"{definition.name}/"
|
|
413
424
|
return tuple(
|
|
414
425
|
sorted(
|
|
@@ -432,8 +443,6 @@ class InvocationContext:
|
|
|
432
443
|
definition: Channel[ValueT] | ChannelMap[ValueT],
|
|
433
444
|
instance: str | None,
|
|
434
445
|
) -> tuple[ChannelMessage[ValueT], ...]:
|
|
435
|
-
if self._method is not InvocationMethod.RPC:
|
|
436
|
-
raise ValueError("pending Channel messages require an RPC Context")
|
|
437
446
|
self._require_registered(definition)
|
|
438
447
|
if isinstance(definition, ChannelMap):
|
|
439
448
|
channel_name = self._physical_name(definition, instance)
|
|
@@ -445,8 +454,8 @@ class InvocationContext:
|
|
|
445
454
|
channel_name = self._physical_name(definition, instance)
|
|
446
455
|
is_loaded = definition.name in self._loaded_channel_names
|
|
447
456
|
if not is_loaded:
|
|
448
|
-
raise
|
|
449
|
-
f"Channel messages were not loaded for
|
|
457
|
+
raise ChannelMessagesNotLoadedError(
|
|
458
|
+
f"Channel messages were not loaded for this invocation: {definition.name}"
|
|
450
459
|
)
|
|
451
460
|
values = self._loaded_channel_messages.get(channel_name)
|
|
452
461
|
if values is None:
|
|
@@ -486,29 +495,24 @@ class InvocationContext:
|
|
|
486
495
|
definition: Attribute[Any] | AttributeMap[Any],
|
|
487
496
|
instance: str | None,
|
|
488
497
|
) -> None:
|
|
489
|
-
if
|
|
490
|
-
definition, AttributeMap
|
|
491
|
-
):
|
|
498
|
+
if not isinstance(definition, AttributeMap):
|
|
492
499
|
return
|
|
493
500
|
physical_name = self._physical_name(definition, instance)
|
|
494
501
|
if (
|
|
495
502
|
f"{definition.name}/" not in self._loaded_attribute_map_instances
|
|
496
503
|
and physical_name not in self._loaded_attribute_map_instances
|
|
497
504
|
):
|
|
498
|
-
raise
|
|
499
|
-
f"AttributeMap instance was not loaded for
|
|
505
|
+
raise AttributeMapNotLoadedError(
|
|
506
|
+
f"AttributeMap instance was not loaded for this invocation: {physical_name}"
|
|
500
507
|
)
|
|
501
508
|
|
|
502
509
|
def _require_attribute_map_all_loaded(
|
|
503
510
|
self,
|
|
504
511
|
definition: AttributeMap[object],
|
|
505
512
|
) -> None:
|
|
506
|
-
if
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
):
|
|
510
|
-
raise StateNotLoadedError(
|
|
511
|
-
f"all AttributeMap instances were not loaded for RPC: {definition.name}"
|
|
513
|
+
if f"{definition.name}/" not in self._loaded_attribute_map_instances:
|
|
514
|
+
raise AttributeMapNotLoadedError(
|
|
515
|
+
f"all AttributeMap instances were not loaded for this invocation: {definition.name}"
|
|
512
516
|
)
|
|
513
517
|
|
|
514
518
|
def _require_registered(self, definition: _Definition) -> None:
|
|
@@ -80,12 +80,25 @@ class ValueHydrator:
|
|
|
80
80
|
if has_heartbeat:
|
|
81
81
|
values.append(request.context.last_heartbeat_value)
|
|
82
82
|
values.extend(entry.value for entry in request.attributes)
|
|
83
|
+
channel_messages = [
|
|
84
|
+
message
|
|
85
|
+
for channel_values in request.loaded_channel_messages.values()
|
|
86
|
+
for message in channel_values.messages
|
|
87
|
+
]
|
|
88
|
+
values.extend(message.value for message in channel_messages)
|
|
83
89
|
hydrated = iter(self.hydrate_all(values))
|
|
84
90
|
result.step_input.CopyFrom(next(hydrated))
|
|
85
91
|
if has_heartbeat:
|
|
86
92
|
result.context.last_heartbeat_value.CopyFrom(next(hydrated))
|
|
87
|
-
for entry
|
|
88
|
-
entry.value.CopyFrom(
|
|
93
|
+
for entry in result.attributes:
|
|
94
|
+
entry.value.CopyFrom(next(hydrated))
|
|
95
|
+
result_messages = [
|
|
96
|
+
message
|
|
97
|
+
for channel_values in result.loaded_channel_messages.values()
|
|
98
|
+
for message in channel_values.messages
|
|
99
|
+
]
|
|
100
|
+
for message in result_messages:
|
|
101
|
+
message.value.CopyFrom(next(hydrated))
|
|
89
102
|
return result
|
|
90
103
|
|
|
91
104
|
def execute_request(
|
|
@@ -101,6 +114,12 @@ class ValueHydrator:
|
|
|
101
114
|
values.append(request.context.last_heartbeat_value)
|
|
102
115
|
values.extend(entry.value for entry in request.attributes)
|
|
103
116
|
values.extend(entry.value for entry in request.step_exe_locals)
|
|
117
|
+
channel_messages = [
|
|
118
|
+
message
|
|
119
|
+
for channel_values in request.loaded_channel_messages.values()
|
|
120
|
+
for message in channel_values.messages
|
|
121
|
+
]
|
|
122
|
+
values.extend(message.value for message in channel_messages)
|
|
104
123
|
for channel_result in request.condition_results.channel_results:
|
|
105
124
|
values.extend(channel_result.values)
|
|
106
125
|
for flow_result in request.condition_results.sub_flow_results:
|
|
@@ -116,6 +135,13 @@ class ValueHydrator:
|
|
|
116
135
|
entry.value.CopyFrom(next(hydrated))
|
|
117
136
|
for entry in result.step_exe_locals:
|
|
118
137
|
entry.value.CopyFrom(next(hydrated))
|
|
138
|
+
result_messages = [
|
|
139
|
+
message
|
|
140
|
+
for channel_values in result.loaded_channel_messages.values()
|
|
141
|
+
for message in channel_values.messages
|
|
142
|
+
]
|
|
143
|
+
for message in result_messages:
|
|
144
|
+
message.value.CopyFrom(next(hydrated))
|
|
119
145
|
for channel_result in result.condition_results.channel_results:
|
|
120
146
|
for value in channel_result.values:
|
|
121
147
|
value.CopyFrom(next(hydrated))
|