aws-durable-execution-sdk-python 2.1.0__py3-none-any.whl → 3.0.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.
- aws_durable_execution_sdk_python/__about__.py +1 -1
- aws_durable_execution_sdk_python/concurrency/executor.py +2 -4
- aws_durable_execution_sdk_python/concurrency/models.py +31 -21
- aws_durable_execution_sdk_python/config.py +109 -7
- aws_durable_execution_sdk_python/context.py +63 -22
- aws_durable_execution_sdk_python/execution.py +30 -20
- aws_durable_execution_sdk_python/identifier.py +9 -8
- aws_durable_execution_sdk_python/lambda_service.py +55 -36
- aws_durable_execution_sdk_python/operation/child.py +0 -5
- aws_durable_execution_sdk_python/operation/map.py +4 -4
- aws_durable_execution_sdk_python/operation/parallel.py +4 -4
- aws_durable_execution_sdk_python/plugin.py +1190 -152
- aws_durable_execution_sdk_python/plugin_discovery.py +296 -132
- aws_durable_execution_sdk_python/state.py +101 -7
- {aws_durable_execution_sdk_python-2.1.0.dist-info → aws_durable_execution_sdk_python-3.0.0.dist-info}/METADATA +91 -41
- {aws_durable_execution_sdk_python-2.1.0.dist-info → aws_durable_execution_sdk_python-3.0.0.dist-info}/RECORD +17 -17
- {aws_durable_execution_sdk_python-2.1.0.dist-info → aws_durable_execution_sdk_python-3.0.0.dist-info}/WHEEL +0 -0
|
@@ -6,7 +6,7 @@ import datetime
|
|
|
6
6
|
import logging
|
|
7
7
|
from collections.abc import MutableMapping
|
|
8
8
|
from dataclasses import dataclass, field
|
|
9
|
-
from enum import Enum
|
|
9
|
+
from enum import Enum, StrEnum
|
|
10
10
|
from typing import TYPE_CHECKING, Any, NoReturn, Protocol, TypeAlias, cast
|
|
11
11
|
|
|
12
12
|
import boto3
|
|
@@ -38,13 +38,25 @@ TimeoutSeconds: TypeAlias = int
|
|
|
38
38
|
logger = logging.getLogger(__name__)
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
"""Return True if this SDK is installed under /var/lang/.
|
|
41
|
+
_BUNDLED_LOCAL_LABEL = "+bundled"
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
|
|
44
|
+
def _is_bundled(version: str) -> bool:
|
|
45
|
+
"""True if the managed runtime stamped a +bundled label onto this install."""
|
|
46
|
+
return version.endswith(_BUNDLED_LOCAL_LABEL)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _user_agent_version(version: str) -> str:
|
|
50
|
+
"""Return the version segment reported in the boto3 user agent.
|
|
51
|
+
|
|
52
|
+
The managed runtime appends ``+bundled`` to ``__version__`` at image build
|
|
53
|
+
time. The user agent reports that install as ``<public version>-bundled``.
|
|
54
|
+
The ``-bundled`` form predates the label and matches the JS SDK, so existing
|
|
55
|
+
telemetry queries keep working. Any other local label is reported unchanged.
|
|
46
56
|
"""
|
|
47
|
-
|
|
57
|
+
if _is_bundled(version):
|
|
58
|
+
return f"{version.removesuffix(_BUNDLED_LOCAL_LABEL)}-bundled"
|
|
59
|
+
return version
|
|
48
60
|
|
|
49
61
|
|
|
50
62
|
# region model
|
|
@@ -81,7 +93,19 @@ class CallbackTimeoutType(Enum):
|
|
|
81
93
|
HEARTBEAT = "Callback.Heartbeat"
|
|
82
94
|
|
|
83
95
|
|
|
84
|
-
class OperationSubType(
|
|
96
|
+
class OperationSubType(StrEnum):
|
|
97
|
+
"""The subtypes the SDK records for its own operations.
|
|
98
|
+
|
|
99
|
+
A subtype is a plain ``str``, and every subtype field is typed
|
|
100
|
+
``str | None``. These members catalog the values the SDK writes for its
|
|
101
|
+
own operations; the SDK assigns ``.value`` into the field. A step, a
|
|
102
|
+
callback, or a child context records a caller-defined string instead when
|
|
103
|
+
``StepConfig.sub_type``, ``CallbackConfig.sub_type``, or
|
|
104
|
+
``ChildConfig.sub_type`` is set. Compare with ``==``:
|
|
105
|
+
``operation.sub_type == OperationSubType.MAP`` is true for the operation
|
|
106
|
+
that ``context.map()`` records.
|
|
107
|
+
"""
|
|
108
|
+
|
|
85
109
|
STEP = "Step"
|
|
86
110
|
WAIT = "Wait"
|
|
87
111
|
CALLBACK = "Callback"
|
|
@@ -217,7 +241,7 @@ class ErrorObject:
|
|
|
217
241
|
)
|
|
218
242
|
|
|
219
243
|
@classmethod
|
|
220
|
-
def from_exception(cls, exception:
|
|
244
|
+
def from_exception(cls, exception: BaseException) -> ErrorObject:
|
|
221
245
|
# SerDesError and subclasses pin to the base discriminator so replay
|
|
222
246
|
# always reconstructs them as SerDesError.
|
|
223
247
|
if isinstance(exception, SerDesError):
|
|
@@ -485,7 +509,7 @@ class OperationUpdate:
|
|
|
485
509
|
action: OperationAction
|
|
486
510
|
parent_id: str | None = None
|
|
487
511
|
name: str | None = None
|
|
488
|
-
sub_type:
|
|
512
|
+
sub_type: str | None = None
|
|
489
513
|
payload: str | None = None
|
|
490
514
|
error: ErrorObject | None = None
|
|
491
515
|
context_options: ContextOptions | None = None
|
|
@@ -506,7 +530,7 @@ class OperationUpdate:
|
|
|
506
530
|
if self.name:
|
|
507
531
|
result["Name"] = self.name
|
|
508
532
|
if self.sub_type:
|
|
509
|
-
result["SubType"] = self.sub_type
|
|
533
|
+
result["SubType"] = self.sub_type
|
|
510
534
|
if self.payload is not None:
|
|
511
535
|
result["Payload"] = self.payload
|
|
512
536
|
if self.error:
|
|
@@ -555,7 +579,7 @@ class OperationUpdate:
|
|
|
555
579
|
action=OperationAction(data["Action"]),
|
|
556
580
|
parent_id=data.get("ParentId"),
|
|
557
581
|
name=data.get("Name"),
|
|
558
|
-
sub_type=
|
|
582
|
+
sub_type=data.get("SubType") or None,
|
|
559
583
|
payload=data.get("Payload"),
|
|
560
584
|
error=error,
|
|
561
585
|
context_options=context_options,
|
|
@@ -574,7 +598,7 @@ class OperationUpdate:
|
|
|
574
598
|
operation_id=identifier.operation_id,
|
|
575
599
|
parent_id=identifier.parent_id,
|
|
576
600
|
operation_type=OperationType.CALLBACK,
|
|
577
|
-
sub_type=
|
|
601
|
+
sub_type=identifier.sub_type,
|
|
578
602
|
action=OperationAction.START,
|
|
579
603
|
name=identifier.name,
|
|
580
604
|
callback_options=callback_options,
|
|
@@ -582,15 +606,13 @@ class OperationUpdate:
|
|
|
582
606
|
|
|
583
607
|
# region context
|
|
584
608
|
@classmethod
|
|
585
|
-
def create_context_start(
|
|
586
|
-
cls, identifier: OperationIdentifier, sub_type: OperationSubType
|
|
587
|
-
) -> OperationUpdate:
|
|
609
|
+
def create_context_start(cls, identifier: OperationIdentifier) -> OperationUpdate:
|
|
588
610
|
"""Create an instance of OperationUpdate for type: CONTEXT, action: START."""
|
|
589
611
|
return cls(
|
|
590
612
|
operation_id=identifier.operation_id,
|
|
591
613
|
parent_id=identifier.parent_id,
|
|
592
614
|
operation_type=OperationType.CONTEXT,
|
|
593
|
-
sub_type=sub_type,
|
|
615
|
+
sub_type=identifier.sub_type,
|
|
594
616
|
action=OperationAction.START,
|
|
595
617
|
name=identifier.name,
|
|
596
618
|
)
|
|
@@ -600,7 +622,7 @@ class OperationUpdate:
|
|
|
600
622
|
cls,
|
|
601
623
|
identifier: OperationIdentifier,
|
|
602
624
|
payload: str | None,
|
|
603
|
-
|
|
625
|
+
*,
|
|
604
626
|
context_options: ContextOptions | None = None,
|
|
605
627
|
) -> OperationUpdate:
|
|
606
628
|
"""Create an instance of OperationUpdate for type: CONTEXT, action: SUCCEED."""
|
|
@@ -608,7 +630,7 @@ class OperationUpdate:
|
|
|
608
630
|
operation_id=identifier.operation_id,
|
|
609
631
|
parent_id=identifier.parent_id,
|
|
610
632
|
operation_type=OperationType.CONTEXT,
|
|
611
|
-
sub_type=sub_type,
|
|
633
|
+
sub_type=identifier.sub_type,
|
|
612
634
|
action=OperationAction.SUCCEED,
|
|
613
635
|
name=identifier.name,
|
|
614
636
|
payload=payload,
|
|
@@ -620,14 +642,13 @@ class OperationUpdate:
|
|
|
620
642
|
cls,
|
|
621
643
|
identifier: OperationIdentifier,
|
|
622
644
|
error: ErrorObject,
|
|
623
|
-
sub_type: OperationSubType,
|
|
624
645
|
) -> OperationUpdate:
|
|
625
646
|
"""Create an instance of OperationUpdate for type: CONTEXT, action: FAIL."""
|
|
626
647
|
return cls(
|
|
627
648
|
operation_id=identifier.operation_id,
|
|
628
649
|
parent_id=identifier.parent_id,
|
|
629
650
|
operation_type=OperationType.CONTEXT,
|
|
630
|
-
sub_type=sub_type,
|
|
651
|
+
sub_type=identifier.sub_type,
|
|
631
652
|
action=OperationAction.FAIL,
|
|
632
653
|
name=identifier.name,
|
|
633
654
|
error=error,
|
|
@@ -668,7 +689,7 @@ class OperationUpdate:
|
|
|
668
689
|
operation_id=identifier.operation_id,
|
|
669
690
|
parent_id=identifier.parent_id,
|
|
670
691
|
operation_type=OperationType.STEP,
|
|
671
|
-
sub_type=
|
|
692
|
+
sub_type=identifier.sub_type,
|
|
672
693
|
action=OperationAction.SUCCEED,
|
|
673
694
|
name=identifier.name,
|
|
674
695
|
payload=payload,
|
|
@@ -683,7 +704,7 @@ class OperationUpdate:
|
|
|
683
704
|
operation_id=identifier.operation_id,
|
|
684
705
|
parent_id=identifier.parent_id,
|
|
685
706
|
operation_type=OperationType.STEP,
|
|
686
|
-
sub_type=
|
|
707
|
+
sub_type=identifier.sub_type,
|
|
687
708
|
action=OperationAction.FAIL,
|
|
688
709
|
name=identifier.name,
|
|
689
710
|
error=error,
|
|
@@ -696,7 +717,7 @@ class OperationUpdate:
|
|
|
696
717
|
operation_id=identifier.operation_id,
|
|
697
718
|
parent_id=identifier.parent_id,
|
|
698
719
|
operation_type=OperationType.STEP,
|
|
699
|
-
sub_type=
|
|
720
|
+
sub_type=identifier.sub_type,
|
|
700
721
|
action=OperationAction.START,
|
|
701
722
|
name=identifier.name,
|
|
702
723
|
)
|
|
@@ -713,7 +734,7 @@ class OperationUpdate:
|
|
|
713
734
|
operation_id=identifier.operation_id,
|
|
714
735
|
parent_id=identifier.parent_id,
|
|
715
736
|
operation_type=OperationType.STEP,
|
|
716
|
-
sub_type=
|
|
737
|
+
sub_type=identifier.sub_type,
|
|
717
738
|
action=OperationAction.RETRY,
|
|
718
739
|
name=identifier.name,
|
|
719
740
|
error=error,
|
|
@@ -737,7 +758,7 @@ class OperationUpdate:
|
|
|
737
758
|
operation_id=identifier.operation_id,
|
|
738
759
|
parent_id=identifier.parent_id,
|
|
739
760
|
operation_type=OperationType.CHAINED_INVOKE,
|
|
740
|
-
sub_type=
|
|
761
|
+
sub_type=identifier.sub_type,
|
|
741
762
|
action=OperationAction.START,
|
|
742
763
|
name=identifier.name,
|
|
743
764
|
payload=payload,
|
|
@@ -756,7 +777,7 @@ class OperationUpdate:
|
|
|
756
777
|
operation_id=identifier.operation_id,
|
|
757
778
|
parent_id=identifier.parent_id,
|
|
758
779
|
operation_type=OperationType.STEP,
|
|
759
|
-
sub_type=
|
|
780
|
+
sub_type=identifier.sub_type,
|
|
760
781
|
action=OperationAction.START,
|
|
761
782
|
name=identifier.name,
|
|
762
783
|
)
|
|
@@ -770,7 +791,7 @@ class OperationUpdate:
|
|
|
770
791
|
operation_id=identifier.operation_id,
|
|
771
792
|
parent_id=identifier.parent_id,
|
|
772
793
|
operation_type=OperationType.STEP,
|
|
773
|
-
sub_type=
|
|
794
|
+
sub_type=identifier.sub_type,
|
|
774
795
|
action=OperationAction.SUCCEED,
|
|
775
796
|
name=identifier.name,
|
|
776
797
|
payload=payload,
|
|
@@ -788,7 +809,7 @@ class OperationUpdate:
|
|
|
788
809
|
operation_id=identifier.operation_id,
|
|
789
810
|
parent_id=identifier.parent_id,
|
|
790
811
|
operation_type=OperationType.STEP,
|
|
791
|
-
sub_type=
|
|
812
|
+
sub_type=identifier.sub_type,
|
|
792
813
|
action=OperationAction.RETRY,
|
|
793
814
|
name=identifier.name,
|
|
794
815
|
payload=payload,
|
|
@@ -806,7 +827,7 @@ class OperationUpdate:
|
|
|
806
827
|
operation_id=identifier.operation_id,
|
|
807
828
|
parent_id=identifier.parent_id,
|
|
808
829
|
operation_type=OperationType.STEP,
|
|
809
|
-
sub_type=
|
|
830
|
+
sub_type=identifier.sub_type,
|
|
810
831
|
action=OperationAction.FAIL,
|
|
811
832
|
name=identifier.name,
|
|
812
833
|
error=error,
|
|
@@ -824,7 +845,7 @@ class OperationUpdate:
|
|
|
824
845
|
operation_id=identifier.operation_id,
|
|
825
846
|
parent_id=identifier.parent_id,
|
|
826
847
|
operation_type=OperationType.WAIT,
|
|
827
|
-
sub_type=
|
|
848
|
+
sub_type=identifier.sub_type,
|
|
828
849
|
action=OperationAction.START,
|
|
829
850
|
name=identifier.name,
|
|
830
851
|
wait_options=wait_options,
|
|
@@ -862,7 +883,7 @@ class Operation:
|
|
|
862
883
|
name: str | None = None
|
|
863
884
|
start_timestamp: datetime.datetime | None = None
|
|
864
885
|
end_timestamp: datetime.datetime | None = None
|
|
865
|
-
sub_type:
|
|
886
|
+
sub_type: str | None = None
|
|
866
887
|
execution_details: ExecutionDetails | None = None
|
|
867
888
|
context_details: ContextDetails | None = None
|
|
868
889
|
step_details: StepDetails | None = None
|
|
@@ -883,9 +904,7 @@ class Operation:
|
|
|
883
904
|
operation_type = OperationType(data.get("Type"))
|
|
884
905
|
operation_status = OperationStatus(data.get("Status"))
|
|
885
906
|
|
|
886
|
-
sub_type = None
|
|
887
|
-
if sub_type_input := data.get("SubType"):
|
|
888
|
-
sub_type = OperationSubType(sub_type_input)
|
|
907
|
+
sub_type = data.get("SubType") or None
|
|
889
908
|
|
|
890
909
|
execution_details = None
|
|
891
910
|
if execution_details_input := data.get("ExecutionDetails"):
|
|
@@ -945,7 +964,7 @@ class Operation:
|
|
|
945
964
|
if self.end_timestamp:
|
|
946
965
|
result["EndTimestamp"] = self.end_timestamp
|
|
947
966
|
if self.sub_type:
|
|
948
|
-
result["SubType"] = self.sub_type
|
|
967
|
+
result["SubType"] = self.sub_type
|
|
949
968
|
if self.execution_details:
|
|
950
969
|
result["ExecutionDetails"] = {
|
|
951
970
|
"InputPayload": self.execution_details.input_payload
|
|
@@ -1201,7 +1220,7 @@ class LambdaClient(DurableServiceClient):
|
|
|
1201
1220
|
config=Config(
|
|
1202
1221
|
connect_timeout=5,
|
|
1203
1222
|
read_timeout=50,
|
|
1204
|
-
user_agent_extra=f"aws-durable-execution-sdk-python/{__version__
|
|
1223
|
+
user_agent_extra=f"aws-durable-execution-sdk-python/{_user_agent_version(__version__)}",
|
|
1205
1224
|
),
|
|
1206
1225
|
)
|
|
1207
1226
|
return cls(client=cls._cached_boto_client)
|
|
@@ -18,7 +18,6 @@ from aws_durable_execution_sdk_python.lambda_service import (
|
|
|
18
18
|
ErrorObject,
|
|
19
19
|
Operation,
|
|
20
20
|
OperationStatus,
|
|
21
|
-
OperationSubType,
|
|
22
21
|
OperationUpdate,
|
|
23
22
|
)
|
|
24
23
|
from aws_durable_execution_sdk_python.constants import CHECKPOINT_SIZE_LIMIT_BYTES
|
|
@@ -71,7 +70,6 @@ class ChildOperationExecutor(OperationExecutor[T]):
|
|
|
71
70
|
self.operation_identifier = operation_identifier
|
|
72
71
|
self.config = config
|
|
73
72
|
self.is_virtual: bool = config.is_virtual
|
|
74
|
-
self.sub_type = config.sub_type or OperationSubType.RUN_IN_CHILD_CONTEXT
|
|
75
73
|
|
|
76
74
|
@staticmethod
|
|
77
75
|
def validate_virtual_checkpoint(
|
|
@@ -151,7 +149,6 @@ class ChildOperationExecutor(OperationExecutor[T]):
|
|
|
151
149
|
if not checkpointed_result.is_existent() and not self.is_virtual:
|
|
152
150
|
start_operation: OperationUpdate = OperationUpdate.create_context_start(
|
|
153
151
|
identifier=self.operation_identifier,
|
|
154
|
-
sub_type=self.sub_type,
|
|
155
152
|
)
|
|
156
153
|
# Checkpoint child context START with non-blocking (is_sync=False).
|
|
157
154
|
# This is a fire-and-forget operation for performance - we don't need to wait for
|
|
@@ -282,7 +279,6 @@ class ChildOperationExecutor(OperationExecutor[T]):
|
|
|
282
279
|
success_operation: OperationUpdate = OperationUpdate.create_context_succeed(
|
|
283
280
|
identifier=self.operation_identifier,
|
|
284
281
|
payload=payload_to_checkpoint,
|
|
285
|
-
sub_type=self.sub_type,
|
|
286
282
|
context_options=ContextOptions(replay_children=replay_children),
|
|
287
283
|
)
|
|
288
284
|
# Checkpoint child context SUCCEED with blocking (is_sync=True, default).
|
|
@@ -319,7 +315,6 @@ class ChildOperationExecutor(OperationExecutor[T]):
|
|
|
319
315
|
fail_operation: OperationUpdate = OperationUpdate.create_context_fail(
|
|
320
316
|
identifier=self.operation_identifier,
|
|
321
317
|
error=error_object,
|
|
322
|
-
sub_type=self.sub_type,
|
|
323
318
|
)
|
|
324
319
|
# Checkpoint child context FAIL with blocking (is_sync=True, default).
|
|
325
320
|
# Must ensure the failure state is persisted before raising the exception.
|
|
@@ -39,8 +39,8 @@ class MapExecutor(Generic[T, R], ConcurrentExecutor[Callable, R]): # noqa: PYI0
|
|
|
39
39
|
items: Sequence[T],
|
|
40
40
|
max_concurrency: int | None,
|
|
41
41
|
completion_config,
|
|
42
|
-
top_level_sub_type:
|
|
43
|
-
iteration_sub_type:
|
|
42
|
+
top_level_sub_type: str,
|
|
43
|
+
iteration_sub_type: str,
|
|
44
44
|
name_prefix: str,
|
|
45
45
|
serdes: SerDes | None,
|
|
46
46
|
operation_id_namespace: OperationIdNamespace,
|
|
@@ -92,8 +92,8 @@ class MapExecutor(Generic[T, R], ConcurrentExecutor[Callable, R]): # noqa: PYI0
|
|
|
92
92
|
items=items,
|
|
93
93
|
max_concurrency=config.max_concurrency,
|
|
94
94
|
completion_config=config.completion_config,
|
|
95
|
-
top_level_sub_type=OperationSubType.MAP,
|
|
96
|
-
iteration_sub_type=OperationSubType.MAP_ITERATION,
|
|
95
|
+
top_level_sub_type=OperationSubType.MAP.value,
|
|
96
|
+
iteration_sub_type=OperationSubType.MAP_ITERATION.value,
|
|
97
97
|
name_prefix="map-item-",
|
|
98
98
|
serdes=config.serdes,
|
|
99
99
|
operation_id_namespace=operation_id_namespace,
|
|
@@ -38,8 +38,8 @@ class ParallelExecutor(ConcurrentExecutor[Callable, R]):
|
|
|
38
38
|
executables: list[Executable[Callable]],
|
|
39
39
|
max_concurrency: int | None,
|
|
40
40
|
completion_config,
|
|
41
|
-
top_level_sub_type:
|
|
42
|
-
iteration_sub_type:
|
|
41
|
+
top_level_sub_type: str,
|
|
42
|
+
iteration_sub_type: str,
|
|
43
43
|
name_prefix: str,
|
|
44
44
|
serdes: SerDes | None,
|
|
45
45
|
operation_id_namespace: OperationIdNamespace,
|
|
@@ -84,8 +84,8 @@ class ParallelExecutor(ConcurrentExecutor[Callable, R]):
|
|
|
84
84
|
executables=executables,
|
|
85
85
|
max_concurrency=config.max_concurrency,
|
|
86
86
|
completion_config=config.completion_config,
|
|
87
|
-
top_level_sub_type=OperationSubType.PARALLEL,
|
|
88
|
-
iteration_sub_type=OperationSubType.PARALLEL_BRANCH,
|
|
87
|
+
top_level_sub_type=OperationSubType.PARALLEL.value,
|
|
88
|
+
iteration_sub_type=OperationSubType.PARALLEL_BRANCH.value,
|
|
89
89
|
name_prefix="parallel-branch-",
|
|
90
90
|
serdes=config.serdes,
|
|
91
91
|
operation_id_namespace=operation_id_namespace,
|