dex-python-sdk 0.12.0__tar.gz → 0.12.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.12.0 → dex_python_sdk-0.12.1}/PKG-INFO +11 -6
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/README.md +10 -5
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/__init__.py +2 -2
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_grpc_errors.py +4 -6
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/async_client.py +67 -28
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/client.py +67 -28
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/dexpb/dex_pb2.py +248 -248
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/dexpb/dex_pb2.pyi +14 -10
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/dexpb/dex_pb2_grpc.py +12 -12
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/runtime_errors.py +4 -4
- dex_python_sdk-0.12.1/dex/wait_options.py +112 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/pyproject.toml +1 -1
- dex_python_sdk-0.12.0/dex/wait_options.py +0 -84
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/LEGACY_NOTICES.md +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/LICENSE +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_async_value_hydrator.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_async_worker_dispatcher.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_async_worker_service.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_invocation_context.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_native.pyi +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_server_protocol.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_utils.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_value_hydrator.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_value_mapper.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_worker_dispatcher.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/_worker_service.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/async_worker.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/attribute.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/attribute_match.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/blob_cache.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/channel.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/client_options.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/codec.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/condition.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/context.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/dexpb/__init__.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/flow.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/flow_config.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/flow_info.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/flow_options.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/flow_result.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/py.typed +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/step.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/step_execution.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/stream.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/subflow.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/timer.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/wait.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/worker.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/dex/worker_options.py +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/Cargo.lock +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/Cargo.toml +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache/Cargo.toml +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache/LICENSE +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache/src/config.rs +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache/src/entry.rs +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache/src/error.rs +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache/src/format.rs +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache/src/lib.rs +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache/src/policy.rs +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache/src/store.rs +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache/tests/blob_cache_integration.rs +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache-python/Cargo.toml +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.1}/sdk-rust/crates/dex-blob-cache-python/LICENSE +0 -0
- {dex_python_sdk-0.12.0 → dex_python_sdk-0.12.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.12.
|
|
3
|
+
Version: 0.12.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
|
|
@@ -363,11 +363,16 @@ sorted. Use `force_complete_if_channels_empty(...)` for conditional completion.
|
|
|
363
363
|
|
|
364
364
|
Request IDs are optional for both durable waits. When omitted, the server
|
|
365
365
|
derives a stable ID from the Step execution or Attribute condition. Reuse an
|
|
366
|
-
override only for the same wait. `
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
366
|
+
override only for the same wait. `request_timeout` is the total SDK call budget
|
|
367
|
+
across transparent transport reattachments. Zero waits indefinitely.
|
|
368
|
+
Temporal permits 10 in-flight Updates and 2,000 total Updates in History per
|
|
369
|
+
Workflow Execution. A caller timeout or cancellation does not finish an
|
|
370
|
+
accepted durable wait, so an abandoned handler can retain one in-flight slot.
|
|
371
|
+
Set `internal_handler_timeout` only when abandoned waits can approach that
|
|
372
|
+
limit. An active call transparently starts another generation, which counts
|
|
373
|
+
toward the 2,000-Update history limit. Prefer a value comfortably longer than
|
|
374
|
+
normal request timeouts and reconnect gaps. Zero disables timed rollover.
|
|
375
|
+
Request expiry raises `RequestTimeoutError`.
|
|
371
376
|
|
|
372
377
|
`Client.wait_for_flow` and `AsyncClient.wait_for_flow` return a
|
|
373
378
|
`FlowResult` after hydrating every output-bearing completion. Use
|
|
@@ -347,11 +347,16 @@ sorted. Use `force_complete_if_channels_empty(...)` for conditional completion.
|
|
|
347
347
|
|
|
348
348
|
Request IDs are optional for both durable waits. When omitted, the server
|
|
349
349
|
derives a stable ID from the Step execution or Attribute condition. Reuse an
|
|
350
|
-
override only for the same wait. `
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
350
|
+
override only for the same wait. `request_timeout` is the total SDK call budget
|
|
351
|
+
across transparent transport reattachments. Zero waits indefinitely.
|
|
352
|
+
Temporal permits 10 in-flight Updates and 2,000 total Updates in History per
|
|
353
|
+
Workflow Execution. A caller timeout or cancellation does not finish an
|
|
354
|
+
accepted durable wait, so an abandoned handler can retain one in-flight slot.
|
|
355
|
+
Set `internal_handler_timeout` only when abandoned waits can approach that
|
|
356
|
+
limit. An active call transparently starts another generation, which counts
|
|
357
|
+
toward the 2,000-Update history limit. Prefer a value comfortably longer than
|
|
358
|
+
normal request timeouts and reconnect gaps. Zero disables timed rollover.
|
|
359
|
+
Request expiry raises `RequestTimeoutError`.
|
|
355
360
|
|
|
356
361
|
`Client.wait_for_flow` and `AsyncClient.wait_for_flow` return a
|
|
357
362
|
`FlowResult` after hydrating every output-bearing completion. Use
|
|
@@ -74,9 +74,9 @@ from dex.runtime_errors import (
|
|
|
74
74
|
FlowNotFoundError,
|
|
75
75
|
InvalidStepResultError,
|
|
76
76
|
LongPollTimeoutError,
|
|
77
|
+
RequestTimeoutError,
|
|
77
78
|
RpcLockConflictError,
|
|
78
79
|
ValueMappingError,
|
|
79
|
-
WaitHandlerTimeoutError,
|
|
80
80
|
WorkerInvocationError,
|
|
81
81
|
)
|
|
82
82
|
from dex.step import (
|
|
@@ -166,7 +166,7 @@ __all__ = [
|
|
|
166
166
|
"IndexType",
|
|
167
167
|
"JsonCodec",
|
|
168
168
|
"LongPollTimeoutError",
|
|
169
|
-
"
|
|
169
|
+
"RequestTimeoutError",
|
|
170
170
|
"PersistenceSchema",
|
|
171
171
|
"RPCResult",
|
|
172
172
|
"RecoveryErrorInfo",
|
|
@@ -26,8 +26,8 @@ from dex.runtime_errors import (
|
|
|
26
26
|
FlowNotActiveError,
|
|
27
27
|
FlowNotFoundError,
|
|
28
28
|
LongPollTimeoutError,
|
|
29
|
+
RequestTimeoutError,
|
|
29
30
|
RpcLockConflictError,
|
|
30
|
-
WaitHandlerTimeoutError,
|
|
31
31
|
WorkerInvocationError,
|
|
32
32
|
)
|
|
33
33
|
|
|
@@ -144,8 +144,8 @@ def translate_rpc_error(
|
|
|
144
144
|
return LongPollTimeoutError(*parameters)
|
|
145
145
|
if sub_status is ErrorSubStatus.CHANNEL_MESSAGE_NOT_FOUND:
|
|
146
146
|
return ChannelMessageNotFoundError(*parameters)
|
|
147
|
-
if sub_status is ErrorSubStatus.
|
|
148
|
-
return
|
|
147
|
+
if sub_status is ErrorSubStatus.REQUEST_TIMEOUT:
|
|
148
|
+
return RequestTimeoutError(*parameters)
|
|
149
149
|
return DexServiceError(*parameters)
|
|
150
150
|
|
|
151
151
|
|
|
@@ -249,8 +249,6 @@ def _map_sub_status(value: int) -> ErrorSubStatus:
|
|
|
249
249
|
int(
|
|
250
250
|
pb.ERROR_SUB_STATUS_CHANNEL_MESSAGE_NOT_FOUND
|
|
251
251
|
): ErrorSubStatus.CHANNEL_MESSAGE_NOT_FOUND,
|
|
252
|
-
int(
|
|
253
|
-
pb.ERROR_SUB_STATUS_WAIT_HANDLER_TIME_OUT
|
|
254
|
-
): ErrorSubStatus.WAIT_HANDLER_TIMEOUT,
|
|
252
|
+
int(pb.ERROR_SUB_STATUS_REQUEST_TIMEOUT): ErrorSubStatus.REQUEST_TIMEOUT,
|
|
255
253
|
}
|
|
256
254
|
return statuses.get(value, ErrorSubStatus.UNCATEGORIZED)
|
|
@@ -45,14 +45,15 @@ from dex.flow_options import (
|
|
|
45
45
|
_resolve_flow_timeout_policy,
|
|
46
46
|
)
|
|
47
47
|
from dex.flow_result import FlowResult, flow_result_from_proto
|
|
48
|
-
from dex.runtime_errors import FlowErrorType, LongPollTimeoutError
|
|
48
|
+
from dex.runtime_errors import DexServiceError, FlowErrorType, LongPollTimeoutError
|
|
49
49
|
from dex.step import RetryPolicy, StepDurability
|
|
50
50
|
from dex.step_execution import StepExecutionId, TimerId
|
|
51
51
|
from dex.stream import Stream, StreamMessage, StreamMessagesPage
|
|
52
52
|
from dex.wait_options import (
|
|
53
53
|
WaitForAttributeOptions,
|
|
54
54
|
WaitForStepCompletionOptions,
|
|
55
|
-
|
|
55
|
+
_ClientRequestBudget,
|
|
56
|
+
_duration_seconds32,
|
|
56
57
|
)
|
|
57
58
|
|
|
58
59
|
InputT = TypeVar("InputT")
|
|
@@ -738,27 +739,33 @@ class AsyncClient:
|
|
|
738
739
|
step_execution_id: StepExecutionId,
|
|
739
740
|
options: WaitForStepCompletionOptions,
|
|
740
741
|
) -> None:
|
|
741
|
-
"""Await one Step execution
|
|
742
|
+
"""Await one Step execution completion or its request timeout expiry.
|
|
742
743
|
|
|
743
744
|
The server derives a stable Request ID from the Step execution when none
|
|
744
|
-
is supplied.
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
positive value bounds the caller-visible wait.
|
|
745
|
+
is supplied. Request timeout covers transparent transport reattachments.
|
|
746
|
+
Internal handler timeout controls Update generation rollover without ending
|
|
747
|
+
this request.
|
|
748
748
|
|
|
749
749
|
Args:
|
|
750
750
|
flow_id: The non-empty active Flow ID.
|
|
751
751
|
step_execution_id: The Step type and positive execution number.
|
|
752
|
-
options: The optional Request ID
|
|
752
|
+
options: The optional Request ID, request timeout, and internal handler
|
|
753
|
+
timeout controls.
|
|
753
754
|
|
|
754
755
|
Raises:
|
|
755
|
-
ValueError: If the Request ID or
|
|
756
|
-
|
|
756
|
+
ValueError: If the Request ID or request timeout is invalid.
|
|
757
|
+
RequestTimeoutError: If a positive request timeout expires first.
|
|
757
758
|
FlowNotActiveError: If the Flow closes first.
|
|
758
759
|
DexServiceError: If FlowService cannot perform the wait.
|
|
759
760
|
"""
|
|
760
|
-
|
|
761
|
+
request_budget = _ClientRequestBudget(options.request_timeout)
|
|
762
|
+
internal_handler_timeout_seconds = _duration_seconds32(
|
|
763
|
+
options.internal_handler_timeout
|
|
764
|
+
)
|
|
761
765
|
while True:
|
|
766
|
+
request_attempt = request_budget.next_attempt(
|
|
767
|
+
"wait_for_step_completion", flow_id
|
|
768
|
+
)
|
|
762
769
|
try:
|
|
763
770
|
await self._call(
|
|
764
771
|
self._service.WaitForStepCompletion,
|
|
@@ -766,18 +773,29 @@ class AsyncClient:
|
|
|
766
773
|
flow_id=require_name(flow_id),
|
|
767
774
|
step_type=step_execution_id.step_type,
|
|
768
775
|
step_execution_number=str(step_execution_id.number),
|
|
769
|
-
|
|
770
|
-
|
|
776
|
+
request_timeout_seconds=request_attempt.request_timeout_seconds,
|
|
777
|
+
internal_handler_timeout_seconds=(
|
|
778
|
+
internal_handler_timeout_seconds
|
|
771
779
|
),
|
|
772
780
|
request_id=options.request_id,
|
|
773
781
|
),
|
|
774
782
|
"wait_for_step_completion",
|
|
775
783
|
flow_id,
|
|
776
784
|
"active",
|
|
785
|
+
request_attempt.transport_timeout_seconds,
|
|
777
786
|
)
|
|
778
787
|
return
|
|
779
788
|
except LongPollTimeoutError:
|
|
780
789
|
continue
|
|
790
|
+
except DexServiceError as error:
|
|
791
|
+
if (
|
|
792
|
+
error.code is grpc.StatusCode.DEADLINE_EXCEEDED
|
|
793
|
+
and request_budget.has_expired()
|
|
794
|
+
):
|
|
795
|
+
raise request_budget.timeout_error(
|
|
796
|
+
"wait_for_step_completion", flow_id
|
|
797
|
+
) from error
|
|
798
|
+
raise
|
|
781
799
|
|
|
782
800
|
@overload
|
|
783
801
|
async def wait_for_attribute_match(
|
|
@@ -791,24 +809,23 @@ class AsyncClient:
|
|
|
791
809
|
|
|
792
810
|
The Client returns the value observed by the successful wait. The server
|
|
793
811
|
derives a stable Request ID from the condition when none is supplied.
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
Workflow history; prefer at least one minute when nonzero. A positive value
|
|
797
|
-
bounds the caller-visible wait.
|
|
812
|
+
Request timeout covers transparent transport reattachments. Internal handler
|
|
813
|
+
timeout controls Update generation rollover without ending this request.
|
|
798
814
|
JSON, bytes, and null operands raise ``ValueError`` before transport.
|
|
799
815
|
|
|
800
816
|
Args:
|
|
801
817
|
flow_id: The non-empty active Flow ID.
|
|
802
818
|
attribute: The registered singleton Attribute to observe.
|
|
803
819
|
match: The scalar predicate to await.
|
|
804
|
-
options: The optional Request ID
|
|
820
|
+
options: The optional Request ID, request timeout, and internal handler
|
|
821
|
+
timeout controls.
|
|
805
822
|
|
|
806
823
|
Returns:
|
|
807
824
|
The current Attribute value that satisfied ``match``.
|
|
808
825
|
|
|
809
826
|
Raises:
|
|
810
827
|
ValueError: If an identifier, option, or match operand is invalid.
|
|
811
|
-
|
|
828
|
+
RequestTimeoutError: If a positive request timeout expires first.
|
|
812
829
|
FlowNotActiveError: If the Flow closes first.
|
|
813
830
|
DexServiceError: If FlowService cannot perform the wait.
|
|
814
831
|
"""
|
|
@@ -825,7 +842,7 @@ class AsyncClient:
|
|
|
825
842
|
) -> ValueT:
|
|
826
843
|
"""Await one AttributeMap instance in the current run satisfying a match.
|
|
827
844
|
|
|
828
|
-
Match restrictions, Request ID, handler-
|
|
845
|
+
Match restrictions, Request ID, request-timeout and handler-generation behavior, and service
|
|
829
846
|
errors match :meth:`wait_for_attribute_match`.
|
|
830
847
|
|
|
831
848
|
Args:
|
|
@@ -833,14 +850,14 @@ class AsyncClient:
|
|
|
833
850
|
attribute: The registered AttributeMap to observe.
|
|
834
851
|
instance: The map instance to observe. Slash is prohibited because it is a reserved character.
|
|
835
852
|
match: The scalar predicate to await.
|
|
836
|
-
options: The optional Request ID override and total handler
|
|
853
|
+
options: The optional Request ID override and total handler request timeout.
|
|
837
854
|
|
|
838
855
|
Returns:
|
|
839
856
|
The current AttributeMap value that satisfied ``match``.
|
|
840
857
|
|
|
841
858
|
Raises:
|
|
842
859
|
ValueError: If an identifier, option, or match operand is invalid.
|
|
843
|
-
|
|
860
|
+
RequestTimeoutError: If a positive request timeout expires first.
|
|
844
861
|
FlowNotActiveError: If the Flow closes first.
|
|
845
862
|
DexServiceError: If FlowService cannot perform the wait.
|
|
846
863
|
"""
|
|
@@ -870,7 +887,7 @@ class AsyncClient:
|
|
|
870
887
|
Raises:
|
|
871
888
|
TypeError: If arguments do not match the Attribute definition.
|
|
872
889
|
ValueError: If an identifier, option, or expected value is invalid.
|
|
873
|
-
|
|
890
|
+
RequestTimeoutError: If a positive request timeout expires first.
|
|
874
891
|
FlowNotActiveError: If the Flow closes first.
|
|
875
892
|
DexServiceError: If FlowService cannot perform the wait.
|
|
876
893
|
"""
|
|
@@ -895,8 +912,14 @@ class AsyncClient:
|
|
|
895
912
|
self._values.codec(attribute.value_type),
|
|
896
913
|
)
|
|
897
914
|
encoded_match.key = self._definition_name(attribute, instance)
|
|
898
|
-
|
|
915
|
+
request_budget = _ClientRequestBudget(options.request_timeout)
|
|
916
|
+
internal_handler_timeout_seconds = _duration_seconds32(
|
|
917
|
+
options.internal_handler_timeout
|
|
918
|
+
)
|
|
899
919
|
while True:
|
|
920
|
+
request_attempt = request_budget.next_attempt(
|
|
921
|
+
"wait_for_attribute_match", flow_id
|
|
922
|
+
)
|
|
900
923
|
try:
|
|
901
924
|
response = cast(
|
|
902
925
|
pb.WaitForAttributeResponse,
|
|
@@ -905,14 +928,18 @@ class AsyncClient:
|
|
|
905
928
|
pb.WaitForAttributeRequest(
|
|
906
929
|
flow_id=require_name(flow_id),
|
|
907
930
|
match=encoded_match,
|
|
908
|
-
|
|
909
|
-
|
|
931
|
+
request_timeout_seconds=(
|
|
932
|
+
request_attempt.request_timeout_seconds
|
|
933
|
+
),
|
|
934
|
+
internal_handler_timeout_seconds=(
|
|
935
|
+
internal_handler_timeout_seconds
|
|
910
936
|
),
|
|
911
937
|
request_id=options.request_id,
|
|
912
938
|
),
|
|
913
939
|
"wait_for_attribute_match",
|
|
914
940
|
flow_id,
|
|
915
941
|
"active",
|
|
942
|
+
request_attempt.transport_timeout_seconds,
|
|
916
943
|
),
|
|
917
944
|
)
|
|
918
945
|
if not response.HasField("matched_value"):
|
|
@@ -923,6 +950,15 @@ class AsyncClient:
|
|
|
923
950
|
)
|
|
924
951
|
except LongPollTimeoutError:
|
|
925
952
|
continue
|
|
953
|
+
except DexServiceError as error:
|
|
954
|
+
if (
|
|
955
|
+
error.code is grpc.StatusCode.DEADLINE_EXCEEDED
|
|
956
|
+
and request_budget.has_expired()
|
|
957
|
+
):
|
|
958
|
+
raise request_budget.timeout_error(
|
|
959
|
+
"wait_for_attribute_match", flow_id
|
|
960
|
+
) from error
|
|
961
|
+
raise
|
|
926
962
|
|
|
927
963
|
async def update_flow_config(self, flow_id: str, config: FlowConfig) -> None:
|
|
928
964
|
"""Replace mutable configuration for an active Flow.
|
|
@@ -1245,13 +1281,16 @@ class AsyncClient:
|
|
|
1245
1281
|
|
|
1246
1282
|
@staticmethod
|
|
1247
1283
|
async def _call(
|
|
1248
|
-
method: Callable[
|
|
1284
|
+
method: Callable[..., Any],
|
|
1249
1285
|
request: Any,
|
|
1250
1286
|
operation: str,
|
|
1251
1287
|
flow_id: str | None,
|
|
1252
1288
|
requirement: FlowTargetRequirement,
|
|
1289
|
+
timeout: float | None = None,
|
|
1253
1290
|
) -> Any:
|
|
1254
1291
|
try:
|
|
1255
|
-
|
|
1292
|
+
if timeout is None:
|
|
1293
|
+
return await method(request)
|
|
1294
|
+
return await method(request, timeout=timeout)
|
|
1256
1295
|
except grpc.RpcError as error:
|
|
1257
1296
|
raise translate_rpc_error(error, operation, flow_id, requirement) from error
|
|
@@ -45,14 +45,15 @@ from dex.flow_options import (
|
|
|
45
45
|
_resolve_flow_timeout_policy,
|
|
46
46
|
)
|
|
47
47
|
from dex.flow_result import FlowResult, flow_result_from_proto
|
|
48
|
-
from dex.runtime_errors import FlowErrorType, LongPollTimeoutError
|
|
48
|
+
from dex.runtime_errors import DexServiceError, FlowErrorType, LongPollTimeoutError
|
|
49
49
|
from dex.step import RetryPolicy, StepDurability
|
|
50
50
|
from dex.step_execution import StepExecutionId, TimerId
|
|
51
51
|
from dex.stream import Stream, StreamMessage, StreamMessagesPage
|
|
52
52
|
from dex.wait_options import (
|
|
53
53
|
WaitForAttributeOptions,
|
|
54
54
|
WaitForStepCompletionOptions,
|
|
55
|
-
|
|
55
|
+
_ClientRequestBudget,
|
|
56
|
+
_duration_seconds32,
|
|
56
57
|
)
|
|
57
58
|
|
|
58
59
|
InputT = TypeVar("InputT")
|
|
@@ -734,27 +735,33 @@ class Client:
|
|
|
734
735
|
step_execution_id: StepExecutionId,
|
|
735
736
|
options: WaitForStepCompletionOptions,
|
|
736
737
|
) -> None:
|
|
737
|
-
"""Block until one Step execution completes or its
|
|
738
|
+
"""Block until one Step execution completes or its request timeout expires.
|
|
738
739
|
|
|
739
740
|
The server derives a stable Request ID from the Step execution when none
|
|
740
|
-
is supplied.
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
positive value bounds the caller-visible wait.
|
|
741
|
+
is supplied. Request timeout covers transparent transport reattachments.
|
|
742
|
+
Internal handler timeout controls Update generation rollover without ending
|
|
743
|
+
this request.
|
|
744
744
|
|
|
745
745
|
Args:
|
|
746
746
|
flow_id: The non-empty active Flow ID.
|
|
747
747
|
step_execution_id: The Step type and positive execution number.
|
|
748
|
-
options: The optional Request ID
|
|
748
|
+
options: The optional Request ID, request timeout, and internal handler
|
|
749
|
+
timeout controls.
|
|
749
750
|
|
|
750
751
|
Raises:
|
|
751
|
-
ValueError: If the Request ID or
|
|
752
|
-
|
|
752
|
+
ValueError: If the Request ID or request timeout is invalid.
|
|
753
|
+
RequestTimeoutError: If a positive request timeout expires first.
|
|
753
754
|
FlowNotActiveError: If the Flow closes first.
|
|
754
755
|
DexServiceError: If FlowService cannot perform the wait.
|
|
755
756
|
"""
|
|
756
|
-
|
|
757
|
+
request_budget = _ClientRequestBudget(options.request_timeout)
|
|
758
|
+
internal_handler_timeout_seconds = _duration_seconds32(
|
|
759
|
+
options.internal_handler_timeout
|
|
760
|
+
)
|
|
757
761
|
while True:
|
|
762
|
+
request_attempt = request_budget.next_attempt(
|
|
763
|
+
"wait_for_step_completion", flow_id
|
|
764
|
+
)
|
|
758
765
|
try:
|
|
759
766
|
self._call(
|
|
760
767
|
self._service.WaitForStepCompletion,
|
|
@@ -762,18 +769,29 @@ class Client:
|
|
|
762
769
|
flow_id=require_name(flow_id),
|
|
763
770
|
step_type=step_execution_id.step_type,
|
|
764
771
|
step_execution_number=str(step_execution_id.number),
|
|
765
|
-
|
|
766
|
-
|
|
772
|
+
request_timeout_seconds=request_attempt.request_timeout_seconds,
|
|
773
|
+
internal_handler_timeout_seconds=(
|
|
774
|
+
internal_handler_timeout_seconds
|
|
767
775
|
),
|
|
768
776
|
request_id=options.request_id,
|
|
769
777
|
),
|
|
770
778
|
"wait_for_step_completion",
|
|
771
779
|
flow_id,
|
|
772
780
|
"active",
|
|
781
|
+
request_attempt.transport_timeout_seconds,
|
|
773
782
|
)
|
|
774
783
|
return
|
|
775
784
|
except LongPollTimeoutError:
|
|
776
785
|
continue
|
|
786
|
+
except DexServiceError as error:
|
|
787
|
+
if (
|
|
788
|
+
error.code is grpc.StatusCode.DEADLINE_EXCEEDED
|
|
789
|
+
and request_budget.has_expired()
|
|
790
|
+
):
|
|
791
|
+
raise request_budget.timeout_error(
|
|
792
|
+
"wait_for_step_completion", flow_id
|
|
793
|
+
) from error
|
|
794
|
+
raise
|
|
777
795
|
|
|
778
796
|
@overload
|
|
779
797
|
def wait_for_attribute_match(
|
|
@@ -787,24 +805,23 @@ class Client:
|
|
|
787
805
|
|
|
788
806
|
The Client returns the value observed by the successful wait. The server
|
|
789
807
|
derives a stable Request ID from the condition when none is supplied.
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
Workflow history; prefer at least one minute when nonzero. A positive value
|
|
793
|
-
bounds the caller-visible wait.
|
|
808
|
+
Request timeout covers transparent transport reattachments. Internal handler
|
|
809
|
+
timeout controls Update generation rollover without ending this request.
|
|
794
810
|
JSON, bytes, and null operands raise ``ValueError`` before transport.
|
|
795
811
|
|
|
796
812
|
Args:
|
|
797
813
|
flow_id: The non-empty active Flow ID.
|
|
798
814
|
attribute: The registered singleton Attribute to observe.
|
|
799
815
|
match: The scalar predicate to await.
|
|
800
|
-
options: The optional Request ID
|
|
816
|
+
options: The optional Request ID, request timeout, and internal handler
|
|
817
|
+
timeout controls.
|
|
801
818
|
|
|
802
819
|
Returns:
|
|
803
820
|
The current Attribute value that satisfied ``match``.
|
|
804
821
|
|
|
805
822
|
Raises:
|
|
806
823
|
ValueError: If an identifier, option, or match operand is invalid.
|
|
807
|
-
|
|
824
|
+
RequestTimeoutError: If a positive request timeout expires first.
|
|
808
825
|
FlowNotActiveError: If the Flow closes first.
|
|
809
826
|
DexServiceError: If FlowService cannot perform the wait.
|
|
810
827
|
"""
|
|
@@ -822,7 +839,7 @@ class Client:
|
|
|
822
839
|
"""Wait for one AttributeMap instance in the current run to match.
|
|
823
840
|
|
|
824
841
|
``instance`` is encoded as a map key. Match restrictions, Request ID,
|
|
825
|
-
handler-
|
|
842
|
+
request-timeout and handler-generation behavior, and service errors match
|
|
826
843
|
:meth:`wait_for_attribute_match`.
|
|
827
844
|
|
|
828
845
|
Args:
|
|
@@ -830,14 +847,14 @@ class Client:
|
|
|
830
847
|
attribute: The registered AttributeMap to observe.
|
|
831
848
|
instance: The map instance to observe. Slash is prohibited because it is a reserved character.
|
|
832
849
|
match: The scalar predicate to await.
|
|
833
|
-
options: The optional Request ID override and total handler
|
|
850
|
+
options: The optional Request ID override and total handler request timeout.
|
|
834
851
|
|
|
835
852
|
Returns:
|
|
836
853
|
The current AttributeMap value that satisfied ``match``.
|
|
837
854
|
|
|
838
855
|
Raises:
|
|
839
856
|
ValueError: If an identifier, option, or match operand is invalid.
|
|
840
|
-
|
|
857
|
+
RequestTimeoutError: If a positive request timeout expires first.
|
|
841
858
|
FlowNotActiveError: If the Flow closes first.
|
|
842
859
|
DexServiceError: If FlowService cannot perform the wait.
|
|
843
860
|
"""
|
|
@@ -867,7 +884,7 @@ class Client:
|
|
|
867
884
|
Raises:
|
|
868
885
|
TypeError: If arguments do not match the Attribute definition.
|
|
869
886
|
ValueError: If an identifier, option, or expected value is invalid.
|
|
870
|
-
|
|
887
|
+
RequestTimeoutError: If a positive request timeout expires first.
|
|
871
888
|
FlowNotActiveError: If the Flow closes first.
|
|
872
889
|
DexServiceError: If FlowService cannot perform the wait.
|
|
873
890
|
"""
|
|
@@ -892,8 +909,14 @@ class Client:
|
|
|
892
909
|
self._values.codec(attribute.value_type),
|
|
893
910
|
)
|
|
894
911
|
encoded_match.key = self._definition_name(attribute, instance)
|
|
895
|
-
|
|
912
|
+
request_budget = _ClientRequestBudget(options.request_timeout)
|
|
913
|
+
internal_handler_timeout_seconds = _duration_seconds32(
|
|
914
|
+
options.internal_handler_timeout
|
|
915
|
+
)
|
|
896
916
|
while True:
|
|
917
|
+
request_attempt = request_budget.next_attempt(
|
|
918
|
+
"wait_for_attribute_match", flow_id
|
|
919
|
+
)
|
|
897
920
|
try:
|
|
898
921
|
response = cast(
|
|
899
922
|
pb.WaitForAttributeResponse,
|
|
@@ -902,14 +925,18 @@ class Client:
|
|
|
902
925
|
pb.WaitForAttributeRequest(
|
|
903
926
|
flow_id=require_name(flow_id),
|
|
904
927
|
match=encoded_match,
|
|
905
|
-
|
|
906
|
-
|
|
928
|
+
request_timeout_seconds=(
|
|
929
|
+
request_attempt.request_timeout_seconds
|
|
930
|
+
),
|
|
931
|
+
internal_handler_timeout_seconds=(
|
|
932
|
+
internal_handler_timeout_seconds
|
|
907
933
|
),
|
|
908
934
|
request_id=options.request_id,
|
|
909
935
|
),
|
|
910
936
|
"wait_for_attribute_match",
|
|
911
937
|
flow_id,
|
|
912
938
|
"active",
|
|
939
|
+
request_attempt.transport_timeout_seconds,
|
|
913
940
|
),
|
|
914
941
|
)
|
|
915
942
|
if not response.HasField("matched_value"):
|
|
@@ -920,6 +947,15 @@ class Client:
|
|
|
920
947
|
)
|
|
921
948
|
except LongPollTimeoutError:
|
|
922
949
|
continue
|
|
950
|
+
except DexServiceError as error:
|
|
951
|
+
if (
|
|
952
|
+
error.code is grpc.StatusCode.DEADLINE_EXCEEDED
|
|
953
|
+
and request_budget.has_expired()
|
|
954
|
+
):
|
|
955
|
+
raise request_budget.timeout_error(
|
|
956
|
+
"wait_for_attribute_match", flow_id
|
|
957
|
+
) from error
|
|
958
|
+
raise
|
|
923
959
|
|
|
924
960
|
def update_flow_config(self, flow_id: str, config: FlowConfig) -> None:
|
|
925
961
|
"""Replace mutable configuration for an active Flow.
|
|
@@ -1242,13 +1278,16 @@ class Client:
|
|
|
1242
1278
|
|
|
1243
1279
|
@staticmethod
|
|
1244
1280
|
def _call(
|
|
1245
|
-
method: Callable[
|
|
1281
|
+
method: Callable[..., Any],
|
|
1246
1282
|
request: Any,
|
|
1247
1283
|
operation: str,
|
|
1248
1284
|
flow_id: str | None,
|
|
1249
1285
|
requirement: FlowTargetRequirement,
|
|
1286
|
+
timeout: float | None = None,
|
|
1250
1287
|
) -> Any:
|
|
1251
1288
|
try:
|
|
1252
|
-
|
|
1289
|
+
if timeout is None:
|
|
1290
|
+
return method(request)
|
|
1291
|
+
return method(request, timeout=timeout)
|
|
1253
1292
|
except grpc.RpcError as error:
|
|
1254
1293
|
raise translate_rpc_error(error, operation, flow_id, requirement) from error
|