aws-durable-execution-sdk-python-testing 1.1.1__tar.gz → 1.2.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_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/.gitignore +2 -2
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/PKG-INFO +2 -3
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/__about__.py +1 -1
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/__init__.py +3 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/cli.py +27 -25
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/execution.py +7 -3
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/executor.py +5 -4
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/invoker.py +30 -13
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/model.py +32 -3
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/runner.py +2 -3
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/web/handlers.py +6 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/pyproject.toml +4 -35
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/LICENSE +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/NOTICE +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/README.md +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/__init__.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/processor.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/processors/__init__.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/processors/base.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/processors/callback.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/processors/context.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/processors/execution.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/processors/step.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/processors/wait.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/transformer.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/validators/__init__.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/validators/checkpoint.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/validators/operations/__init__.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/validators/operations/callback.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/validators/operations/context.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/validators/operations/execution.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/validators/operations/invoke.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/validators/operations/step.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/validators/operations/wait.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/checkpoint/validators/transitions.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/client.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/exceptions.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/observer.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/py.typed +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/scheduler.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/stores/__init__.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/stores/base.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/stores/filesystem.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/stores/memory.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/stores/sqlite.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/token.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/web/__init__.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/web/errors.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/web/models.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/web/routes.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/web/serialization.py +0 -0
- {aws_durable_execution_sdk_python_testing-1.1.1 → aws_durable_execution_sdk_python_testing-1.2.0}/aws_durable_execution_sdk_python_testing/web/server.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aws-durable-execution-sdk-python-testing
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: AWS Durable Execution Testing SDK for Python
|
|
5
5
|
Project-URL: Documentation, https://github.com/aws/aws-durable-execution-sdk-python-testing#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/aws/aws-durable-execution-sdk-python-testing/issues
|
|
7
7
|
Project-URL: Source, https://github.com/aws/aws-durable-execution-sdk-python-testing
|
|
8
|
-
Author-email:
|
|
8
|
+
Author-email: AWS durable-execution-dev <durable-execution-dev@amazon.com>
|
|
9
9
|
License-Expression: Apache-2.0
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
License-File: NOTICE
|
|
@@ -20,7 +20,6 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
20
20
|
Requires-Python: >=3.11
|
|
21
21
|
Requires-Dist: aws-durable-execution-sdk-python>=1.0.0
|
|
22
22
|
Requires-Dist: boto3>=1.42.1
|
|
23
|
-
Requires-Dist: requests>=2.25.0
|
|
24
23
|
Description-Content-Type: text/markdown
|
|
25
24
|
|
|
26
25
|
# AWS Durable Execution Testing SDK for Python
|
|
@@ -9,6 +9,8 @@ from aws_durable_execution_sdk_python_testing.runner import (
|
|
|
9
9
|
WebRunnerConfig,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
+
from aws_durable_execution_sdk_python_testing.__about__ import __version__
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
__all__ = [
|
|
14
16
|
"DurableChildContextTestRunner",
|
|
@@ -17,4 +19,5 @@ __all__ = [
|
|
|
17
19
|
"DurableFunctionTestRunner",
|
|
18
20
|
"WebRunner",
|
|
19
21
|
"WebRunnerConfig",
|
|
22
|
+
"__version__",
|
|
20
23
|
]
|
|
@@ -21,7 +21,9 @@ from urllib.parse import urljoin
|
|
|
21
21
|
|
|
22
22
|
import aws_durable_execution_sdk_python
|
|
23
23
|
import boto3 # type: ignore
|
|
24
|
-
import
|
|
24
|
+
from urllib.error import HTTPError, URLError
|
|
25
|
+
from urllib.request import Request, urlopen
|
|
26
|
+
|
|
25
27
|
from botocore.exceptions import ConnectionError # type: ignore
|
|
26
28
|
|
|
27
29
|
from aws_durable_execution_sdk_python_testing.exceptions import (
|
|
@@ -348,37 +350,37 @@ class CliApp:
|
|
|
348
350
|
endpoint_url = self.config.local_runner_endpoint
|
|
349
351
|
url = urljoin(endpoint_url, "/start-durable-execution")
|
|
350
352
|
|
|
351
|
-
headers = {"Content-Type": "application/json"}
|
|
352
353
|
payload = start_input.to_dict()
|
|
354
|
+
data = json.dumps(payload).encode("utf-8")
|
|
355
|
+
req = Request(
|
|
356
|
+
url,
|
|
357
|
+
data=data,
|
|
358
|
+
headers={"Content-Type": "application/json"},
|
|
359
|
+
method="POST",
|
|
360
|
+
)
|
|
353
361
|
|
|
354
|
-
response = requests.post(url, json=payload, headers=headers, timeout=30)
|
|
355
|
-
|
|
356
|
-
if response.status_code == 201: # noqa: PLR2004
|
|
357
|
-
# Success - print the response
|
|
358
|
-
result = response.json()
|
|
359
|
-
print(json.dumps(result, indent=2)) # noqa: T201
|
|
360
|
-
return 0
|
|
361
|
-
|
|
362
|
-
# Error - print error details
|
|
363
362
|
try:
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
363
|
+
with urlopen(req, timeout=10) as response: # noqa: S310
|
|
364
|
+
result = json.loads(response.read().decode("utf-8"))
|
|
365
|
+
print(json.dumps(result, indent=2)) # noqa: T201
|
|
366
|
+
return 0
|
|
367
|
+
except HTTPError as e:
|
|
368
|
+
try:
|
|
369
|
+
error_data = json.loads(e.read().decode("utf-8"))
|
|
370
|
+
logger.exception("HTTP error response")
|
|
371
|
+
print( # noqa: T201
|
|
372
|
+
f"Error: {error_data.get('ErrorMessage', 'Unknown error')}",
|
|
373
|
+
file=sys.stderr,
|
|
374
|
+
)
|
|
375
|
+
except json.JSONDecodeError:
|
|
376
|
+
logger.exception("Non-JSON error response")
|
|
377
|
+
return 1
|
|
378
|
+
|
|
379
|
+
except URLError:
|
|
375
380
|
logger.exception(
|
|
376
381
|
"Error: Could not connect to the local runner server. Is it running?"
|
|
377
382
|
)
|
|
378
383
|
return 1
|
|
379
|
-
except requests.exceptions.Timeout:
|
|
380
|
-
logger.exception("Request timeout")
|
|
381
|
-
return 1
|
|
382
384
|
except Exception:
|
|
383
385
|
logger.exception("Unexpected error in invoke command")
|
|
384
386
|
return 1
|
|
@@ -91,7 +91,11 @@ class Execution:
|
|
|
91
91
|
# Pattern: arn:(aws[a-zA-Z-]*)?:lambda:[a-z]{2}(-gov)?-[a-z]+-\d{1}:\d{12}:durable-execution:[a-zA-Z0-9-_\.]+:[a-zA-Z0-9-_\.]+:[a-zA-Z0-9-_\.]+
|
|
92
92
|
# Example: arn:aws:lambda:us-east-1:123456789012:durable-execution:myDurableFunction:myDurableExecutionName:ce67da72-3701-4f83-9174-f4189d27b0a5
|
|
93
93
|
return Execution(
|
|
94
|
-
durable_execution_arn=str(uuid4())
|
|
94
|
+
durable_execution_arn=str(uuid4())
|
|
95
|
+
+ "/"
|
|
96
|
+
+ (input.invocation_id or str(uuid4())),
|
|
97
|
+
start_input=input,
|
|
98
|
+
operations=[],
|
|
95
99
|
)
|
|
96
100
|
|
|
97
101
|
def to_json_dict(self) -> dict[str, Any]:
|
|
@@ -102,7 +106,7 @@ class Execution:
|
|
|
102
106
|
"Operations": [op.to_json_dict() for op in self.operations],
|
|
103
107
|
"Updates": [update.to_dict() for update in self.updates],
|
|
104
108
|
"InvocationCompletions": [
|
|
105
|
-
completion.
|
|
109
|
+
completion.to_json_dict() for completion in self.invocation_completions
|
|
106
110
|
],
|
|
107
111
|
"UsedTokens": list(self.used_tokens),
|
|
108
112
|
"TokenSequence": self._token_sequence,
|
|
@@ -135,7 +139,7 @@ class Execution:
|
|
|
135
139
|
OperationUpdate.from_dict(update_data) for update_data in data["Updates"]
|
|
136
140
|
]
|
|
137
141
|
execution.invocation_completions = [
|
|
138
|
-
InvocationCompletedDetails.
|
|
142
|
+
InvocationCompletedDetails.from_json_dict(item)
|
|
139
143
|
for item in data.get("InvocationCompletions", [])
|
|
140
144
|
]
|
|
141
145
|
execution.used_tokens = set(data["UsedTokens"])
|
|
@@ -115,6 +115,7 @@ class Executor(ExecutionObserver):
|
|
|
115
115
|
execution = Execution.new(input=input)
|
|
116
116
|
execution.start()
|
|
117
117
|
self._store.save(execution)
|
|
118
|
+
logger.debug("Created execution with ARN: %s", execution.durable_execution_arn)
|
|
118
119
|
|
|
119
120
|
completion_event = self._scheduler.create_event()
|
|
120
121
|
self._completion_events[execution.durable_execution_arn] = completion_event
|
|
@@ -322,14 +323,14 @@ class Executor(ExecutionObserver):
|
|
|
322
323
|
|
|
323
324
|
Raises:
|
|
324
325
|
ResourceNotFoundException: If execution does not exist
|
|
325
|
-
ExecutionAlreadyStartedException: If execution is already completed
|
|
326
326
|
"""
|
|
327
327
|
execution = self.get_execution(execution_arn)
|
|
328
328
|
|
|
329
329
|
if execution.is_complete:
|
|
330
|
-
#
|
|
331
|
-
|
|
332
|
-
|
|
330
|
+
# Idempotent: return the existing stop timestamp
|
|
331
|
+
execution_op = execution.get_operation_execution_started()
|
|
332
|
+
stop_timestamp = execution_op.end_timestamp or datetime.now(UTC)
|
|
333
|
+
return StopDurableExecutionResponse(stop_timestamp=stop_timestamp)
|
|
333
334
|
|
|
334
335
|
# Use provided error or create a default one
|
|
335
336
|
stop_error = error or ErrorObject.from_message(
|
|
@@ -7,6 +7,8 @@ from typing import TYPE_CHECKING, Any, Protocol
|
|
|
7
7
|
from uuid import uuid4
|
|
8
8
|
|
|
9
9
|
import boto3 # type: ignore
|
|
10
|
+
from botocore.config import Config # type: ignore
|
|
11
|
+
|
|
10
12
|
from aws_durable_execution_sdk_python.execution import (
|
|
11
13
|
DurableExecutionInvocationInput,
|
|
12
14
|
DurableExecutionInvocationInputWithClient,
|
|
@@ -16,9 +18,12 @@ from aws_durable_execution_sdk_python.execution import (
|
|
|
16
18
|
|
|
17
19
|
from aws_durable_execution_sdk_python_testing.exceptions import (
|
|
18
20
|
DurableFunctionsTestError,
|
|
21
|
+
InvalidParameterValueException,
|
|
22
|
+
ResourceNotFoundException,
|
|
19
23
|
)
|
|
20
24
|
from aws_durable_execution_sdk_python_testing.model import LambdaContext
|
|
21
25
|
|
|
26
|
+
|
|
22
27
|
if TYPE_CHECKING:
|
|
23
28
|
from collections.abc import Callable
|
|
24
29
|
|
|
@@ -26,6 +31,26 @@ if TYPE_CHECKING:
|
|
|
26
31
|
from aws_durable_execution_sdk_python_testing.execution import Execution
|
|
27
32
|
|
|
28
33
|
|
|
34
|
+
# Max Lambda function timeout is 15 minutes (900s); we give headroom for
|
|
35
|
+
# network round-trip and RIE startup.
|
|
36
|
+
_LAMBDA_READ_TIMEOUT_SECONDS = 960
|
|
37
|
+
_LAMBDA_CLIENT_CONFIG = Config(
|
|
38
|
+
read_timeout=_LAMBDA_READ_TIMEOUT_SECONDS,
|
|
39
|
+
retries={"max_attempts": 0},
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def create_lambda_client(endpoint_url: str | None, region_name: str) -> Any:
|
|
44
|
+
"""Create a boto3 Lambda client configured for durable function invocations."""
|
|
45
|
+
|
|
46
|
+
return boto3.client(
|
|
47
|
+
"lambda",
|
|
48
|
+
endpoint_url=endpoint_url,
|
|
49
|
+
region_name=region_name,
|
|
50
|
+
config=_LAMBDA_CLIENT_CONFIG,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
29
54
|
@dataclass(frozen=True)
|
|
30
55
|
class InvokeResponse:
|
|
31
56
|
"""Response from invoking a durable function."""
|
|
@@ -133,9 +158,7 @@ class LambdaInvoker(Invoker):
|
|
|
133
158
|
@staticmethod
|
|
134
159
|
def create(endpoint_url: str, region_name: str) -> LambdaInvoker:
|
|
135
160
|
"""Create with the boto lambda client."""
|
|
136
|
-
invoker = LambdaInvoker(
|
|
137
|
-
boto3.client("lambda", endpoint_url=endpoint_url, region_name=region_name)
|
|
138
|
-
)
|
|
161
|
+
invoker = LambdaInvoker(create_lambda_client(endpoint_url, region_name))
|
|
139
162
|
invoker._current_endpoint = endpoint_url
|
|
140
163
|
invoker._endpoint_clients[endpoint_url] = invoker.lambda_client
|
|
141
164
|
return invoker
|
|
@@ -145,8 +168,8 @@ class LambdaInvoker(Invoker):
|
|
|
145
168
|
# Cache client by endpoint to reuse across executions
|
|
146
169
|
with self._lock:
|
|
147
170
|
if endpoint_url not in self._endpoint_clients:
|
|
148
|
-
self._endpoint_clients[endpoint_url] =
|
|
149
|
-
|
|
171
|
+
self._endpoint_clients[endpoint_url] = create_lambda_client(
|
|
172
|
+
endpoint_url, region_name
|
|
150
173
|
)
|
|
151
174
|
self.lambda_client = self._endpoint_clients[endpoint_url]
|
|
152
175
|
self._current_endpoint = endpoint_url
|
|
@@ -161,10 +184,8 @@ class LambdaInvoker(Invoker):
|
|
|
161
184
|
# Use provided endpoint or fall back to cached endpoint for this execution
|
|
162
185
|
if lambda_endpoint:
|
|
163
186
|
if lambda_endpoint not in self._endpoint_clients:
|
|
164
|
-
self._endpoint_clients[lambda_endpoint] =
|
|
165
|
-
"
|
|
166
|
-
endpoint_url=lambda_endpoint,
|
|
167
|
-
region_name=region_name or "us-east-1",
|
|
187
|
+
self._endpoint_clients[lambda_endpoint] = create_lambda_client(
|
|
188
|
+
lambda_endpoint, region_name or "us-east-1"
|
|
168
189
|
)
|
|
169
190
|
return self._endpoint_clients[lambda_endpoint]
|
|
170
191
|
|
|
@@ -217,10 +238,6 @@ class LambdaInvoker(Invoker):
|
|
|
217
238
|
InvalidParameterValueException: If parameters are invalid
|
|
218
239
|
DurableFunctionsTestError: For other invocation failures
|
|
219
240
|
"""
|
|
220
|
-
from aws_durable_execution_sdk_python_testing.exceptions import (
|
|
221
|
-
ResourceNotFoundException,
|
|
222
|
-
InvalidParameterValueException,
|
|
223
|
-
)
|
|
224
241
|
|
|
225
242
|
# Parameter validation
|
|
226
243
|
if not function_name or not function_name.strip():
|
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import datetime
|
|
6
|
+
import json
|
|
6
7
|
from dataclasses import dataclass, replace
|
|
7
8
|
from enum import Enum
|
|
8
9
|
from typing import Any
|
|
9
|
-
import json
|
|
10
|
-
|
|
11
|
-
from dateutil.tz import UTC
|
|
12
10
|
|
|
13
11
|
from aws_durable_execution_sdk_python.execution import DurableExecutionInvocationOutput
|
|
14
12
|
|
|
@@ -30,12 +28,14 @@ from aws_durable_execution_sdk_python.lambda_service import (
|
|
|
30
28
|
OperationUpdate,
|
|
31
29
|
StepDetails,
|
|
32
30
|
StepOptions,
|
|
31
|
+
TimestampConverter,
|
|
33
32
|
WaitDetails,
|
|
34
33
|
WaitOptions,
|
|
35
34
|
)
|
|
36
35
|
from aws_durable_execution_sdk_python.types import (
|
|
37
36
|
LambdaContext as LambdaContextProtocol,
|
|
38
37
|
)
|
|
38
|
+
from dateutil.tz import UTC
|
|
39
39
|
|
|
40
40
|
from aws_durable_execution_sdk_python_testing.exceptions import (
|
|
41
41
|
InvalidParameterValueException,
|
|
@@ -1239,6 +1239,27 @@ class InvocationCompletedDetails:
|
|
|
1239
1239
|
request_id=data["RequestId"],
|
|
1240
1240
|
)
|
|
1241
1241
|
|
|
1242
|
+
@classmethod
|
|
1243
|
+
def from_json_dict(cls, data: dict) -> InvocationCompletedDetails:
|
|
1244
|
+
"""Deserialize from JSON dict with Unix millisecond timestamps."""
|
|
1245
|
+
start_ts: datetime.datetime | None = TimestampConverter.from_unix_millis(
|
|
1246
|
+
data["StartTimestamp"]
|
|
1247
|
+
) # type: ignore[arg-type]
|
|
1248
|
+
end_ts: datetime.datetime | None = TimestampConverter.from_unix_millis(
|
|
1249
|
+
data["EndTimestamp"]
|
|
1250
|
+
) # type: ignore[arg-type]
|
|
1251
|
+
|
|
1252
|
+
if start_ts is None or end_ts is None:
|
|
1253
|
+
raise InvalidParameterValueException(
|
|
1254
|
+
"StartTimestamp and EndTimestamp cannot be null"
|
|
1255
|
+
)
|
|
1256
|
+
|
|
1257
|
+
return cls(
|
|
1258
|
+
start_timestamp=start_ts,
|
|
1259
|
+
end_timestamp=end_ts,
|
|
1260
|
+
request_id=data["RequestId"],
|
|
1261
|
+
)
|
|
1262
|
+
|
|
1242
1263
|
def to_dict(self) -> dict[str, Any]:
|
|
1243
1264
|
return {
|
|
1244
1265
|
"StartTimestamp": self.start_timestamp,
|
|
@@ -1246,6 +1267,14 @@ class InvocationCompletedDetails:
|
|
|
1246
1267
|
"RequestId": self.request_id,
|
|
1247
1268
|
}
|
|
1248
1269
|
|
|
1270
|
+
def to_json_dict(self) -> dict[str, Any]:
|
|
1271
|
+
"""Convert to JSON-serializable dict with Unix millisecond timestamps."""
|
|
1272
|
+
return {
|
|
1273
|
+
"StartTimestamp": TimestampConverter.to_unix_millis(self.start_timestamp),
|
|
1274
|
+
"EndTimestamp": TimestampConverter.to_unix_millis(self.end_timestamp),
|
|
1275
|
+
"RequestId": self.request_id,
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1249
1278
|
|
|
1250
1279
|
# endregion event_structures
|
|
1251
1280
|
|
|
@@ -46,6 +46,7 @@ from aws_durable_execution_sdk_python_testing.executor import Executor
|
|
|
46
46
|
from aws_durable_execution_sdk_python_testing.invoker import (
|
|
47
47
|
InProcessInvoker,
|
|
48
48
|
LambdaInvoker,
|
|
49
|
+
create_lambda_client,
|
|
49
50
|
)
|
|
50
51
|
from aws_durable_execution_sdk_python_testing.model import (
|
|
51
52
|
GetDurableExecutionHistoryResponse,
|
|
@@ -858,9 +859,7 @@ class WebRunner:
|
|
|
858
859
|
Exception: If client creation fails - exceptions propagate naturally
|
|
859
860
|
for CLI to handle as general Exception
|
|
860
861
|
"""
|
|
861
|
-
|
|
862
|
-
return boto3.client(
|
|
863
|
-
"lambda",
|
|
862
|
+
return create_lambda_client(
|
|
864
863
|
endpoint_url=self._config.lambda_endpoint,
|
|
865
864
|
region_name=self._config.local_runner_region,
|
|
866
865
|
)
|
|
@@ -291,16 +291,22 @@ class StartExecutionHandler(EndpointHandler):
|
|
|
291
291
|
Returns:
|
|
292
292
|
HTTPResponse: The HTTP response to send to the client
|
|
293
293
|
"""
|
|
294
|
+
logger.debug("🌟 HANDLER: Received POST /start-durable-execution request")
|
|
294
295
|
try:
|
|
295
296
|
body_data: dict[str, Any] = self._parse_json_body(request)
|
|
297
|
+
logger.debug("🌟 HANDLER: Parsed request body successfully")
|
|
296
298
|
|
|
297
299
|
start_input: StartDurableExecutionInput = (
|
|
298
300
|
StartDurableExecutionInput.from_dict(body_data)
|
|
299
301
|
)
|
|
302
|
+
logger.debug(
|
|
303
|
+
"🌟 HANDLER: Created StartDurableExecutionInput, calling executor.start_execution()"
|
|
304
|
+
)
|
|
300
305
|
|
|
301
306
|
start_output: StartDurableExecutionOutput = self.executor.start_execution(
|
|
302
307
|
start_input
|
|
303
308
|
)
|
|
309
|
+
logger.debug("🌟 HANDLER: executor.start_execution() returned successfully")
|
|
304
310
|
|
|
305
311
|
response_data: dict[str, Any] = start_output.to_dict()
|
|
306
312
|
|
|
@@ -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 = "
|
|
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",
|
|
@@ -23,7 +23,6 @@ classifiers = [
|
|
|
23
23
|
]
|
|
24
24
|
dependencies = [
|
|
25
25
|
"boto3>=1.42.1",
|
|
26
|
-
"requests>=2.25.0",
|
|
27
26
|
"aws_durable_execution_sdk_python>=1.0.0",
|
|
28
27
|
]
|
|
29
28
|
|
|
@@ -64,28 +63,7 @@ dependencies = [
|
|
|
64
63
|
|
|
65
64
|
[tool.hatch.envs.test.scripts]
|
|
66
65
|
test = "pytest tests/ -v"
|
|
67
|
-
|
|
68
|
-
examples-integration = "pytest --runner-mode=cloud -m example examples/test/ -v {args}"
|
|
69
|
-
cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=src/aws_durable_execution_sdk_python_testing --cov-fail-under=96"
|
|
70
|
-
|
|
71
|
-
[tool.hatch.envs.examples]
|
|
72
|
-
dependencies = [
|
|
73
|
-
"boto3",
|
|
74
|
-
"PyYAML",
|
|
75
|
-
"aws_durable_execution_sdk_python>=1.0.0",
|
|
76
|
-
]
|
|
77
|
-
[tool.hatch.envs.examples.scripts]
|
|
78
|
-
cli = "python examples/cli.py {args}"
|
|
79
|
-
bootstrap = "python examples/cli.py bootstrap"
|
|
80
|
-
generate-sam-template = "python examples/scripts/generate_sam_template.py"
|
|
81
|
-
build = "python examples/cli.py build"
|
|
82
|
-
deploy = "python examples/cli.py deploy {args}"
|
|
83
|
-
invoke = "python examples/cli.py invoke {args}"
|
|
84
|
-
get = "python examples/cli.py get {args}"
|
|
85
|
-
history = "python examples/cli.py history {args}"
|
|
86
|
-
policy = "python examples/cli.py policy {args}"
|
|
87
|
-
list = "python examples/cli.py list"
|
|
88
|
-
clean = "rm -rf examples/build examples/.aws-sam examples/*.zip"
|
|
66
|
+
cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=src/aws_durable_execution_sdk_python_testing --cov-fail-under=95"
|
|
89
67
|
|
|
90
68
|
[tool.hatch.envs.types]
|
|
91
69
|
extra-dependencies = ["mypy>=1.0.0", "pytest"]
|
|
@@ -115,7 +93,7 @@ exclude_lines = [
|
|
|
115
93
|
|
|
116
94
|
[tool.ruff]
|
|
117
95
|
line-length = 88
|
|
118
|
-
target-version = "
|
|
96
|
+
target-version = "py311"
|
|
119
97
|
|
|
120
98
|
[tool.ruff.lint]
|
|
121
99
|
preview = false
|
|
@@ -136,15 +114,6 @@ lines-after-imports = 2
|
|
|
136
114
|
"SIM117",
|
|
137
115
|
"TRY301",
|
|
138
116
|
]
|
|
139
|
-
"examples/test/**" = [
|
|
140
|
-
"ARG001",
|
|
141
|
-
"ARG002",
|
|
142
|
-
"ARG005",
|
|
143
|
-
"S101",
|
|
144
|
-
"PLR2004",
|
|
145
|
-
"SIM117",
|
|
146
|
-
"TRY301",
|
|
147
|
-
]
|
|
148
117
|
"src/aws_durable_execution_sdk_python_testing/invoker.py" = [
|
|
149
118
|
"A002", # Argument `input` is shadowing a Python builtin
|
|
150
119
|
]
|
|
@@ -158,6 +127,6 @@ markers = [
|
|
|
158
127
|
"durable_execution: marks tests that use the durable_runner fixture (not used for test selection)",
|
|
159
128
|
]
|
|
160
129
|
# Default test discovery paths
|
|
161
|
-
testpaths = ["tests"
|
|
130
|
+
testpaths = ["tests"]
|
|
162
131
|
# Default options for all test runs
|
|
163
132
|
addopts = "-v --strict-markers"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|