async-durable-execution 0.1.0__tar.gz → 2.0.0a1__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 (78) hide show
  1. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/.gitignore +10 -1
  2. async_durable_execution-2.0.0a1/NOTICE +8 -0
  3. async_durable_execution-2.0.0a1/PKG-INFO +146 -0
  4. async_durable_execution-2.0.0a1/README.md +125 -0
  5. async_durable_execution-0.1.0/src/async_durable_execution/__about__.py → async_durable_execution-2.0.0a1/VERSION.py +2 -1
  6. async_durable_execution-2.0.0a1/pyproject.toml +86 -0
  7. async_durable_execution-2.0.0a1/src/async_durable_execution/__about__.py +33 -0
  8. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/__init__.py +14 -6
  9. async_durable_execution-2.0.0a1/src/async_durable_execution/async_tools.py +49 -0
  10. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/concurrency/executor.py +55 -27
  11. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/concurrency/models.py +4 -4
  12. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/config.py +114 -20
  13. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/context.py +179 -49
  14. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/exceptions.py +100 -42
  15. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/execution.py +111 -117
  16. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/identifier.py +10 -0
  17. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/lambda_service.py +101 -2
  18. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/logger.py +3 -3
  19. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/operation/base.py +2 -2
  20. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/operation/callback.py +10 -10
  21. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/operation/child.py +58 -38
  22. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/operation/invoke.py +8 -8
  23. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/operation/map.py +27 -11
  24. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/operation/parallel.py +36 -14
  25. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/operation/step.py +24 -14
  26. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/operation/wait.py +5 -5
  27. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/operation/wait_for_condition.py +20 -12
  28. async_durable_execution-2.0.0a1/src/async_durable_execution/plugin.py +409 -0
  29. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/retries.py +107 -2
  30. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/serdes.py +3 -7
  31. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/state.py +204 -45
  32. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/suspend.py +1 -1
  33. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/threading.py +1 -1
  34. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/types.py +13 -6
  35. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/waits.py +2 -2
  36. async_durable_execution-2.0.0a1/tests/async_tools_test.py +22 -0
  37. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/concurrency_test.py +337 -74
  38. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/config_test.py +0 -11
  39. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/context_test.py +400 -30
  40. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/durable_executions_python_language_sdk_test.py +1 -1
  41. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/e2e/checkpoint_response_int_test.py +21 -41
  42. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/e2e/execution_int_test.py +13 -25
  43. async_durable_execution-2.0.0a1/tests/e2e/map_with_concurrent_waits_int_test.py +202 -0
  44. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/exceptions_test.py +145 -34
  45. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/execution_test.py +631 -159
  46. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/lambda_service_test.py +121 -14
  47. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/logger_test.py +8 -2
  48. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/operation/callback_test.py +92 -34
  49. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/operation/child_test.py +349 -27
  50. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/operation/invoke_test.py +102 -38
  51. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/operation/map_test.py +430 -171
  52. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/operation/parallel_test.py +383 -174
  53. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/operation/step_test.py +113 -27
  54. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/operation/wait_for_condition_test.py +149 -40
  55. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/operation/wait_test.py +29 -11
  56. async_durable_execution-2.0.0a1/tests/plugin_test.py +783 -0
  57. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/state_test.py +933 -14
  58. async_durable_execution-2.0.0a1/tests/with_retry_test.py +410 -0
  59. async_durable_execution-0.1.0/.github/workflows/pypi.yml +0 -70
  60. async_durable_execution-0.1.0/PKG-INFO +0 -80
  61. async_durable_execution-0.1.0/README.md +0 -70
  62. async_durable_execution-0.1.0/pyproject.toml +0 -44
  63. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/LICENSE +0 -0
  64. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/.gitignore +0 -0
  65. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/concurrency/__init__.py +0 -0
  66. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/operation/__init__.py +0 -0
  67. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/src/async_durable_execution/py.typed +0 -0
  68. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/__init__.py +0 -0
  69. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/e2e/__init__.py +0 -0
  70. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/operation/__init__.py +0 -0
  71. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/operation/base_test.py +0 -0
  72. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/retries_test.py +0 -0
  73. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/serdes_test.py +0 -0
  74. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/suspend_test.py +0 -0
  75. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/test_helpers.py +0 -0
  76. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/threading_test.py +0 -0
  77. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/tests/types_test.py +0 -0
  78. {async_durable_execution-0.1.0 → async_durable_execution-2.0.0a1}/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,12 @@ dist/
