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.
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/.gitignore +3 -2
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/PKG-INFO +3 -5
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/README.md +2 -2
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/pyproject.toml +8 -59
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- aws_durable_execution_sdk_python-1.6.0/src/aws_durable_execution_sdk_python/plugin.py +407 -0
- aws_durable_execution_sdk_python-1.6.0/src/aws_durable_execution_sdk_python/retries.py +377 -0
- {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
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/concurrency_test.py +108 -12
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/context_test.py +72 -27
- {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
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/e2e/execution_int_test.py +7 -7
- {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
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/execution_test.py +349 -55
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/lambda_service_test.py +86 -14
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/logger_test.py +8 -2
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/callback_test.py +92 -34
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/child_test.py +109 -24
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/invoke_test.py +101 -35
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/map_test.py +218 -162
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/parallel_test.py +216 -161
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/step_test.py +58 -25
- {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
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/wait_test.py +29 -11
- aws_durable_execution_sdk_python-1.6.0/tests/plugin_test.py +787 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/retries_test.py +147 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/state_test.py +603 -7
- aws_durable_execution_sdk_python-1.6.0/tests/with_retry_test.py +372 -0
- aws_durable_execution_sdk_python-1.5.0/.github/ISSUE_TEMPLATE/bug_report.yml +0 -91
- aws_durable_execution_sdk_python-1.5.0/.github/ISSUE_TEMPLATE/config.yml +0 -5
- aws_durable_execution_sdk_python-1.5.0/.github/ISSUE_TEMPLATE/documentation.yml +0 -36
- aws_durable_execution_sdk_python-1.5.0/.github/ISSUE_TEMPLATE/feature_request.yml +0 -57
- aws_durable_execution_sdk_python-1.5.0/.github/dependabot.yml +0 -14
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/__init__.py +0 -1
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/ci-checks.sh +0 -18
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/lintcommit.py +0 -265
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/parse_sdk_branch.py +0 -31
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests/__init__.py +0 -1
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests/test_lintcommit.py +0 -261
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests/test_parse_sdk_branch.py +0 -84
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/ci.yml +0 -52
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/deploy-examples.yml +0 -134
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/integration-tests.yml +0 -162
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-issues.yml +0 -23
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-pr.yml +0 -24
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-release.yml +0 -29
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/pypi-publish.yml +0 -79
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/scorecard.yml +0 -80
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/sync-package.yml +0 -72
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/test-parser.yml +0 -24
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/update-sam-template.yml +0 -42
- aws_durable_execution_sdk_python-1.5.0/AGENTS.md +0 -929
- aws_durable_execution_sdk_python-1.5.0/CODE_OF_CONDUCT.md +0 -4
- aws_durable_execution_sdk_python-1.5.0/CONTRIBUTING.md +0 -388
- aws_durable_execution_sdk_python-1.5.0/examples/cli.py +0 -487
- aws_durable_execution_sdk_python-1.5.0/examples/examples-catalog.json +0 -607
- aws_durable_execution_sdk_python-1.5.0/examples/scripts/generate_sam_template.py +0 -106
- aws_durable_execution_sdk_python-1.5.0/examples/src/__init__.py +0 -3
- aws_durable_execution_sdk_python-1.5.0/examples/src/block_example/block_example.py +0 -47
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_concurrency.py +0 -51
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_heartbeat.py +0 -22
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_mixed_ops.py +0 -35
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_serdes.py +0 -76
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_simple.py +0 -22
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_with_timeout.py +0 -23
- aws_durable_execution_sdk_python-1.5.0/examples/src/comprehensive_operations/comprehensive_operations.py +0 -51
- aws_durable_execution_sdk_python-1.5.0/examples/src/handler_error/handler_error.py +0 -13
- aws_durable_execution_sdk_python-1.5.0/examples/src/hello_world.py +0 -62
- aws_durable_execution_sdk_python-1.5.0/examples/src/logger_example/logger_example.py +0 -64
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_completion.py +0 -117
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_operations.py +0 -23
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_operations_flat.py +0 -23
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_batch_serdes.py +0 -96
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_custom_serdes.py +0 -63
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_failure_tolerance.py +0 -53
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_item_namer.py +0 -30
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_large_scale.py +0 -64
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_max_concurrency.py +0 -23
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_min_successful.py +0 -43
- aws_durable_execution_sdk_python-1.5.0/examples/src/no_replay_execution/no_replay_execution.py +0 -15
- aws_durable_execution_sdk_python-1.5.0/examples/src/none_results/none_results.py +0 -31
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel.py +0 -27
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_first_successful.py +0 -27
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_flat.py +0 -27
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_batch_serdes.py +0 -97
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_custom_serdes.py +0 -60
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_failure_tolerance.py +0 -59
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_max_concurrency.py +0 -25
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_named_branches.py +0 -51
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_wait.py +0 -24
- aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context.py +0 -22
- aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context_large_data.py +0 -73
- aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context_step_failure.py +0 -50
- aws_durable_execution_sdk_python-1.5.0/examples/src/simple_execution/simple_execution.py +0 -18
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step.py +0 -19
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_no_name.py +0 -11
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_semantics_at_most_once.py +0 -18
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_exponential_backoff.py +0 -27
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_name.py +0 -11
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_retry.py +0 -46
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/steps_with_retry.py +0 -81
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait/multiple_wait.py +0 -19
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait/wait.py +0 -11
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait/wait_with_name.py +0 -12
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback.py +0 -27
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_anonymous.py +0 -20
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_child.py +0 -42
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_heartbeat.py +0 -33
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_mixed_ops.py +0 -47
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_multiple_invocations.py +0 -53
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_nested.py +0 -66
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_serdes.py +0 -90
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_submitter_failure.py +0 -39
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_submitter_failure_catchable.py +0 -52
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_timeout.py +0 -35
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_condition/wait_for_condition.py +0 -32
- aws_durable_execution_sdk_python-1.5.0/examples/template.yaml +0 -982
- aws_durable_execution_sdk_python-1.5.0/examples/test/README.md +0 -119
- aws_durable_execution_sdk_python-1.5.0/examples/test/__init__.py +0 -1
- aws_durable_execution_sdk_python-1.5.0/examples/test/block_example/test_block_example.py +0 -104
- aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_concurrency.py +0 -83
- aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_heartbeat.py +0 -51
- aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_mixed_ops.py +0 -49
- aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_serdes.py +0 -60
- aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_simple.py +0 -47
- aws_durable_execution_sdk_python-1.5.0/examples/test/comprehensive_operations/test_comprehensive_operations.py +0 -94
- aws_durable_execution_sdk_python-1.5.0/examples/test/conftest.py +0 -268
- aws_durable_execution_sdk_python-1.5.0/examples/test/handler_error/test_handler_error.py +0 -32
- aws_durable_execution_sdk_python-1.5.0/examples/test/logger_example/test_logger_example.py +0 -35
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_completion.py +0 -32
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_operations.py +0 -42
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_operations_flat.py +0 -39
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_batch_serdes.py +0 -43
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_custom_serdes.py +0 -48
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_failure_tolerance.py +0 -52
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_item_namer.py +0 -39
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_large_scale.py +0 -36
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_max_concurrency.py +0 -37
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_min_successful.py +0 -70
- aws_durable_execution_sdk_python-1.5.0/examples/test/no_replay_execution/test_no_replay_execution.py +0 -52
- aws_durable_execution_sdk_python-1.5.0/examples/test/none_results/test_none_results.py +0 -51
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel.py +0 -42
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_flat.py +0 -42
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_batch_serdes.py +0 -43
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_custom_serdes.py +0 -46
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_failure_tolerance.py +0 -49
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_max_concurrency.py +0 -36
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_named_branches.py +0 -57
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_wait.py +0 -47
- aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context.py +0 -27
- aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context_large_data.py +0 -36
- aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context_step_failure.py +0 -23
- aws_durable_execution_sdk_python-1.5.0/examples/test/simple_execution/test_simple_execution.py +0 -40
- aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step.py +0 -24
- aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_permutations.py +0 -75
- aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_semantics_at_most_once.py +0 -32
- aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_with_retry.py +0 -40
- aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_steps_with_retry.py +0 -52
- aws_durable_execution_sdk_python-1.5.0/examples/test/test_hello_world.py +0 -24
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_multiple_wait.py +0 -57
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_wait.py +0 -27
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_wait_permutations.py +0 -25
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_anonymous.py +0 -39
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_child.py +0 -73
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_failure.py +0 -27
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_heartbeat.py +0 -62
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_mixed_ops.py +0 -52
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_multiple_invocations.py +0 -74
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_nested.py +0 -101
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_serdes.py +0 -66
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_submitter_failure.py +0 -32
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_submitter_failure_catchable.py +0 -28
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_success.py +0 -25
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_timeout.py +0 -32
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_condition/test_wait_for_condition.py +0 -24
- aws_durable_execution_sdk_python-1.5.0/src/aws_durable_execution_sdk_python/retries.py +0 -174
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/LICENSE +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/NOTICE +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/__init__.py +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/config_test.py +0 -0
- {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
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/e2e/__init__.py +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/exceptions_test.py +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/__init__.py +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/base_test.py +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/serdes_test.py +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/suspend_test.py +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/test_helpers.py +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/threading_test.py +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/types_test.py +0 -0
- {aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/tests/waits_test.py +0 -0
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aws-durable-execution-sdk-python
|
|
3
|
-
Version: 1.
|
|
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
|
[](https://pypi.org/project/aws-durable-execution-sdk-python)
|
|
28
26
|
[](https://pypi.org/project/aws-durable-execution-sdk-python)
|
|
29
27
|
[](https://scorecard.dev/viewer/?uri=github.com/aws/aws-durable-execution-sdk-python)
|
|
30
|
-
[](LICENSE)
|
|
28
|
+
[](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
|
[](https://pypi.org/project/aws-durable-execution-sdk-python)
|
|
5
5
|
[](https://pypi.org/project/aws-durable-execution-sdk-python)
|
|
6
6
|
[](https://scorecard.dev/viewer/?uri=github.com/aws/aws-durable-execution-sdk-python)
|
|
7
|
-
[](LICENSE)
|
|
7
|
+
[](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.
|
{aws_durable_execution_sdk_python-1.5.0 → aws_durable_execution_sdk_python-1.6.0}/pyproject.toml
RENAMED
|
@@ -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"
|
|
@@ -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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
433
|
-
|
|
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
|
|
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,
|
|
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(
|
|
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)
|