aws-durable-execution-sdk-python 1.5.0__tar.gz → 1.6.0__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.
Files changed (219) hide show
  1. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/.gitignore +3 -2
  2. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/PKG-INFO +3 -5
  3. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/README.md +2 -2
  4. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/pyproject.toml +8 -59
  5. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/__about__.py +1 -1
  6. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/__init__.py +3 -0
  7. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/concurrency/executor.py +57 -15
  8. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/config.py +12 -0
  9. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/context.py +21 -2
  10. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/execution.py +45 -58
  11. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/identifier.py +10 -0
  12. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/lambda_service.py +95 -1
  13. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/child.py +8 -2
  14. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/step.py +11 -1
  15. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/wait_for_condition.py +9 -1
  16. aws_durable_execution_sdk_python-1.6.0/src/aws_durable_execution_sdk_python/plugin.py +407 -0
  17. aws_durable_execution_sdk_python-1.6.0/src/aws_durable_execution_sdk_python/retries.py +377 -0
  18. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/state.py +84 -20
  19. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/concurrency_test.py +108 -12
  20. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/context_test.py +72 -27
  21. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/e2e/checkpoint_response_int_test.py +11 -11
  22. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/e2e/execution_int_test.py +7 -7
  23. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/e2e/map_with_concurrent_waits_int_test.py +2 -0
  24. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/execution_test.py +349 -55
  25. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/lambda_service_test.py +86 -14
  26. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/logger_test.py +8 -2
  27. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/callback_test.py +92 -34
  28. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/child_test.py +109 -24
  29. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/invoke_test.py +101 -35
  30. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/map_test.py +218 -162
  31. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/parallel_test.py +216 -161
  32. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/step_test.py +58 -25
  33. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/wait_for_condition_test.py +140 -31
  34. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/wait_test.py +29 -11
  35. aws_durable_execution_sdk_python-1.6.0/tests/plugin_test.py +787 -0
  36. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/retries_test.py +147 -0
  37. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/state_test.py +603 -7
  38. aws_durable_execution_sdk_python-1.6.0/tests/with_retry_test.py +372 -0
  39. aws_durable_execution_sdk_python-1.5.0/.github/ISSUE_TEMPLATE/bug_report.yml +0 -91
  40. aws_durable_execution_sdk_python-1.5.0/.github/ISSUE_TEMPLATE/config.yml +0 -5
  41. aws_durable_execution_sdk_python-1.5.0/.github/ISSUE_TEMPLATE/documentation.yml +0 -36
  42. aws_durable_execution_sdk_python-1.5.0/.github/ISSUE_TEMPLATE/feature_request.yml +0 -57
  43. aws_durable_execution_sdk_python-1.5.0/.github/dependabot.yml +0 -14
  44. aws_durable_execution_sdk_python-1.5.0/.github/scripts/__init__.py +0 -1
  45. aws_durable_execution_sdk_python-1.5.0/.github/scripts/ci-checks.sh +0 -18
  46. aws_durable_execution_sdk_python-1.5.0/.github/scripts/lintcommit.py +0 -265
  47. aws_durable_execution_sdk_python-1.5.0/.github/scripts/parse_sdk_branch.py +0 -31
  48. aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests/__init__.py +0 -1
  49. aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests/test_lintcommit.py +0 -261
  50. aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests/test_parse_sdk_branch.py +0 -84
  51. aws_durable_execution_sdk_python-1.5.0/.github/workflows/ci.yml +0 -52
  52. aws_durable_execution_sdk_python-1.5.0/.github/workflows/deploy-examples.yml +0 -134
  53. aws_durable_execution_sdk_python-1.5.0/.github/workflows/integration-tests.yml +0 -162
  54. aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-issues.yml +0 -23
  55. aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-pr.yml +0 -24
  56. aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-release.yml +0 -29
  57. aws_durable_execution_sdk_python-1.5.0/.github/workflows/pypi-publish.yml +0 -79
  58. aws_durable_execution_sdk_python-1.5.0/.github/workflows/scorecard.yml +0 -80
  59. aws_durable_execution_sdk_python-1.5.0/.github/workflows/sync-package.yml +0 -72
  60. aws_durable_execution_sdk_python-1.5.0/.github/workflows/test-parser.yml +0 -24
  61. aws_durable_execution_sdk_python-1.5.0/.github/workflows/update-sam-template.yml +0 -42
  62. aws_durable_execution_sdk_python-1.5.0/AGENTS.md +0 -929
  63. aws_durable_execution_sdk_python-1.5.0/CODE_OF_CONDUCT.md +0 -4
  64. aws_durable_execution_sdk_python-1.5.0/CONTRIBUTING.md +0 -388
  65. aws_durable_execution_sdk_python-1.5.0/examples/cli.py +0 -487
  66. aws_durable_execution_sdk_python-1.5.0/examples/examples-catalog.json +0 -607
  67. aws_durable_execution_sdk_python-1.5.0/examples/scripts/generate_sam_template.py +0 -106
  68. aws_durable_execution_sdk_python-1.5.0/examples/src/__init__.py +0 -3
  69. aws_durable_execution_sdk_python-1.5.0/examples/src/block_example/block_example.py +0 -47
  70. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_concurrency.py +0 -51
  71. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_heartbeat.py +0 -22
  72. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_mixed_ops.py +0 -35
  73. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_serdes.py +0 -76
  74. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_simple.py +0 -22
  75. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_with_timeout.py +0 -23
  76. aws_durable_execution_sdk_python-1.5.0/examples/src/comprehensive_operations/comprehensive_operations.py +0 -51
  77. aws_durable_execution_sdk_python-1.5.0/examples/src/handler_error/handler_error.py +0 -13
  78. aws_durable_execution_sdk_python-1.5.0/examples/src/hello_world.py +0 -62
  79. aws_durable_execution_sdk_python-1.5.0/examples/src/logger_example/logger_example.py +0 -64
  80. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_completion.py +0 -117
  81. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_operations.py +0 -23
  82. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_operations_flat.py +0 -23
  83. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_batch_serdes.py +0 -96
  84. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_custom_serdes.py +0 -63
  85. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_failure_tolerance.py +0 -53
  86. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_item_namer.py +0 -30
  87. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_large_scale.py +0 -64
  88. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_max_concurrency.py +0 -23
  89. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_min_successful.py +0 -43
  90. aws_durable_execution_sdk_python-1.5.0/examples/src/no_replay_execution/no_replay_execution.py +0 -15
  91. aws_durable_execution_sdk_python-1.5.0/examples/src/none_results/none_results.py +0 -31
  92. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel.py +0 -27
  93. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_first_successful.py +0 -27
  94. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_flat.py +0 -27
  95. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_batch_serdes.py +0 -97
  96. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_custom_serdes.py +0 -60
  97. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_failure_tolerance.py +0 -59
  98. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_max_concurrency.py +0 -25
  99. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_named_branches.py +0 -51
  100. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_wait.py +0 -24
  101. aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context.py +0 -22
  102. aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context_large_data.py +0 -73
  103. aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context_step_failure.py +0 -50
  104. aws_durable_execution_sdk_python-1.5.0/examples/src/simple_execution/simple_execution.py +0 -18
  105. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step.py +0 -19
  106. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_no_name.py +0 -11
  107. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_semantics_at_most_once.py +0 -18
  108. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_exponential_backoff.py +0 -27
  109. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_name.py +0 -11
  110. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_retry.py +0 -46
  111. aws_durable_execution_sdk_python-1.5.0/examples/src/step/steps_with_retry.py +0 -81
  112. aws_durable_execution_sdk_python-1.5.0/examples/src/wait/multiple_wait.py +0 -19
  113. aws_durable_execution_sdk_python-1.5.0/examples/src/wait/wait.py +0 -11
  114. aws_durable_execution_sdk_python-1.5.0/examples/src/wait/wait_with_name.py +0 -12
  115. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback.py +0 -27
  116. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_anonymous.py +0 -20
  117. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_child.py +0 -42
  118. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_heartbeat.py +0 -33
  119. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_mixed_ops.py +0 -47
  120. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_multiple_invocations.py +0 -53
  121. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_nested.py +0 -66
  122. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_serdes.py +0 -90
  123. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_submitter_failure.py +0 -39
  124. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_submitter_failure_catchable.py +0 -52
  125. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_timeout.py +0 -35
  126. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_condition/wait_for_condition.py +0 -32
  127. aws_durable_execution_sdk_python-1.5.0/examples/template.yaml +0 -982
  128. aws_durable_execution_sdk_python-1.5.0/examples/test/README.md +0 -119
  129. aws_durable_execution_sdk_python-1.5.0/examples/test/__init__.py +0 -1
  130. aws_durable_execution_sdk_python-1.5.0/examples/test/block_example/test_block_example.py +0 -104
  131. aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_concurrency.py +0 -83
  132. aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_heartbeat.py +0 -51
  133. aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_mixed_ops.py +0 -49
  134. aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_serdes.py +0 -60
  135. aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_simple.py +0 -47
  136. aws_durable_execution_sdk_python-1.5.0/examples/test/comprehensive_operations/test_comprehensive_operations.py +0 -94
  137. aws_durable_execution_sdk_python-1.5.0/examples/test/conftest.py +0 -268
  138. aws_durable_execution_sdk_python-1.5.0/examples/test/handler_error/test_handler_error.py +0 -32
  139. aws_durable_execution_sdk_python-1.5.0/examples/test/logger_example/test_logger_example.py +0 -35
  140. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_completion.py +0 -32
  141. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_operations.py +0 -42
  142. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_operations_flat.py +0 -39
  143. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_batch_serdes.py +0 -43
  144. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_custom_serdes.py +0 -48
  145. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_failure_tolerance.py +0 -52
  146. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_item_namer.py +0 -39
  147. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_large_scale.py +0 -36
  148. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_max_concurrency.py +0 -37
  149. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_min_successful.py +0 -70
  150. aws_durable_execution_sdk_python-1.5.0/examples/test/no_replay_execution/test_no_replay_execution.py +0 -52
  151. aws_durable_execution_sdk_python-1.5.0/examples/test/none_results/test_none_results.py +0 -51
  152. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel.py +0 -42
  153. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_flat.py +0 -42
  154. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_batch_serdes.py +0 -43
  155. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_custom_serdes.py +0 -46
  156. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_failure_tolerance.py +0 -49
  157. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_max_concurrency.py +0 -36
  158. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_named_branches.py +0 -57
  159. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_wait.py +0 -47
  160. aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context.py +0 -27
  161. aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context_large_data.py +0 -36
  162. aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context_step_failure.py +0 -23
  163. aws_durable_execution_sdk_python-1.5.0/examples/test/simple_execution/test_simple_execution.py +0 -40
  164. aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step.py +0 -24
  165. aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_permutations.py +0 -75
  166. aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_semantics_at_most_once.py +0 -32
  167. aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_with_retry.py +0 -40
  168. aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_steps_with_retry.py +0 -52
  169. aws_durable_execution_sdk_python-1.5.0/examples/test/test_hello_world.py +0 -24
  170. aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_multiple_wait.py +0 -57
  171. aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_wait.py +0 -27
  172. aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_wait_permutations.py +0 -25
  173. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_anonymous.py +0 -39
  174. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_child.py +0 -73
  175. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_failure.py +0 -27
  176. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_heartbeat.py +0 -62
  177. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_mixed_ops.py +0 -52
  178. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_multiple_invocations.py +0 -74
  179. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_nested.py +0 -101
  180. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_serdes.py +0 -66
  181. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_submitter_failure.py +0 -32
  182. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_submitter_failure_catchable.py +0 -28
  183. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_success.py +0 -25
  184. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_timeout.py +0 -32
  185. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_condition/test_wait_for_condition.py +0 -24
  186. aws_durable_execution_sdk_python-1.5.0/src/aws_durable_execution_sdk_python/retries.py +0 -174
  187. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/LICENSE +0 -0
  188. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/NOTICE +0 -0
  189. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/.gitignore +0 -0
  190. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/concurrency/__init__.py +0 -0
  191. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/concurrency/models.py +0 -0
  192. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/exceptions.py +0 -0
  193. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/logger.py +0 -0
  194. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/__init__.py +0 -0
  195. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/base.py +0 -0
  196. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/callback.py +0 -0
  197. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/invoke.py +0 -0
  198. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/map.py +0 -0
  199. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/parallel.py +0 -0
  200. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/wait.py +0 -0
  201. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/py.typed +0 -0
  202. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/serdes.py +0 -0
  203. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/suspend.py +0 -0
  204. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/threading.py +0 -0
  205. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/types.py +0 -0
  206. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/waits.py +0 -0
  207. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/__init__.py +0 -0
  208. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/config_test.py +0 -0
  209. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/durable_executions_python_language_sdk_test.py +0 -0
  210. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/e2e/__init__.py +0 -0
  211. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/exceptions_test.py +0 -0
  212. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/__init__.py +0 -0
  213. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/base_test.py +0 -0
  214. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/serdes_test.py +0 -0
  215. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/suspend_test.py +0 -0
  216. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/test_helpers.py +0 -0
  217. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/threading_test.py +0 -0
  218. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/types_test.py +0 -0
  219. {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/waits_test.py +0 -0
@@ -9,6 +9,7 @@ __pycache__/
9
9
  *$py.class
10
10
  *.egg-info/
11
11
 
12
+ *.coverage
12
13
  /.coverage
13
14
  /.coverage.*
14
15
  /.cache
@@ -30,7 +31,7 @@ dist/
30
31
 
31
32
  .kiro/
32
33
 
33
- /examples/build/*
34
- /examples/*.zip
34
+ **/build/
35
+ **/*.zip
35
36
 
36
37
  .env
@@ -1,14 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-durable-execution-sdk-python
3
- Version: 1.5.0
3
+ Version: 1.6.0
4
4
  Summary: AWS Durable Execution SDK for Python
5
5
  Project-URL: Documentation, https://github.com/aws/aws-durable-execution-sdk-python#readme
6
6
  Project-URL: Issues, https://github.com/aws/aws-durable-execution-sdk-python/issues
7
7
  Project-URL: Source, https://github.com/aws/aws-durable-execution-sdk-python
8
8
  Author-email: AWS durable-execution-dev <durable-execution-dev@amazon.com>
9
9
  License-Expression: Apache-2.0
10
- License-File: LICENSE
11
- License-File: NOTICE
12
10
  Classifier: Development Status :: 4 - Beta
13
11
  Classifier: Programming Language :: Python
14
12
  Classifier: Programming Language :: Python :: 3.11
@@ -27,7 +25,7 @@ Description-Content-Type: text/markdown
27
25
  [![PyPI - Version](https://img.shields.io/pypi/v/aws-durable-execution-sdk-python.svg)](https://pypi.org/project/aws-durable-execution-sdk-python)
28
26
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/aws-durable-execution-sdk-python.svg)](https://pypi.org/project/aws-durable-execution-sdk-python)
29
27
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/aws/aws-durable-execution-sdk-python/badge)](https://scorecard.dev/viewer/?uri=github.com/aws/aws-durable-execution-sdk-python)
30
- [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
28
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/aws/aws-durable-execution-sdk-python/blob/main/LICENSE)
31
29
 
32
30
  -----
33
31
 
@@ -105,4 +103,4 @@ The complete documentation for the AWS Durable Execution SDK for Python lives on
105
103
 
106
104
  ## 📄 License
107
105
 
108
- See the [LICENSE](LICENSE) file for our project's licensing.
106
+ See the [LICENSE](https://github.com/aws/aws-durable-execution-sdk-python/blob/main/LICENSE) file for our project's licensing.
@@ -4,7 +4,7 @@
4
4
  [![PyPI - Version](https://img.shields.io/pypi/v/aws-durable-execution-sdk-python.svg)](https://pypi.org/project/aws-durable-execution-sdk-python)
5
5
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/aws-durable-execution-sdk-python.svg)](https://pypi.org/project/aws-durable-execution-sdk-python)
6
6
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/aws/aws-durable-execution-sdk-python/badge)](https://scorecard.dev/viewer/?uri=github.com/aws/aws-durable-execution-sdk-python)
7
- [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
7
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/aws/aws-durable-execution-sdk-python/blob/main/LICENSE)
8
8
 
9
9
  -----
10
10
 
@@ -82,4 +82,4 @@ The complete documentation for the AWS Durable Execution SDK for Python lives on
82
82
 
83
83
  ## 📄 License
84
84
 
85
- See the [LICENSE](LICENSE) file for our project's licensing.
85
+ See the [LICENSE](https://github.com/aws/aws-durable-execution-sdk-python/blob/main/LICENSE) file for our project's licensing.
@@ -28,49 +28,20 @@ Documentation = "https://github.com/aws/aws-durable-execution-sdk-python#readme"
28
28
  Issues = "https://github.com/aws/aws-durable-execution-sdk-python/issues"
29
29
  Source = "https://github.com/aws/aws-durable-execution-sdk-python"
30
30
 
31
+ [tool.hatch.build.targets.sdist.force-include]
32
+ "../../LICENSE" = "LICENSE"
33
+ "../../NOTICE" = "NOTICE"
34
+
31
35
  [tool.hatch.build.targets.wheel]
32
36
  packages = ["src/aws_durable_execution_sdk_python"]
33
37
 
38
+ [tool.hatch.build.targets.wheel.force-include]
39
+ "../../LICENSE" = "aws_durable_execution_sdk_python/LICENSE"
40
+ "../../NOTICE" = "aws_durable_execution_sdk_python/NOTICE"
41
+
34
42
  [tool.hatch.version]
35
43
  path = "src/aws_durable_execution_sdk_python/__about__.py"
36
44
 
37
- [tool.hatch.envs.test]
38
- dependencies = [
39
- "coverage[toml]",
40
- "pytest",
41
- "pytest-cov",
42
- "aws-durable-execution-sdk-python-testing>=1.0.0",
43
- ]
44
-
45
- [tool.hatch.envs.test.scripts]
46
- examples = "pytest --runner-mode=local -m example examples/test/ -v"
47
- examples-integration = "pytest --runner-mode=cloud -m example examples/test/ -v {args}"
48
- cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=src/aws_durable_execution_sdk_python --cov-fail-under=98"
49
-
50
- [tool.hatch.envs.types]
51
- extra-dependencies = ["mypy>=1.0.0", "pytest", "boto3-stubs[lambda]"]
52
-
53
- [tool.hatch.envs.types.scripts]
54
- check = "mypy --install-types --non-interactive {args:src/aws_durable_execution_sdk_python tests}"
55
-
56
- [tool.hatch.envs.examples]
57
- dependencies = [
58
- "aws-durable-execution-sdk-python-testing>=1.0.0",
59
- ]
60
-
61
- [tool.hatch.envs.examples.scripts]
62
- cli = "python examples/cli.py {args}"
63
- bootstrap = "python examples/cli.py bootstrap"
64
- generate-sam-template = "python examples/scripts/generate_sam_template.py"
65
- build = "python examples/cli.py build"
66
- deploy = "python examples/cli.py deploy {args}"
67
- invoke = "python examples/cli.py invoke {args}"
68
- get = "python examples/cli.py get {args}"
69
- history = "python examples/cli.py history {args}"
70
- policy = "python examples/cli.py policy {args}"
71
- list = "python examples/cli.py list"
72
- clean = "rm -rf examples/build examples/.aws-sam examples/*.zip"
73
-
74
45
  [tool.coverage.run]
75
46
  source_pkgs = ["aws_durable_execution_sdk_python"]
76
47
  branch = true
@@ -110,25 +81,3 @@ lines-after-imports = 2
110
81
  "SIM117",
111
82
  "TRY301",
112
83
  ]
113
- "examples/test/**" = [
114
- "ARG001",
115
- "ARG002",
116
- "ARG005",
117
- "S101",
118
- "PLR2004",
119
- "SIM117",
120
- "TRY301",
121
- ]
122
-
123
- [tool.pytest.ini_options]
124
- # Declare custom markers to avoid warnings with --strict-markers
125
- markers = [
126
- # Used for test selection with -m example
127
- "example: marks tests as example tests (deselect with '-m \"not example\"')",
128
- # Used for configuration - passes handler and lambda_function_name to durable_runner fixture
129
- "durable_execution: marks tests that use the durable_runner fixture (not used for test selection)",
130
- ]
131
- # Default test discovery paths
132
- testpaths = ["tests"]
133
- # Default options for all test runs
134
- addopts = "-v --strict-markers"
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2025-present Amazon.com, Inc. or its affiliates.
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- __version__ = "1.5.0"
4
+ __version__ = "1.6.0"
@@ -25,6 +25,7 @@ from aws_durable_execution_sdk_python.exceptions import (
25
25
 
26
26
  # Core decorator - used in every durable function
27
27
  from aws_durable_execution_sdk_python.execution import durable_execution
28
+ from aws_durable_execution_sdk_python.retries import WithRetryConfig, with_retry
28
29
 
29
30
  # Essential context types - passed to user functions
30
31
  from aws_durable_execution_sdk_python.types import StepContext
@@ -38,10 +39,12 @@ __all__ = [
38
39
  "ParallelBranch",
39
40
  "StepContext",
40
41
  "ValidationError",
42
+ "WithRetryConfig",
41
43
  "__version__",
42
44
  "durable_execution",
43
45
  "durable_parallel_branch",
44
46
  "durable_step",
45
47
  "durable_wait_for_callback",
46
48
  "durable_with_child_context",
49
+ "with_retry",
47
50
  ]
@@ -30,7 +30,7 @@ from aws_durable_execution_sdk_python.exceptions import (
30
30
  TimedSuspendExecution,
31
31
  )
32
32
  from aws_durable_execution_sdk_python.identifier import OperationIdentifier
33
- from aws_durable_execution_sdk_python.lambda_service import ErrorObject
33
+ from aws_durable_execution_sdk_python.lambda_service import ErrorObject, OperationType
34
34
  from aws_durable_execution_sdk_python.operation.child import child_handler
35
35
 
36
36
 
@@ -59,7 +59,7 @@ class TimerScheduler:
59
59
  """Manage timed suspend tasks with a background timer thread."""
60
60
 
61
61
  def __init__(
62
- self, resubmit_callback: Callable[[ExecutableWithState], None]
62
+ self, resubmit_callback: Callable[[list[ExecutableWithState]], None]
63
63
  ) -> None:
64
64
  self.resubmit_callback = resubmit_callback
65
65
  self._pending_resumes: list[tuple[float, int, ExecutableWithState]] = []
@@ -114,18 +114,31 @@ class TimerScheduler:
114
114
 
115
115
  current_time = time.time()
116
116
  if current_time >= next_resume_time:
117
- # Time to resume
117
+ # Drain every due resume under the lock, transitioning each to
118
+ # PENDING atomically with the pop. Keeping pop+reset_to_pending
119
+ # together is required: should_execution_suspend reads branch
120
+ # status without this lock, so an item that is removed from the
121
+ # heap but still SUSPENDED_WITH_TIMEOUT could trigger a spurious
122
+ # parent suspend.
123
+ ready: list[ExecutableWithState] = []
118
124
  with self._lock:
119
- # no branch cover because hard to test reliably - this is a double-safety check if heap mutated
120
- # since the first peek on next_resume_time further up
121
- if ( # pragma: no branch
125
+ while (
122
126
  self._pending_resumes
123
127
  and self._pending_resumes[0][0] <= current_time
124
128
  ):
125
129
  _, _, exe_state = heapq.heappop(self._pending_resumes)
126
130
  if exe_state.can_resume:
127
131
  exe_state.reset_to_pending()
128
- self.resubmit_callback(exe_state)
132
+ ready.append(exe_state)
133
+ # Resubmit outside the lock. Only the heap pop and the PENDING
134
+ # transition need the lock. The checkpoint refresh is a blocking
135
+ # network call and the submit hands work to the pool, so running
136
+ # them off the lock keeps timed resumes from serializing behind
137
+ # the network round trip and keeps the timer thread from
138
+ # re-entering this non-reentrant lock when a submitted future
139
+ # completes inline and its done-callback calls schedule_resume.
140
+ if ready:
141
+ self.resubmit_callback(ready)
129
142
  else:
130
143
  # Wait until next resume time
131
144
  wait_time = min(next_resume_time - current_time, 0.1)
@@ -169,6 +182,7 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
169
182
  # Event-driven state tracking for when the executor is done
170
183
  self._completion_event = threading.Event()
171
184
  self._suspend_exception: SuspendExecution | None = None
185
+ self._resume_error: Exception | None = None
172
186
 
173
187
  # ExecutionCounters will keep track of completion criteria and on-going counters
174
188
  min_successful = self.completion_config.min_successful or len(self.executables)
@@ -222,11 +236,32 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
222
236
  ]
223
237
  self._completion_event.clear()
224
238
  self._suspend_exception = None
225
-
226
- def resubmitter(executable_with_state: ExecutableWithState) -> None:
227
- """Resubmit a timed suspended task."""
228
- execution_state.create_checkpoint()
229
- submit_task(executable_with_state)
239
+ self._resume_error = None
240
+
241
+ def resubmitter(ready: list[ExecutableWithState]) -> None:
242
+ """Resubmit a wave of timed-suspended tasks.
243
+
244
+ One checkpoint refresh serves the whole due wave: the fetch returns
245
+ all operations, so every resumed branch reads fresh state. The
246
+ refresh only raises when the background checkpoint subsystem has
247
+ failed, which is terminal for the whole execution, so record the
248
+ error and wake the parent to re-raise it. Catching here keeps the
249
+ single timer thread alive so a failure does not strand the other
250
+ pending resumes.
251
+ """
252
+ try:
253
+ execution_state.create_checkpoint()
254
+ except Exception as exc: # noqa: BLE001
255
+ # resubmitter runs only on the single timer thread, so this
256
+ # check-then-set needs no lock. First error wins: keep the
257
+ # earliest failure if several waves fail before execute() reads
258
+ # it (they are the same terminal checkpoint failure anyway).
259
+ if self._resume_error is None: # pragma: no branch
260
+ self._resume_error = exc
261
+ self._completion_event.set()
262
+ return
263
+ for executable_with_state in ready:
264
+ submit_task(executable_with_state)
230
265
 
231
266
  thread_executor = ThreadPoolExecutor(max_workers=max_workers)
232
267
  try:
@@ -259,6 +294,12 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
259
294
  for future in futures:
260
295
  future.cancel()
261
296
 
297
+ # A timed resume failed to refresh state (terminal checkpoint
298
+ # subsystem failure). Re-raise so the invocation fails and the
299
+ # backend retries from the last durable checkpoint.
300
+ if self._resume_error is not None:
301
+ raise self._resume_error
302
+
262
303
  # Suspend execution if everything done and at least one of the tasks raised a suspend exception.
263
304
  if self._suspend_exception:
264
305
  raise self._suspend_exception
@@ -428,9 +469,10 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
428
469
  # For FLAT `child_handler` skips checkpoints, so not used.
429
470
  # Construct it unconditionally to keep the call simple.
430
471
  operation_identifier = OperationIdentifier(
431
- operation_id,
432
- executor_context._parent_id, # noqa: SLF001
433
- name,
472
+ operation_id=operation_id,
473
+ sub_type=self.sub_type_iteration,
474
+ parent_id=executor_context._parent_id, # noqa: SLF001
475
+ name=name,
434
476
  )
435
477
 
436
478
  def run_in_child_handler() -> ResultType:
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import math
5
6
  import random
6
7
  from dataclasses import dataclass, field
7
8
  from enum import Enum, StrEnum
@@ -589,5 +590,16 @@ class JitterStrategy(StrEnum):
589
590
  # Full jitter: random(0, delay)
590
591
  return random.random() * delay # noqa: S311
591
592
 
593
+ def finalize_delay(self, base_delay: float) -> int:
594
+ """Apply jitter, round up, and clamp to a minimum of 1 second.
595
+
596
+ Args:
597
+ base_delay: The base delay value before jitter is applied
598
+
599
+ Returns:
600
+ The final delay in whole seconds, at least 1
601
+ """
602
+ return max(1, math.ceil(self.apply_jitter(base_delay)))
603
+
592
604
 
593
605
  # endregion Jitter
@@ -23,7 +23,10 @@ from aws_durable_execution_sdk_python.exceptions import (
23
23
  ValidationError,
24
24
  )
25
25
  from aws_durable_execution_sdk_python.identifier import OperationIdentifier
26
- from aws_durable_execution_sdk_python.lambda_service import OperationSubType
26
+ from aws_durable_execution_sdk_python.lambda_service import (
27
+ OperationSubType,
28
+ OperationType,
29
+ )
27
30
  from aws_durable_execution_sdk_python.logger import Logger, LogInfo
28
31
  from aws_durable_execution_sdk_python.operation.callback import (
29
32
  CallbackOperationExecutor,
@@ -443,6 +446,7 @@ class DurableContext(DurableContextProtocol):
443
446
  state=self.state,
444
447
  operation_identifier=OperationIdentifier(
445
448
  operation_id=operation_id,
449
+ sub_type=OperationSubType.CALLBACK,
446
450
  parent_id=self._parent_id,
447
451
  name=name,
448
452
  ),
@@ -485,6 +489,7 @@ class DurableContext(DurableContextProtocol):
485
489
  state=self.state,
486
490
  operation_identifier=OperationIdentifier(
487
491
  operation_id=operation_id,
492
+ sub_type=OperationSubType.CHAINED_INVOKE,
488
493
  parent_id=self._parent_id,
489
494
  name=name,
490
495
  ),
@@ -507,6 +512,7 @@ class DurableContext(DurableContextProtocol):
507
512
  operation_id = self._create_step_id()
508
513
  operation_identifier = OperationIdentifier(
509
514
  operation_id=operation_id,
515
+ sub_type=OperationSubType.MAP,
510
516
  parent_id=self._parent_id,
511
517
  name=map_name,
512
518
  )
@@ -553,7 +559,10 @@ class DurableContext(DurableContextProtocol):
553
559
  operation_id = self._create_step_id()
554
560
  parallel_context = self.create_child_context(operation_id=operation_id)
555
561
  operation_identifier = OperationIdentifier(
556
- operation_id=operation_id, parent_id=self._parent_id, name=name
562
+ operation_id=operation_id,
563
+ sub_type=OperationSubType.PARALLEL,
564
+ parent_id=self._parent_id,
565
+ name=name,
557
566
  )
558
567
 
559
568
  def parallel_in_child_context() -> BatchResult[T]:
@@ -606,6 +615,11 @@ class DurableContext(DurableContextProtocol):
606
615
  step_name: str | None = self._resolve_step_name(name, func)
607
616
  # _create_step_id() is thread-safe. rest of method is safe, since using local copy of parent id
608
617
  operation_id = self._create_step_id()
618
+ sub_type = (
619
+ config.sub_type
620
+ if config and config.sub_type
621
+ else OperationSubType.RUN_IN_CHILD_CONTEXT
622
+ )
609
623
 
610
624
  is_virtual: bool = config.is_virtual if config else False
611
625
 
@@ -621,6 +635,7 @@ class DurableContext(DurableContextProtocol):
621
635
  state=self.state,
622
636
  operation_identifier=OperationIdentifier(
623
637
  operation_id=operation_id,
638
+ sub_type=sub_type,
624
639
  parent_id=self._parent_id,
625
640
  name=step_name,
626
641
  ),
@@ -646,6 +661,7 @@ class DurableContext(DurableContextProtocol):
646
661
  state=self.state,
647
662
  operation_identifier=OperationIdentifier(
648
663
  operation_id=operation_id,
664
+ sub_type=OperationSubType.STEP,
649
665
  parent_id=self._parent_id,
650
666
  name=step_name,
651
667
  ),
@@ -673,6 +689,7 @@ class DurableContext(DurableContextProtocol):
673
689
  state=self.state,
674
690
  operation_identifier=OperationIdentifier(
675
691
  operation_id=operation_id,
692
+ sub_type=OperationSubType.WAIT,
676
693
  parent_id=self._parent_id,
677
694
  name=name,
678
695
  ),
@@ -695,6 +712,7 @@ class DurableContext(DurableContextProtocol):
695
712
  return self.run_in_child_context(
696
713
  wait_in_child_context,
697
714
  step_name,
715
+ ChildConfig(sub_type=OperationSubType.WAIT_FOR_CALLBACK),
698
716
  )
699
717
 
700
718
  def wait_for_condition(
@@ -728,6 +746,7 @@ class DurableContext(DurableContextProtocol):
728
746
  state=self.state,
729
747
  operation_identifier=OperationIdentifier(
730
748
  operation_id=operation_id,
749
+ sub_type=OperationSubType.WAIT_FOR_CONDITION,
731
750
  parent_id=self._parent_id,
732
751
  name=name,
733
752
  ),
@@ -4,9 +4,9 @@ import contextlib
4
4
  import functools
5
5
  import json
6
6
  import logging
7
+ import warnings
7
8
  from concurrent.futures import ThreadPoolExecutor
8
9
  from dataclasses import dataclass
9
- from enum import Enum
10
10
  from typing import TYPE_CHECKING, Any
11
11
 
12
12
  from aws_durable_execution_sdk_python.context import DurableContext
@@ -20,15 +20,22 @@ from aws_durable_execution_sdk_python.exceptions import (
20
20
  SuspendExecution,
21
21
  )
22
22
  from aws_durable_execution_sdk_python.lambda_service import (
23
+ DurableExecutionInvocationOutput,
23
24
  DurableServiceClient,
24
25
  ErrorObject,
26
+ InvocationStatus,
25
27
  LambdaClient,
26
28
  Operation,
27
29
  OperationType,
28
30
  OperationUpdate,
29
31
  )
32
+ from aws_durable_execution_sdk_python.plugin import (
33
+ DurableInstrumentationPlugin,
34
+ PluginExecutor,
35
+ )
30
36
  from aws_durable_execution_sdk_python.state import ExecutionState, ReplayStatus
31
37
 
38
+
32
39
  if TYPE_CHECKING:
33
40
  from collections.abc import Callable, MutableMapping
34
41
 
@@ -149,62 +156,6 @@ class DurableExecutionInvocationInputWithClient(DurableExecutionInvocationInput)
149
156
  )
150
157
 
151
158
 
152
- class InvocationStatus(Enum):
153
- SUCCEEDED = "SUCCEEDED"
154
- FAILED = "FAILED"
155
- PENDING = "PENDING"
156
-
157
-
158
- @dataclass(frozen=True)
159
- class DurableExecutionInvocationOutput:
160
- """Representation the DurableExecutionInvocationOutput. This is what the Durable lambda handler returns.
161
-
162
- If the execution has been already completed via an update to the EXECUTION operation via CheckpointDurableExecution,
163
- payload must be empty for SUCCEEDED/FAILED status.
164
- """
165
-
166
- status: InvocationStatus
167
- result: str | None = None
168
- error: ErrorObject | None = None
169
-
170
- @classmethod
171
- def from_dict(
172
- cls, data: MutableMapping[str, Any]
173
- ) -> DurableExecutionInvocationOutput:
174
- """Create an instance from a dictionary.
175
-
176
- Args:
177
- data: Dictionary with camelCase keys matching the original structure
178
-
179
- Returns:
180
- A DurableExecutionInvocationOutput instance
181
- """
182
- status = InvocationStatus(data.get("Status"))
183
- error = ErrorObject.from_dict(data["Error"]) if data.get("Error") else None
184
- return cls(status=status, result=data.get("Result"), error=error)
185
-
186
- def to_dict(self) -> MutableMapping[str, Any]:
187
- """Convert to a dictionary with the original field names.
188
-
189
- Returns:
190
- Dictionary with the original camelCase keys
191
- """
192
- result: MutableMapping[str, Any] = {"Status": self.status.value}
193
-
194
- if self.result is not None:
195
- # large payloads return "", because checkpointed already
196
- result["Result"] = self.result
197
- if self.error:
198
- result["Error"] = self.error.to_dict()
199
-
200
- return result
201
-
202
- @classmethod
203
- def create_succeeded(cls, result: str) -> DurableExecutionInvocationOutput:
204
- """Create a succeeded invocation output."""
205
- return cls(status=InvocationStatus.SUCCEEDED, result=result)
206
-
207
-
208
159
  # endregion Invocation models
209
160
 
210
161
 
@@ -212,14 +163,36 @@ def durable_execution(
212
163
  func: Callable[[Any, DurableContext], Any] | None = None,
213
164
  *,
214
165
  boto3_client: Boto3LambdaClient | None = None,
166
+ plugins: list[DurableInstrumentationPlugin] | None = None,
215
167
  ) -> Callable[[Any, LambdaContext], Any]:
168
+ """
169
+ Decorator to create a durable execution handler.
170
+
171
+ Args:
172
+ func: The user function to decorate
173
+ boto3_client: Optional boto3 Lambda client to use
174
+ plugins: Optional list of plugins to use (EXPERIMENTAL: This
175
+ feature has known issues and this parameter may change or be removed.)
176
+ """
216
177
  # Decorator called with parameters
217
178
  if func is None:
218
179
  logger.debug("Decorator called with parameters")
219
- return functools.partial(durable_execution, boto3_client=boto3_client)
180
+ return functools.partial(
181
+ durable_execution, boto3_client=boto3_client, plugins=plugins
182
+ )
220
183
 
221
184
  logger.debug("Starting durable execution handler...")
222
185
 
186
+ if plugins:
187
+ warnings.warn(
188
+ "The 'plugins' parameter is provisional and may be altered or removed.",
189
+ category=FutureWarning,
190
+ stacklevel=2, # point the warning to the caller of durable_execution
191
+ )
192
+
193
+ plugin_executor = PluginExecutor(plugins)
194
+
195
+ @plugin_executor.handle_durable_output
223
196
  def wrapper(event: Any, context: LambdaContext) -> MutableMapping[str, Any]:
224
197
  invocation_input: DurableExecutionInvocationInput
225
198
  service_client: DurableServiceClient
@@ -255,6 +228,7 @@ def durable_execution(
255
228
  operations={},
256
229
  service_client=service_client,
257
230
  replay_status=ReplayStatus.NEW,
231
+ plugin_executor=plugin_executor,
258
232
  )
259
233
 
260
234
  try:
@@ -306,6 +280,19 @@ def durable_execution(
306
280
  ) as executor,
307
281
  contextlib.closing(execution_state) as execution_state,
308
282
  ):
283
+ execution_operation = execution_state.get_execution_operation()
284
+
285
+ # execute the plugins
286
+ plugin_executor.on_invocation_start(
287
+ execution_arn=invocation_input.durable_execution_arn,
288
+ lambda_context=context,
289
+ execution_start_time=(
290
+ execution_operation.start_timestamp
291
+ if execution_operation is not None
292
+ else None
293
+ ),
294
+ is_first_invocation=not execution_state.is_replaying(),
295
+ )
309
296
  # Thread 1: Run background checkpoint processing
310
297
  executor.submit(execution_state.checkpoint_batches_forever)
311
298
 
@@ -4,11 +4,21 @@ from __future__ import annotations
4
4
 
5
5
  from dataclasses import dataclass
6
6
 
7
+ from aws_durable_execution_sdk_python.lambda_service import (
8
+ OperationType,
9
+ OperationSubType,
10
+ )
11
+
7
12
 
8
13
  @dataclass(frozen=True)
9
14
  class OperationIdentifier:
10
15
  """Container for operation id, parent id, and name."""
11
16
 
12
17
  operation_id: str
18
+ sub_type: OperationSubType
13
19
  parent_id: str | None = None
14
20
  name: str | None = None
21
+
22
+ @property
23
+ def type(self) -> OperationType:
24
+ return OperationType.from_sub_type(self.sub_type)