28
29
 
29
30
  .idea
30
31
 
31
- .kiro/
32
+ .kiro/
33
+
34
+ **/build/
35
+ **/*.zip
36
+ packages/async-durable-execution-examples/template.yaml
37
+ **/template.generated.json
38
+
39
+ .env
40
+ *.egg-info
@@ -0,0 +1,8 @@
1
+ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+
3
+ This repository is a modified fork of software originally released by
4
+ Amazon.com, Inc. or its affiliates under the Apache License 2.0.
5
+
6
+ Fork modifications and continued maintenance:
7
+ Copyright 2026 Zhongke Chen
8
+ Repository: https://github.com/zhongkechen/async-durable-execution
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: async-durable-execution
3
+ Version: 2.0.0a1
4
+ Summary: Community-maintained durable execution SDK for AWS Lambda in Python
5
+ Project-URL: Documentation, https://github.com/zhongkechen/async-durable-execution#readme
6
+ Project-URL: Issues, https://github.com/zhongkechen/async-durable-execution/issues
7
+ Project-URL: Source, https://github.com/zhongkechen/async-durable-execution
8
+ Author: Zhongke Chen
9
+ License-Expression: Apache-2.0
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Programming Language :: Python :: Implementation :: CPython
17
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: boto3>=1.42.1
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Async Durable Execution for Python
23
+
24
+ [![Build](https://github.com/zhongkechen/async-durable-execution/actions/workflows/build.yml/badge.svg)](https://github.com/zhongkechen/async-durable-execution/actions/workflows/build.yml)
25
+ [![PyPI - Version](https://img.shields.io/pypi/v/async-durable-execution.svg)](https://pypi.org/project/async-durable-execution)
26
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/async-durable-execution.svg)](https://pypi.org/project/async-durable-execution)
27
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/zhongkechen/async-durable-execution/badge)](https://scorecard.dev/viewer/?uri=github.com/zhongkechen/async-durable-execution)
28
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](../../LICENSE)
29
+
30
+ -----
31
+
32
+ Build reliable, long-running AWS Lambda workflows with checkpointed steps, waits, callbacks, and parallel execution.
33
+
34
+ This package is distributed from a community-maintained fork of the original Apache-2.0 licensed AWS project and continues under Apache License 2.0 with upstream notices preserved.
35
+
36
+ This fork is specifically focused on making async Python work naturally with durable functions. The public API remains synchronous at the durable operation boundary, but the examples, helpers, and package direction here prioritize `async def` handlers, steps, child contexts, callback submitters, and condition checks.
37
+
38
+ ## ✨ Key Features
39
+
40
+ - **Async-first fork** - This fork prioritizes making `async def` workflows feel natural with durable functions
41
+ - **Automatic checkpointing** - Resume execution after Lambda pauses or restarts
42
+ - **Durable steps** - Run work with retry strategies and deterministic replay
43
+ - **Waits and callbacks** - Pause for time or external signals without blocking Lambda
44
+ - **Parallel and map operations** - Fan out work with configurable completion criteria
45
+ - **Child contexts** - Structure complex workflows into isolated subflows
46
+ - **Replay-safe logging** - Use `context.logger` for structured, de-duplicated logs
47
+ - **Local and cloud testing** - Validate workflows with the testing SDK
48
+ - **Async Python support** - Use `async def` for handlers, steps, child contexts, callback submitters, and wait-for-condition checks
49
+
50
+ ## 📦 Packages
51
+
52
+ | Package | Description | Version |
53
+ | --- | --- | --- |
54
+ | `async-durable-execution` | Execution SDK for Lambda durable functions | [![PyPI - Version](https://img.shields.io/pypi/v/async-durable-execution.svg)](https://pypi.org/project/async-durable-execution) |
55
+ | `async-durable-execution-runner` | Local/cloud test runner and pytest helpers | [![PyPI - Version](https://img.shields.io/pypi/v/async-durable-execution-runner.svg)](https://pypi.org/project/async-durable-execution-runner) |
56
+ | `async-durable-execution-examples` | Example durable functions and integration tests for local and cloud workflows | Shared repo version |
57
+
58
+ ## 🚀 Quick Start
59
+
60
+ This fork recommends writing new durable workflows with async callables by default.
61
+
62
+ Install the execution SDK:
63
+
64
+ ```console
65
+ pip install async-durable-execution
66
+ ```
67
+
68
+ Create a durable Lambda handler:
69
+
70
+ ```python
71
+ import asyncio
72
+
73
+ from async_durable_execution import (
74
+ DurableContext,
75
+ StepContext,
76
+ durable_execution,
77
+ durable_step,
78
+ )
79
+ from async_durable_execution.config import Duration
80
+
81
+
82
+ @durable_step
83
+ async def validate_order(step_ctx: StepContext, order_id: str) -> dict:
84
+ await asyncio.sleep(0)
85
+ step_ctx.logger.info("Validating order", extra={"order_id": order_id})
86
+ return {"order_id": order_id, "valid": True}
87
+
88
+
89
+ @durable_execution
90
+ async def handler(event: dict, context: DurableContext) -> dict:
91
+ order_id = event["order_id"]
92
+ context.logger.info("Starting workflow", extra={"order_id": order_id})
93
+
94
+ validation = context.step(validate_order(order_id), name="validate_order")
95
+ if not validation["valid"]:
96
+ return {"status": "rejected", "order_id": order_id}
97
+
98
+ # simulate approval (real world: use wait_for_callback)
99
+ context.wait(duration=Duration.from_seconds(5), name="await_confirmation")
100
+
101
+ return {"status": "approved", "order_id": order_id}
102
+ ```
103
+
104
+ Async callables are supported anywhere the SDK accepts user code, including `map()` item functions, `parallel()` branches, child contexts, callback submitters, and wait-for-condition checks. The public Durable APIs stay synchronous, so async work is awaited transparently for you:
105
+
106
+ ```python
107
+ import asyncio
108
+
109
+ from async_durable_execution import (
110
+ DurableContext,
111
+ StepContext,
112
+ durable_execution,
113
+ durable_step,
114
+ )
115
+
116
+
117
+ @durable_step
118
+ async def fetch_order(step_ctx: StepContext, order_id: str) -> dict:
119
+ await asyncio.sleep(0)
120
+ step_ctx.logger.info("Fetched order", extra={"order_id": order_id})
121
+ return {"order_id": order_id, "status": "ready"}
122
+
123
+
124
+ @durable_execution
125
+ async def handler(event: dict, context: DurableContext) -> dict:
126
+ order = context.step(fetch_order(event["order_id"]), name="fetch_order")
127
+ return {"order": order}
128
+ ```
129
+
130
+ ## 📚 Documentation
131
+
132
+ The complete documentation for the AWS Durable Execution SDK for Python lives on the AWS Documentation site:
133
+
134
+ - **[AWS Durable Execution Documentation](https://docs.aws.amazon.com/durable-execution/)** - Concepts, getting started, core operations, advanced topics, and API reference
135
+ - **[AWS Lambda Durable Functions Guide](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html)** - How durable functions work on Lambda
136
+
137
+ ## 💬 Feedback & Support
138
+
139
+ - [Bug report](https://github.com/zhongkechen/async-durable-execution/issues/new?template=bug_report.yml)
140
+ - [Feature request](https://github.com/zhongkechen/async-durable-execution/issues/new?template=feature_request.yml)
141
+ - [Documentation feedback](https://github.com/zhongkechen/async-durable-execution/issues/new?template=documentation.yml)
142
+ - [Contributing guide](../../CONTRIBUTING.md)
143
+
144
+ ## 📄 License
145
+
146
+ See the [LICENSE](../../LICENSE) file for our project's licensing.
@@ -0,0 +1,125 @@
1
+ # Async Durable Execution for Python
2
+
3
+ [![Build](https://github.com/zhongkechen/async-durable-execution/actions/workflows/build.yml/badge.svg)](https://github.com/zhongkechen/async-durable-execution/actions/workflows/build.yml)
4
+ [![PyPI - Version](https://img.shields.io/pypi/v/async-durable-execution.svg)](https://pypi.org/project/async-durable-execution)
5
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/async-durable-execution.svg)](https://pypi.org/project/async-durable-execution)
6
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/zhongkechen/async-durable-execution/badge)](https://scorecard.dev/viewer/?uri=github.com/zhongkechen/async-durable-execution)
7
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](../../LICENSE)
8
+
9
+ -----
10
+
11
+ Build reliable, long-running AWS Lambda workflows with checkpointed steps, waits, callbacks, and parallel execution.
12
+
13
+ This package is distributed from a community-maintained fork of the original Apache-2.0 licensed AWS project and continues under Apache License 2.0 with upstream notices preserved.
14
+
15
+ This fork is specifically focused on making async Python work naturally with durable functions. The public API remains synchronous at the durable operation boundary, but the examples, helpers, and package direction here prioritize `async def` handlers, steps, child contexts, callback submitters, and condition checks.
16
+
17
+ ## ✨ Key Features
18
+
19
+ - **Async-first fork** - This fork prioritizes making `async def` workflows feel natural with durable functions
20
+ - **Automatic checkpointing** - Resume execution after Lambda pauses or restarts
21
+ - **Durable steps** - Run work with retry strategies and deterministic replay
22
+ - **Waits and callbacks** - Pause for time or external signals without blocking Lambda
23
+ - **Parallel and map operations** - Fan out work with configurable completion criteria
24
+ - **Child contexts** - Structure complex workflows into isolated subflows
25
+ - **Replay-safe logging** - Use `context.logger` for structured, de-duplicated logs
26
+ - **Local and cloud testing** - Validate workflows with the testing SDK
27
+ - **Async Python support** - Use `async def` for handlers, steps, child contexts, callback submitters, and wait-for-condition checks
28
+
29
+ ## 📦 Packages
30
+
31
+ | Package | Description | Version |
32
+ | --- | --- | --- |
33
+ | `async-durable-execution` | Execution SDK for Lambda durable functions | [![PyPI - Version](https://img.shields.io/pypi/v/async-durable-execution.svg)](https://pypi.org/project/async-durable-execution) |
34
+ | `async-durable-execution-runner` | Local/cloud test runner and pytest helpers | [![PyPI - Version](https://img.shields.io/pypi/v/async-durable-execution-runner.svg)](https://pypi.org/project/async-durable-execution-runner) |
35
+ | `async-durable-execution-examples` | Example durable functions and integration tests for local and cloud workflows | Shared repo version |
36
+
37
+ ## 🚀 Quick Start
38
+
39
+ This fork recommends writing new durable workflows with async callables by default.
40
+
41
+ Install the execution SDK:
42
+
43
+ ```console
44
+ pip install async-durable-execution
45
+ ```
46
+
47
+ Create a durable Lambda handler:
48
+
49
+ ```python
50
+ import asyncio
51
+
52
+ from async_durable_execution import (
53
+ DurableContext,
54
+ StepContext,
55
+ durable_execution,
56
+ durable_step,
57
+ )
58
+ from async_durable_execution.config import Duration
59
+
60
+
61
+ @durable_step
62
+ async def validate_order(step_ctx: StepContext, order_id: str) -> dict:
63
+ await asyncio.sleep(0)
64
+ step_ctx.logger.info("Validating order", extra={"order_id": order_id})
65
+ return {"order_id": order_id, "valid": True}
66
+
67
+
68
+ @durable_execution
69
+ async def handler(event: dict, context: DurableContext) -> dict:
70
+ order_id = event["order_id"]
71
+ context.logger.info("Starting workflow", extra={"order_id": order_id})
72
+
73
+ validation = context.step(validate_order(order_id), name="validate_order")
74
+ if not validation["valid"]:
75
+ return {"status": "rejected", "order_id": order_id}
76
+
77
+ # simulate approval (real world: use wait_for_callback)
78
+ context.wait(duration=Duration.from_seconds(5), name="await_confirmation")
79
+
80
+ return {"status": "approved", "order_id": order_id}
81
+ ```
82
+
83
+ Async callables are supported anywhere the SDK accepts user code, including `map()` item functions, `parallel()` branches, child contexts, callback submitters, and wait-for-condition checks. The public Durable APIs stay synchronous, so async work is awaited transparently for you:
84
+
85
+ ```python
86
+ import asyncio
87
+
88
+ from async_durable_execution import (
89
+ DurableContext,
90
+ StepContext,
91
+ durable_execution,
92
+ durable_step,
93
+ )
94
+
95
+
96
+ @durable_step
97
+ async def fetch_order(step_ctx: StepContext, order_id: str) -> dict:
98
+ await asyncio.sleep(0)
99
+ step_ctx.logger.info("Fetched order", extra={"order_id": order_id})
100
+ return {"order_id": order_id, "status": "ready"}
101
+
102
+
103
+ @durable_execution
104
+ async def handler(event: dict, context: DurableContext) -> dict:
105
+ order = context.step(fetch_order(event["order_id"]), name="fetch_order")
106
+ return {"order": order}
107
+ ```
108
+
109
+ ## 📚 Documentation
110
+
111
+ The complete documentation for the AWS Durable Execution SDK for Python lives on the AWS Documentation site:
112
+
113
+ - **[AWS Durable Execution Documentation](https://docs.aws.amazon.com/durable-execution/)** - Concepts, getting started, core operations, advanced topics, and API reference
114
+ - **[AWS Lambda Durable Functions Guide](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html)** - How durable functions work on Lambda
115
+
116
+ ## 💬 Feedback & Support
117
+
118
+ - [Bug report](https://github.com/zhongkechen/async-durable-execution/issues/new?template=bug_report.yml)
119
+ - [Feature request](https://github.com/zhongkechen/async-durable-execution/issues/new?template=feature_request.yml)
120
+ - [Documentation feedback](https://github.com/zhongkechen/async-durable-execution/issues/new?template=documentation.yml)
121
+ - [Contributing guide](../../CONTRIBUTING.md)
122
+
123
+ ## 📄 License
124
+
125
+ See the [LICENSE](../../LICENSE) file for our project's licensing.
@@ -1,4 +1,5 @@
1
1
  # SPDX-FileCopyrightText: 2025-present Amazon.com, Inc. or its affiliates.
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- __version__ = "0.1.0"
4
+ # Modified for the async-durable-execution fork; shared version source for all packages.
5
+ __version__ = "2.0.0a1"
@@ -0,0 +1,86 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ # Modified fork metadata for async-durable-execution; upstream Apache-2.0 notices are preserved in the repository LICENSE and NOTICE files.
6
+ [project]
7
+ name = "async-durable-execution"
8
+ dynamic = ["version"]
9
+ description = "Community-maintained durable execution SDK for AWS Lambda in Python"
10
+ readme = "README.md"
11
+ requires-python = ">=3.11"
12
+ license = "Apache-2.0"
13
+ keywords = []
14
+ authors = [{ name = "Zhongke Chen" }]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Programming Language :: Python",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Programming Language :: Python :: Implementation :: CPython",
23
+ "Programming Language :: Python :: Implementation :: PyPy",
24
+ ]
25
+ dependencies = ["boto3>=1.42.1"]
26
+
27
+ [project.urls]
28
+ Documentation = "https://github.com/zhongkechen/async-durable-execution#readme"
29
+ Issues = "https://github.com/zhongkechen/async-durable-execution/issues"
30
+ Source = "https://github.com/zhongkechen/async-durable-execution"
31
+
32
+ [tool.hatch.build.targets.sdist.force-include]
33
+ "../../LICENSE" = "LICENSE"
34
+ "../../NOTICE" = "NOTICE"
35
+ "../../VERSION.py" = "VERSION.py"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/async_durable_execution"]
39
+
40
+ [tool.hatch.build.targets.wheel.force-include]
41
+ "../../LICENSE" = "LICENSE"
42
+ "../../NOTICE" = "NOTICE"
43
+ "../../VERSION.py" = "VERSION.py"
44
+
45
+ [tool.hatch.version]
46
+ path = "../../VERSION.py"
47
+
48
+ [tool.coverage.run]
49
+ source_pkgs = ["async_durable_execution"]
50
+ branch = true
51
+ parallel = true
52
+ omit = ["src/async_durable_execution/__about__.py"]
53
+
54
+ [tool.coverage.paths]
55
+ async_durable_execution = [
56
+ "src/async_durable_execution",
57
+ "*/async-durable-execution/src/async_durable_execution",
58
+ ]
59
+
60
+ [tool.coverage.report]
61
+ exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"]
62
+
63
+ [tool.ruff]
64
+ line-length = 88
65
+ target-version = "py311"
66
+
67
+ [tool.ruff.lint]
68
+ preview = true
69
+ select = ["TID252"] # Enforce absolute imports (ban relative imports)
70
+
71
+ [tool.ruff.lint.isort]
72
+ known-first-party = ["async_durable_execution"]
73
+ force-single-line = false
74
+ lines-after-imports = 2
75
+
76
+ [tool.ruff.lint.per-file-ignores]
77
+ "tests/**" = [
78
+ "ARG001",
79
+ "ARG002",
80
+ "ARG005",
81
+ "S101",
82
+ "PLR2004",
83
+ "PLR6301",
84
+ "SIM117",
85
+ "TRY301",
86
+ ]
@@ -0,0 +1,33 @@
1
+ # SPDX-FileCopyrightText: 2025-present Amazon.com, Inc. or its affiliates.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ from importlib.metadata import PackageNotFoundError, version
5
+ from pathlib import Path
6
+
7
+
8
+ _DIST_NAME = "async-durable-execution"
9
+
10
+
11
+ def _read_repo_version() -> str:
12
+ version_file = _find_version_file()
13
+ if version_file is None:
14
+ return "0.0.0"
15
+
16
+ namespace: dict[str, str] = {}
17
+ exec(version_file.read_text(encoding="utf-8"), namespace)
18
+ return namespace["__version__"]
19
+
20
+
21
+ def _find_version_file() -> Path | None:
22
+ current = Path(__file__).resolve()
23
+ for parent in current.parents:
24
+ candidate = parent / "VERSION.py"
25
+ if candidate.exists():
26
+ return candidate
27
+ return None
28
+
29
+
30
+ try:
31
+ __version__ = version(_DIST_NAME)
32
+ except PackageNotFoundError:
33
+ __version__ = _read_repo_version()
@@ -1,42 +1,50 @@
1
1
  """AWS Lambda Durable Executions Python SDK."""
2
2
 
3
3
  # Package metadata
4
- from .__about__ import __version__
4
+ from async_durable_execution.__about__ import __version__
5
5
 
6
6
  # Main context - used in every durable function
7
7
  # Helper decorators - commonly used for step functions
8
8
  # Concurrency
9
- from .concurrency.models import BatchResult
10
- from .context import (
9
+ from async_durable_execution.concurrency.models import BatchResult
10
+ from async_durable_execution.config import ParallelBranch
11
+ from async_durable_execution.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,
15
17
  )
16
18
 
17
19
  # Most common exceptions - users need to handle these exceptions
18
- from .exceptions import (
20
+ from async_durable_execution.exceptions import (
19
21
  DurableExecutionsError,
20
22
  InvocationError,
21
23
  ValidationError,
22
24
  )
23
25
 
24
26
  # Core decorator - used in every durable function
25
- from .execution import durable_execution
27
+ from async_durable_execution.execution import durable_execution
28
+ from async_durable_execution.retries import WithRetryConfig, with_retry
26
29
 
27
30
  # Essential context types - passed to user functions
28
- from .types import StepContext
31
+ from async_durable_execution.types import StepContext
32
+
29
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
  ]
@@ -0,0 +1,49 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import inspect
5
+ import queue
6
+ import threading
7
+ from collections.abc import Awaitable, Callable
8
+ from typing import Any, TypeVar, cast
9
+
10
+
11
+ T = TypeVar("T")
12
+
13
+
14
+ def resolve_awaitable(value: T | Awaitable[T]) -> T:
15
+ if inspect.isawaitable(value):
16
+ return run_awaitable(cast(Awaitable[T], value))
17
+ return value
18
+
19
+
20
+ def invoke_callable(func: Callable[..., T | Awaitable[T]], *args, **kwargs) -> T:
21
+ return resolve_awaitable(func(*args, **kwargs))
22
+
23
+
24
+ def run_awaitable(awaitable: Awaitable[T]) -> T:
25
+ try:
26
+ asyncio.get_running_loop()
27
+ except RuntimeError:
28
+ return asyncio.run(awaitable)
29
+
30
+ return _run_awaitable_in_thread(awaitable)
31
+
32
+
33
+ def _run_awaitable_in_thread(awaitable: Awaitable[T]) -> T:
34
+ result_queue: queue.Queue[tuple[bool, T | BaseException]] = queue.Queue(maxsize=1)
35
+
36
+ def runner() -> None:
37
+ try:
38
+ result_queue.put((True, asyncio.run(awaitable)))
39
+ except BaseException as exc: # noqa: BLE001
40
+ result_queue.put((False, exc))
41
+
42
+ thread = threading.Thread(target=runner, name="dex-async-user-code", daemon=True)
43
+ thread.start()
44
+ success, payload = result_queue.get()
45
+ thread.join()
46
+
47
+ if success:
48
+ return cast(T, payload)
49
+ raise cast(BaseException, payload)