aws-durable-execution-sdk-python 1.4.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 (111) hide show
  1. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/.gitignore +7 -1
  2. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/PKG-INFO +7 -32
  3. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/README.md +5 -28
  4. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/pyproject.toml +9 -12
  5. {aws_durable_execution_sdk_python-1.4.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.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/__init__.py +8 -0
  7. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/concurrency/executor.py +91 -28
  8. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/config.py +120 -14
  9. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/context.py +141 -15
  10. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/exceptions.py +100 -42
  11. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/execution.py +102 -109
  12. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/identifier.py +10 -0
  13. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/lambda_service.py +107 -1
  14. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/child.py +51 -31
  15. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/map.py +15 -2
  16. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/parallel.py +25 -4
  17. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/step.py +13 -3
  18. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/wait_for_condition.py +9 -1
  19. aws_durable_execution_sdk_python-1.6.0/src/aws_durable_execution_sdk_python/plugin.py +407 -0
  20. aws_durable_execution_sdk_python-1.6.0/src/aws_durable_execution_sdk_python/retries.py +377 -0
  21. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/state.py +154 -25
  22. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/types.py +3 -1
  23. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/concurrency_test.py +389 -36
  24. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/config_test.py +0 -11
  25. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/context_test.py +389 -28
  26. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/e2e/checkpoint_response_int_test.py +11 -11
  27. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/e2e/execution_int_test.py +7 -7
  28. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/e2e/map_with_concurrent_waits_int_test.py +2 -0
  29. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/exceptions_test.py +145 -34
  30. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/execution_test.py +577 -150
  31. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/lambda_service_test.py +163 -14
  32. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/logger_test.py +8 -2
  33. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/callback_test.py +92 -34
  34. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/child_test.py +349 -27
  35. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/invoke_test.py +101 -35
  36. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/map_test.py +380 -172
  37. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/parallel_test.py +365 -174
  38. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/step_test.py +112 -24
  39. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/wait_for_condition_test.py +140 -31
  40. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/wait_test.py +29 -11
  41. aws_durable_execution_sdk_python-1.6.0/tests/plugin_test.py +787 -0
  42. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/retries_test.py +147 -0
  43. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/state_test.py +788 -6
  44. aws_durable_execution_sdk_python-1.6.0/tests/with_retry_test.py +372 -0
  45. aws_durable_execution_sdk_python-1.4.0/.github/ISSUE_TEMPLATE/bug_report.yml +0 -91
  46. aws_durable_execution_sdk_python-1.4.0/.github/ISSUE_TEMPLATE/config.yml +0 -5
  47. aws_durable_execution_sdk_python-1.4.0/.github/ISSUE_TEMPLATE/documentation.yml +0 -36
  48. aws_durable_execution_sdk_python-1.4.0/.github/ISSUE_TEMPLATE/feature_request.yml +0 -57
  49. aws_durable_execution_sdk_python-1.4.0/.github/dependabot.yml +0 -14
  50. aws_durable_execution_sdk_python-1.4.0/.github/workflows/ci.yml +0 -53
  51. aws_durable_execution_sdk_python-1.4.0/.github/workflows/integration-tests.yml +0 -182
  52. aws_durable_execution_sdk_python-1.4.0/.github/workflows/lintcommit.js +0 -178
  53. aws_durable_execution_sdk_python-1.4.0/.github/workflows/notify_slack.yml +0 -39
  54. aws_durable_execution_sdk_python-1.4.0/.github/workflows/pypi-publish.yml +0 -71
  55. aws_durable_execution_sdk_python-1.4.0/.github/workflows/scorecard.yml +0 -80
  56. aws_durable_execution_sdk_python-1.4.0/.github/workflows/sync-package.yml +0 -72
  57. aws_durable_execution_sdk_python-1.4.0/.github/workflows/test-parser.yml +0 -24
  58. aws_durable_execution_sdk_python-1.4.0/AGENTS.md +0 -934
  59. aws_durable_execution_sdk_python-1.4.0/CODE_OF_CONDUCT.md +0 -4
  60. aws_durable_execution_sdk_python-1.4.0/CONTRIBUTING.md +0 -324
  61. aws_durable_execution_sdk_python-1.4.0/docs/advanced/error-handling.md +0 -955
  62. aws_durable_execution_sdk_python-1.4.0/docs/advanced/serialization.md +0 -771
  63. aws_durable_execution_sdk_python-1.4.0/docs/advanced/testing-modes.md +0 -495
  64. aws_durable_execution_sdk_python-1.4.0/docs/api-reference/.gitkeep +0 -1
  65. aws_durable_execution_sdk_python-1.4.0/docs/architecture.md +0 -505
  66. aws_durable_execution_sdk_python-1.4.0/docs/best-practices.md +0 -850
  67. aws_durable_execution_sdk_python-1.4.0/docs/core/callbacks.md +0 -877
  68. aws_durable_execution_sdk_python-1.4.0/docs/core/child-contexts.md +0 -703
  69. aws_durable_execution_sdk_python-1.4.0/docs/core/invoke.md +0 -774
  70. aws_durable_execution_sdk_python-1.4.0/docs/core/logger.md +0 -737
  71. aws_durable_execution_sdk_python-1.4.0/docs/core/map.md +0 -589
  72. aws_durable_execution_sdk_python-1.4.0/docs/core/parallel.md +0 -911
  73. aws_durable_execution_sdk_python-1.4.0/docs/core/steps.md +0 -597
  74. aws_durable_execution_sdk_python-1.4.0/docs/core/wait.md +0 -445
  75. aws_durable_execution_sdk_python-1.4.0/docs/getting-started.md +0 -293
  76. aws_durable_execution_sdk_python-1.4.0/docs/index.md +0 -205
  77. aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/.gitkeep +0 -1
  78. aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/basic-tests.md +0 -701
  79. aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/complex-workflows.md +0 -675
  80. aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/stores.md +0 -262
  81. aws_durable_execution_sdk_python-1.4.0/ops/__tests__/test_parse_sdk_branch.py +0 -84
  82. aws_durable_execution_sdk_python-1.4.0/ops/ci-checks.sh +0 -15
  83. aws_durable_execution_sdk_python-1.4.0/ops/parse_sdk_branch.py +0 -31
  84. aws_durable_execution_sdk_python-1.4.0/src/aws_durable_execution_sdk_python/retries.py +0 -174
  85. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/LICENSE +0 -0
  86. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/NOTICE +0 -0
  87. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/.gitignore +0 -0
  88. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/concurrency/__init__.py +0 -0
  89. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/concurrency/models.py +0 -0
  90. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/logger.py +0 -0
  91. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/__init__.py +0 -0
  92. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/base.py +0 -0
  93. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/callback.py +0 -0
  94. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/invoke.py +0 -0
  95. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/operation/wait.py +0 -0
  96. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/py.typed +0 -0
  97. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/serdes.py +0 -0
  98. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/suspend.py +0 -0
  99. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/threading.py +0 -0
  100. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/src/aws_durable_execution_sdk_python/waits.py +0 -0
  101. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/__init__.py +0 -0
  102. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/durable_executions_python_language_sdk_test.py +0 -0
  103. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/e2e/__init__.py +0 -0
  104. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/__init__.py +0 -0
  105. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/operation/base_test.py +0 -0
  106. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/serdes_test.py +0 -0
  107. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/suspend_test.py +0 -0
  108. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/test_helpers.py +0 -0
  109. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/threading_test.py +0 -0
  110. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.6.0}/tests/types_test.py +0 -0
  111. {aws_durable_execution_sdk_python-1.4.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
@@ -28,4 +29,9 @@ dist/
28
29
 
29
30
  .idea
30
31
 
31
- .kiro/
32
+ .kiro/
33
+
34
+ **/build/
35
+ **/*.zip
36
+
37
+ .env
@@ -1,14 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-durable-execution-sdk-python
3
- Version: 1.4.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
- Author-email: yaythomas <tgaigher@amazon.com>
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
 
@@ -91,33 +89,10 @@ def handler(event: dict, context: DurableContext) -> dict:
91
89
 
92
90
  ## 📚 Documentation
93
91
 
94
- - **[AWS Documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html)** - Official AWS Lambda durable functions guide
95
- - **[Documentation index](docs/index.md)** - SDK Overview and navigation
92
+ The complete documentation for the AWS Durable Execution SDK for Python lives on the AWS Documentation site:
96
93
 
97
- **New to durable functions?**
98
- - [Getting started guide](docs/getting-started.md) - Build your first durable function
99
-
100
- **Core operations:**
101
- - [Steps](docs/core/steps.md) - Execute code with automatic checkpointing and retry support
102
- - [Wait operations](docs/core/wait.md) - Pause execution without blocking Lambda resources
103
- - [Callbacks](docs/core/callbacks.md) - Wait for external systems to respond
104
- - [Invoke operations](docs/core/invoke.md) - Call other durable functions and compose workflows
105
- - [Child contexts](docs/core/child-contexts.md) - Organize complex workflows into isolated units
106
- - [Parallel operations](docs/core/parallel.md) - Run multiple operations concurrently
107
- - [Map operations](docs/core/map.md) - Process collections in parallel with batching
108
- - [Logger integration](docs/core/logger.md) - Add structured logging to track execution
109
-
110
- **Advanced topics:**
111
- - [Error handling](docs/advanced/error-handling.md) - Handle failures and implement retry strategies
112
- - [Testing modes](docs/advanced/testing-modes.md) - Run tests locally or against deployed Lambda functions
113
- - [Testing patterns](docs/testing-patterns/basic-tests.md) - Practical testing examples
114
- - [Serialization](docs/advanced/serialization.md) - Customize how data is serialized in checkpoints
115
-
116
- **Architecture:**
117
- - [Architecture diagrams](docs/architecture.md) - Class diagrams and concurrency flows
118
-
119
- **API reference:**
120
- - API reference docs are in progress. Use the core operation docs above for now.
94
+ - **[AWS Durable Execution Documentation](https://docs.aws.amazon.com/durable-execution/)** - Concepts, getting started, core operations, advanced topics, and API reference
95
+ - **[AWS Lambda Durable Functions Guide](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html)** - How durable functions work on Lambda
121
96
 
122
97
  ## 💬 Feedback & Support
123
98
 
@@ -128,4 +103,4 @@ def handler(event: dict, context: DurableContext) -> dict:
128
103
 
129
104
  ## 📄 License
130
105
 
131
- 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
 
@@ -68,33 +68,10 @@ def handler(event: dict, context: DurableContext) -> dict:
68
68
 
69
69
  ## 📚 Documentation
70
70
 
71
- - **[AWS Documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html)** - Official AWS Lambda durable functions guide
72
- - **[Documentation index](docs/index.md)** - SDK Overview and navigation
71
+ The complete documentation for the AWS Durable Execution SDK for Python lives on the AWS Documentation site:
73
72
 
74
- **New to durable functions?**
75
- - [Getting started guide](docs/getting-started.md) - Build your first durable function
76
-
77
- **Core operations:**
78
- - [Steps](docs/core/steps.md) - Execute code with automatic checkpointing and retry support
79
- - [Wait operations](docs/core/wait.md) - Pause execution without blocking Lambda resources
80
- - [Callbacks](docs/core/callbacks.md) - Wait for external systems to respond
81
- - [Invoke operations](docs/core/invoke.md) - Call other durable functions and compose workflows
82
- - [Child contexts](docs/core/child-contexts.md) - Organize complex workflows into isolated units
83
- - [Parallel operations](docs/core/parallel.md) - Run multiple operations concurrently
84
- - [Map operations](docs/core/map.md) - Process collections in parallel with batching
85
- - [Logger integration](docs/core/logger.md) - Add structured logging to track execution
86
-
87
- **Advanced topics:**
88
- - [Error handling](docs/advanced/error-handling.md) - Handle failures and implement retry strategies
89
- - [Testing modes](docs/advanced/testing-modes.md) - Run tests locally or against deployed Lambda functions
90
- - [Testing patterns](docs/testing-patterns/basic-tests.md) - Practical testing examples
91
- - [Serialization](docs/advanced/serialization.md) - Customize how data is serialized in checkpoints
92
-
93
- **Architecture:**
94
- - [Architecture diagrams](docs/architecture.md) - Class diagrams and concurrency flows
95
-
96
- **API reference:**
97
- - API reference docs are in progress. Use the core operation docs above for now.
73
+ - **[AWS Durable Execution Documentation](https://docs.aws.amazon.com/durable-execution/)** - Concepts, getting started, core operations, advanced topics, and API reference
74
+ - **[AWS Lambda Durable Functions Guide](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html)** - How durable functions work on Lambda
98
75
 
99
76
  ## 💬 Feedback & Support
100
77
 
@@ -105,4 +82,4 @@ def handler(event: dict, context: DurableContext) -> dict:
105
82
 
106
83
  ## 📄 License
107
84
 
108
- 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.
@@ -10,7 +10,7 @@ readme = "README.md"
10
10
  requires-python = ">=3.11"
11
11
  license = "Apache-2.0"
12
12
  keywords = []
13
- authors = [{ name = "yaythomas", email = "tgaigher@amazon.com" }]
13
+ authors = [{ name = "AWS durable-execution-dev", email = "durable-execution-dev@amazon.com" }]
14
14
  classifiers = [
15
15
  "Development Status :: 4 - Beta",
16
16
  "Programming Language :: Python",
@@ -28,23 +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 = ["coverage[toml]", "pytest", "pytest-cov"]
39
-
40
- [tool.hatch.envs.test.scripts]
41
- cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=src/aws_durable_execution_sdk_python --cov-fail-under=98"
42
-
43
- [tool.hatch.envs.types]
44
- extra-dependencies = ["mypy>=1.0.0", "pytest", "boto3-stubs[lambda]"]
45
- [tool.hatch.envs.types.scripts]
46
- check = "mypy --install-types --non-interactive {args:src/aws_durable_execution_sdk_python tests}"
47
-
48
45
  [tool.coverage.run]
49
46
  source_pkgs = ["aws_durable_execution_sdk_python"]
50
47
  branch = true
@@ -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.4.0"
4
+ __version__ = "1.6.0"
@@ -7,8 +7,10 @@ from aws_durable_execution_sdk_python.__about__ import __version__
7
7
  # Helper decorators - commonly used for step functions
8
8
  # Concurrency
9
9
  from aws_durable_execution_sdk_python.concurrency.models import BatchResult
10
+ from aws_durable_execution_sdk_python.config import ParallelBranch
10
11
  from aws_durable_execution_sdk_python.context import (
11
12
  DurableContext,
13
+ durable_parallel_branch,
12
14
  durable_step,
13
15
  durable_wait_for_callback,
14
16
  durable_with_child_context,
@@ -23,20 +25,26 @@ from aws_durable_execution_sdk_python.exceptions import (
23
25
 
24
26
  # Core decorator - used in every durable function
25
27
  from aws_durable_execution_sdk_python.execution import durable_execution
28
+ from aws_durable_execution_sdk_python.retries import WithRetryConfig, with_retry
26
29
 
27
30
  # Essential context types - passed to user functions
28
31
  from aws_durable_execution_sdk_python.types import StepContext
29
32
 
33
+
30
34
  __all__ = [
31
35
  "BatchResult",
32
36
  "DurableContext",
33
37
  "DurableExecutionsError",
34
38
  "InvocationError",
39
+ "ParallelBranch",
35
40
  "StepContext",
36
41
  "ValidationError",
42
+ "WithRetryConfig",
37
43
  "__version__",
38
44
  "durable_execution",
45
+ "durable_parallel_branch",
39
46
  "durable_step",
40
47
  "durable_wait_for_callback",
41
48
  "durable_with_child_context",
49
+ "with_retry",
42
50
  ]
@@ -20,14 +20,17 @@ from aws_durable_execution_sdk_python.concurrency.models import (
20
20
  ExecutionCounters,
21
21
  SuspendResult,
22
22
  )
23
- from aws_durable_execution_sdk_python.config import ChildConfig
23
+ from aws_durable_execution_sdk_python.config import (
24
+ ChildConfig,
25
+ NestingType,
26
+ )
24
27
  from aws_durable_execution_sdk_python.exceptions import (
25
28
  OrphanedChildException,
26
29
  SuspendExecution,
27
30
  TimedSuspendExecution,
28
31
  )
29
32
  from aws_durable_execution_sdk_python.identifier import OperationIdentifier
30
- from aws_durable_execution_sdk_python.lambda_service import ErrorObject
33
+ from aws_durable_execution_sdk_python.lambda_service import ErrorObject, OperationType
31
34
  from aws_durable_execution_sdk_python.operation.child import child_handler
32
35
 
33
36
 
@@ -56,7 +59,7 @@ class TimerScheduler:
56
59
  """Manage timed suspend tasks with a background timer thread."""
57
60
 
58
61
  def __init__(
59
- self, resubmit_callback: Callable[[ExecutableWithState], None]
62
+ self, resubmit_callback: Callable[[list[ExecutableWithState]], None]
60
63
  ) -> None:
61
64
  self.resubmit_callback = resubmit_callback
62
65
  self._pending_resumes: list[tuple[float, int, ExecutableWithState]] = []
@@ -111,18 +114,31 @@ class TimerScheduler:
111
114
 
112
115
  current_time = time.time()
113
116
  if current_time >= next_resume_time:
114
- # 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] = []
115
124
  with self._lock:
116
- # no branch cover because hard to test reliably - this is a double-safety check if heap mutated
117
- # since the first peek on next_resume_time further up
118
- if ( # pragma: no branch
125
+ while (
119
126
  self._pending_resumes
120
127
  and self._pending_resumes[0][0] <= current_time
121
128
  ):
122
129
  _, _, exe_state = heapq.heappop(self._pending_resumes)
123
130
  if exe_state.can_resume:
124
131
  exe_state.reset_to_pending()
125
- 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)
126
142
  else:
127
143
  # Wait until next resume time
128
144
  wait_time = min(next_resume_time - current_time, 0.1)
@@ -143,6 +159,7 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
143
159
  serdes: SerDes | None,
144
160
  item_serdes: SerDes | None = None,
145
161
  summary_generator: SummaryGenerator | None = None,
162
+ nesting_type: NestingType = NestingType.NESTED,
146
163
  ):
147
164
  """Initialize ConcurrentExecutor.
148
165
 
@@ -160,10 +177,12 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
160
177
  self.sub_type_iteration = sub_type_iteration
161
178
  self.name_prefix = name_prefix
162
179
  self.summary_generator = summary_generator
180
+ self.nesting_type = nesting_type
163
181
 
164
182
  # Event-driven state tracking for when the executor is done
165
183
  self._completion_event = threading.Event()
166
184
  self._suspend_exception: SuspendExecution | None = None
185
+ self._resume_error: Exception | None = None
167
186
 
168
187
  # ExecutionCounters will keep track of completion criteria and on-going counters
169
188
  min_successful = self.completion_config.min_successful or len(self.executables)
@@ -189,6 +208,14 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
189
208
  """Execute a single executable in a child context and return the result."""
190
209
  raise NotImplementedError
191
210
 
211
+ def get_iteration_name(self, index: int) -> str:
212
+ """Get the display name for an iteration/branch at the given index.
213
+
214
+ Subclasses can override this to provide custom naming (e.g., from item_namer
215
+ or branch names). The default returns "{name_prefix}{index}".
216
+ """
217
+ return f"{self.name_prefix}{index}"
218
+
192
219
  def execute(
193
220
  self, execution_state: ExecutionState, executor_context: DurableContext
194
221
  ) -> BatchResult[ResultType]:
@@ -209,11 +236,32 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
209
236
  ]
210
237
  self._completion_event.clear()
211
238
  self._suspend_exception = None
212
-
213
- def resubmitter(executable_with_state: ExecutableWithState) -> None:
214
- """Resubmit a timed suspended task."""
215
- execution_state.create_checkpoint()
216
- 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)
217
265
 
218
266
  thread_executor = ThreadPoolExecutor(max_workers=max_workers)
219
267
  try:
@@ -246,6 +294,12 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
246
294
  for future in futures:
247
295
  future.cancel()
248
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
+
249
303
  # Suspend execution if everything done and at least one of the tasks raised a suspend exception.
250
304
  if self._suspend_exception:
251
305
  raise self._suspend_exception
@@ -391,29 +445,37 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
391
445
  """
392
446
  Execute a single item in a derived child context.
393
447
 
394
- instead of relying on `executor_context.run_in_child_context`
395
- we generate an operation_id for the child, and then call `child_handler`
396
- directly. This avoids the hidden mutation of the context's internal counter.
397
- we can do this because we explicitly control the generation of step_id and do it
398
- using executable.index.
399
-
448
+ Instead of relying on `executor_context.run_in_child_context` we
449
+ generate an operation_id for the child, then call `child_handler`
450
+ directly. This avoids the hidden mutation of the context's
451
+ internal counter. We explicitly derive the child's operation_id
452
+ from `executable.index` so that the same input always produces
453
+ the same id regardless of the order branches actually run in.
400
454
 
401
- invariant: `operation_id` for a given executable is deterministic,
402
- and execution order invariant.
455
+ Invariant: `operation_id` for a given executable is deterministic
456
+ and execution-order invariant.
403
457
  """
404
458
 
405
- operation_id = executor_context._create_step_id_for_logical_step( # noqa: SLF001
459
+ operation_id: str = executor_context._create_step_id_for_logical_step( # noqa: SLF001
406
460
  executable.index
407
461
  )
408
- name = f"{self.name_prefix}{executable.index}"
409
- child_context = executor_context.create_child_context(operation_id)
462
+ name: str = self.get_iteration_name(executable.index)
463
+ is_virtual: bool = self.nesting_type is NestingType.FLAT
464
+
465
+ child_context: DurableContext = executor_context.create_child_context(
466
+ operation_id, is_virtual=is_virtual
467
+ )
468
+ # For NESTED this is for branch's START/SUCCEED/FAIL checkpoints (not the children of the branch).
469
+ # For FLAT `child_handler` skips checkpoints, so not used.
470
+ # Construct it unconditionally to keep the call simple.
410
471
  operation_identifier = OperationIdentifier(
411
- operation_id,
412
- executor_context._parent_id, # noqa: SLF001
413
- 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,
414
476
  )
415
477
 
416
- def run_in_child_handler():
478
+ def run_in_child_handler() -> ResultType:
417
479
  return self.execute_item(child_context, executable)
418
480
 
419
481
  result: ResultType = child_handler(
@@ -424,6 +486,7 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
424
486
  serdes=self.item_serdes or self.serdes,
425
487
  sub_type=self.sub_type_iteration,
426
488
  summary_generator=self.summary_generator,
489
+ is_virtual=is_virtual,
427
490
  ),
428
491
  )
429
492
  child_context.state.track_replay(operation_id=operation_id)
@@ -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
@@ -9,6 +10,7 @@ from typing import TYPE_CHECKING, Generic, TypeVar
9
10
 
10
11
  from aws_durable_execution_sdk_python.exceptions import ValidationError
11
12
 
13
+
12
14
  P = TypeVar("P") # Payload type
13
15
  R = TypeVar("R") # Result type
14
16
  T = TypeVar("T")
@@ -76,6 +78,42 @@ class TerminationMode(Enum):
76
78
  ABANDON = "ABANDON"
77
79
 
78
80
 
81
+ class NestingType(Enum):
82
+ """Control how child contexts are created for batch operations.
83
+
84
+ Applies to `map` and `parallel`. Each branch or iteration runs inside a
85
+ child context.
86
+
87
+ - NESTED: full checkpointed context
88
+ - FLAT: a virtual context that skips checkpoints for the branch/iteration.
89
+
90
+ """
91
+
92
+ NESTED = "NESTED"
93
+ """Create CONTEXT operations for each branch/iteration with full checkpointing.
94
+
95
+ Operations within each branch/iteration are wrapped in their own context.
96
+
97
+ - Observability: high — each branch/iteration appears as a separate
98
+ operation in execution history.
99
+ - Cost: higher — consumes more operations due to CONTEXT creation
100
+ overhead.
101
+ - Scale: lower maximum iterations due to operation limits.
102
+ """
103
+
104
+ FLAT = "FLAT"
105
+ """Skip CONTEXT operations for branches/iterations using virtual contexts.
106
+
107
+ Operations execute directly without individual context wrapping.
108
+
109
+ - Observability: lower — branches/iterations don't appear as separate
110
+ operations in execution history.
111
+ - Cost: ~30% lower — reduces operation consumption by skipping CONTEXT
112
+ overhead.
113
+ - Scale: higher maximum iterations possible within operation limits.
114
+ """
115
+
116
+
79
117
  @dataclass(frozen=True)
80
118
  class CompletionConfig:
81
119
  """Configuration for determining when parallel/map operations complete.
@@ -187,6 +225,10 @@ class ParallelConfig:
187
225
  Used internally by map/parallel operations to handle large BatchResult payloads.
188
226
  Signature: (result: T) -> str
189
227
 
228
+ nesting_type: How child operations should inherit context from their parent.
229
+ - NESTED: Each branch runs in its own isolated context (default)
230
+ - FLAT: All branches share the same parent context
231
+
190
232
  Example:
191
233
  # Run at most 3 branches concurrently, succeed if any one succeeds
192
234
  config = ParallelConfig(
@@ -202,6 +244,42 @@ class ParallelConfig:
202
244
  serdes: SerDes | None = None
203
245
  item_serdes: SerDes | None = None
204
246
  summary_generator: SummaryGenerator | None = None
247
+ nesting_type: NestingType = NestingType.NESTED
248
+
249
+
250
+ @dataclass(frozen=True)
251
+ class ParallelBranch(Generic[T]):
252
+ """A named branch for parallel execution.
253
+
254
+ Use this to provide custom names for parallel branches, improving
255
+ observability in execution history.
256
+
257
+ Type Parameters:
258
+ T: The return type of the branch function.
259
+
260
+ Args:
261
+ func: The callable to execute in this branch. Receives a DurableContext.
262
+ name: Optional custom name for this branch. When provided, replaces
263
+ the default "parallel-branch-{index}" naming in execution history.
264
+ This affects observability but not replay determinism.
265
+
266
+ Example:
267
+ context.parallel(
268
+ functions=[
269
+ ParallelBranch(func=lambda ctx: fetch_user(ctx), name="fetch-user-data"),
270
+ ParallelBranch(func=lambda ctx: fetch_orders(ctx), name="fetch-order-history"),
271
+ ],
272
+ name="load-data",
273
+ config=ParallelConfig(max_concurrency=2),
274
+ )
275
+ """
276
+
277
+ func: Callable
278
+ name: str | None = None
279
+
280
+ def __call__(self, *args, **kwargs):
281
+ """Delegate to the wrapped function, making ParallelBranch itself callable."""
282
+ return self.func(*args, **kwargs)
205
283
 
206
284
 
207
285
  class StepSemantics(Enum):
@@ -218,12 +296,6 @@ class StepConfig:
218
296
  serdes: SerDes | None = None
219
297
 
220
298
 
221
- class CheckpointMode(Enum):
222
- NO_CHECKPOINT = ("NO_CHECKPOINT",)
223
- CHECKPOINT_AT_FINISH = ("CHECKPOINT_AT_FINISH",)
224
- CHECKPOINT_AT_START_AND_FINISH = "CHECKPOINT_AT_START_AND_FINISH"
225
-
226
-
227
299
  @dataclass(frozen=True)
228
300
  class ChildConfig(Generic[T]):
229
301
  """Configuration options for child context operations.
@@ -259,21 +331,23 @@ class ChildConfig(Generic[T]):
259
331
 
260
332
  Used internally by map/parallel operations to handle large BatchResult payloads.
261
333
  Signature: (result: T) -> str
262
- Note:
263
- checkpoint_mode field is commented out as it's not currently implemented.
264
- When implemented, it will control when checkpoints are created:
265
- - CHECKPOINT_AT_START_AND_FINISH: Checkpoint at both start and completion (default)
266
- - CHECKPOINT_AT_FINISH: Only checkpoint when operation completes
267
- - NO_CHECKPOINT: No automatic checkpointing
334
+
335
+ is_virtual: When True, skip all checkpoints (START, SUCCEED,
336
+ FAIL) for this child context and propagate the caller's reporting
337
+ parent id through to operations created inside the child. The
338
+ branch is a logical scope for step-id prefixing but does not
339
+ appear in the execution history. Used internally by
340
+ NestingType.FLAT branches. Use this to group operations without
341
+ adding a CONTEXT entry to the execution history.
268
342
 
269
343
  See TypeScript reference: aws-durable-execution-sdk-js/src/types/index.ts
270
344
  """
271
345
 
272
- # checkpoint_mode: CheckpointMode = CheckpointMode.CHECKPOINT_AT_START_AND_FINISH
273
346
  serdes: SerDes | None = None
274
347
  item_serdes: SerDes | None = None
275
348
  sub_type: OperationSubType | None = None
276
349
  summary_generator: SummaryGenerator | None = None
350
+ is_virtual: bool = False
277
351
 
278
352
 
279
353
  class ItemsPerBatchUnit(Enum):
@@ -317,12 +391,15 @@ class ItemBatcher(Generic[T]):
317
391
 
318
392
 
319
393
  @dataclass(frozen=True)
320
- class MapConfig:
394
+ class MapConfig(Generic[T]):
321
395
  """Configuration options for map operations over collections.
322
396
 
323
397
  This class configures how map operations process collections of items,
324
398
  including concurrency, batching, completion criteria, and serialization.
325
399
 
400
+ Type Parameters:
401
+ T: The type of items being processed in the map operation.
402
+
326
403
  Args:
327
404
  max_concurrency: Maximum number of items to process concurrently.
328
405
  If None, no limit is imposed and all items are processed concurrently.
@@ -361,6 +438,16 @@ class MapConfig:
361
438
  Used internally by map/parallel operations to handle large BatchResult payloads.
362
439
  Signature: (result: T) -> str
363
440
 
441
+ nesting_type: How child operations should inherit context from their parent.
442
+ - NESTED: Each item runs in its own isolated context (default)
443
+ - FLAT: All items share the same parent context
444
+
445
+ item_namer: Optional callable to generate custom names for each map iteration.
446
+ When provided, replaces the default "map-item-{index}" naming scheme.
447
+ Receives the item and its index, and returns a string name for that iteration.
448
+ This affects observability (execution history names) but not replay determinism.
449
+ If None, uses the default naming: "map-item-{index}".
450
+
364
451
  Example:
365
452
  # Process 5 items at a time, batch by count, require all to succeed
366
453
  config = MapConfig(
@@ -368,6 +455,12 @@ class MapConfig:
368
455
  item_batcher=ItemBatcher(max_items_per_batch=10),
369
456
  completion_config=CompletionConfig.all_successful()
370
457
  )
458
+
459
+ # With custom iteration names
460
+ config = MapConfig(
461
+ max_concurrency=5,
462
+ item_namer=lambda item, index: f"process-order-{item.id}"
463
+ )
371
464
  """
372
465
 
373
466
  max_concurrency: int | None = None
@@ -376,6 +469,8 @@ class MapConfig:
376
469
  serdes: SerDes | None = None
377
470
  item_serdes: SerDes | None = None
378
471
  summary_generator: SummaryGenerator | None = None
472
+ nesting_type: NestingType = NestingType.NESTED
473
+ item_namer: Callable[[T, int], str] | None = None
379
474
 
380
475
 
381
476
  @dataclass(frozen=True)
@@ -495,5 +590,16 @@ class JitterStrategy(StrEnum):
495
590
  # Full jitter: random(0, delay)
496
591
  return random.random() * delay # noqa: S311
497
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
+
498
604
 
499
605
  # endregion Jitter