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.
@@ -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
- def _is_in_var_dir(module_file: str = __file__) -> bool:
42
- """Return True if this SDK is installed under /var/lang/.
41
+ _BUNDLED_LOCAL_LABEL = "+bundled"
43
42
 
44
- Lambda bundled Python runtimes install packages at
45
- /var/lang/lib/pythonX.Y/site-packages/.
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
- return module_file.startswith("/var/lang/")
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(Enum):
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: Exception) -> ErrorObject:
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: OperationSubType | None = None
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.value
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=OperationSubType(data["SubType"]) if data.get("SubType") else None,
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=OperationSubType.CALLBACK,
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
- sub_type: OperationSubType,
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=OperationSubType.STEP,
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=OperationSubType.STEP,
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=OperationSubType.STEP,
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=OperationSubType.STEP,
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=OperationSubType.CHAINED_INVOKE,
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=OperationSubType.WAIT_FOR_CONDITION,
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=OperationSubType.WAIT_FOR_CONDITION,
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=OperationSubType.WAIT_FOR_CONDITION,
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=OperationSubType.WAIT_FOR_CONDITION,
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=OperationSubType.WAIT,
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: OperationSubType | None = None
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.value
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__}{'-bundled' if _is_in_var_dir() else ''}",
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: OperationSubType,
43
- iteration_sub_type: OperationSubType,
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: OperationSubType,
42
- iteration_sub_type: OperationSubType,
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,