aws-durable-execution-sdk-python-testing 1.1.1__py3-none-any.whl → 1.1.2__py3-none-any.whl

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.
@@ -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.1.1"
4
+ __version__ = "1.1.2"
@@ -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
  ]
@@ -102,7 +102,7 @@ class Execution:
102
102
  "Operations": [op.to_json_dict() for op in self.operations],
103
103
  "Updates": [update.to_dict() for update in self.updates],
104
104
  "InvocationCompletions": [
105
- completion.to_dict() for completion in self.invocation_completions
105
+ completion.to_json_dict() for completion in self.invocation_completions
106
106
  ],
107
107
  "UsedTokens": list(self.used_tokens),
108
108
  "TokenSequence": self._token_sequence,
@@ -135,7 +135,7 @@ class Execution:
135
135
  OperationUpdate.from_dict(update_data) for update_data in data["Updates"]
136
136
  ]
137
137
  execution.invocation_completions = [
138
- InvocationCompletedDetails.from_dict(item)
138
+ InvocationCompletedDetails.from_json_dict(item)
139
139
  for item in data.get("InvocationCompletions", [])
140
140
  ]
141
141
  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
- # Context-aware mapping: execution already completed maps to ExecutionAlreadyStartedException
331
- msg: str = f"Execution {execution_arn} is already completed"
332
- raise ExecutionAlreadyStartedException(msg, execution_arn)
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] = boto3.client(
149
- "lambda", endpoint_url=endpoint_url, region_name=region_name
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] = boto3.client(
165
- "lambda",
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
- # Create client with Lambda endpoint configuration
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
 
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-durable-execution-sdk-python-testing
3
- Version: 1.1.1
3
+ Version: 1.1.2
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: yaythomas <tgaigher@amazon.com>
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
@@ -1,15 +1,15 @@
1
- aws_durable_execution_sdk_python_testing/__about__.py,sha256=K6F0cNJyiPOnBo1OtQh0Zl8-xoMS7hubOJU6p2XfWG0,137
2
- aws_durable_execution_sdk_python_testing/__init__.py,sha256=bNtlkAlTpEvWJlUyFHcadozQfnb0PjnDze6upzJiog4,483
1
+ aws_durable_execution_sdk_python_testing/__about__.py,sha256=7qzc63t8OUhHP9YvXrUIRIK7LxcEvXL60VN7Z4b_y4M,137
2
+ aws_durable_execution_sdk_python_testing/__init__.py,sha256=NJnSWv9wZndxH7C0lBDVyQ61uhT6i79m5Z_u-PJCVxg,578
3
3
  aws_durable_execution_sdk_python_testing/cli.py,sha256=ET2C3pYX_54BtV6GpLfP7FKkwOTgGoubPl0G9ixLmJk,18875
4
4
  aws_durable_execution_sdk_python_testing/client.py,sha256=1CFuUXBZIkM7YOWwlJu4OrcjrJSTk01-Zc3N75kYSR4,1682
5
5
  aws_durable_execution_sdk_python_testing/exceptions.py,sha256=ZgFPDtKgBn4-tj7fqaye901CksNQWL9RmMhgJBhEu_U,9110
6
- aws_durable_execution_sdk_python_testing/execution.py,sha256=cxbRYvqe8Fo1yvvd0fokIhXNbS_VkavOPwUVux-rQ2w,17476
7
- aws_durable_execution_sdk_python_testing/executor.py,sha256=EOWLFXzRgd_h2lz0Smb5wvgPeDcruG11Q32a0vdztEw,48548
8
- aws_durable_execution_sdk_python_testing/invoker.py,sha256=Bpgz6MHPpBRoMvT91Epj148gZyCHHs8n-mwRM8gfpPI,13104
9
- aws_durable_execution_sdk_python_testing/model.py,sha256=__3Vtq6n_FgcdRd0SbWN30y6eUcw9p7nj_agEX3KD1g,120112
6
+ aws_durable_execution_sdk_python_testing/execution.py,sha256=kU34EsVgtEPXQ8p4s-zjqhH38h0Gp9LxxyA16ofuwHI,17486
7
+ aws_durable_execution_sdk_python_testing/executor.py,sha256=Q0ktkPa8OUUX8ky8pujmUfiiIRNyMIEPCagqdmOw008,48594
8
+ aws_durable_execution_sdk_python_testing/invoker.py,sha256=Glx9ZN9hRRO_94Skb_qp95Y-DYCdTOfRapmt_KJ5uaU,13494
9
+ aws_durable_execution_sdk_python_testing/model.py,sha256=xS6kHPEo4RdHph_rN8dVpQxZC0XzZJpYRLtbABe3A_U,121291
10
10
  aws_durable_execution_sdk_python_testing/observer.py,sha256=32MOYOfrmlKRqbfTXTM7m22iT38U2oYLGUkp7ar4Kvw,4935
11
11
  aws_durable_execution_sdk_python_testing/py.typed,sha256=DbCDcAirv769HS1ibzZLXq8VPazYf7JCkKSzj3ctudM,58
12
- aws_durable_execution_sdk_python_testing/runner.py,sha256=iRhNs3BDEkKj03i3toVTqCYoyK0C11P-FUf97g44S98,42559
12
+ aws_durable_execution_sdk_python_testing/runner.py,sha256=RKjttyO-hb4o_JPvlpiXYi_2Znk6yAfrFN4zJ0vKyrY,42512
13
13
  aws_durable_execution_sdk_python_testing/scheduler.py,sha256=yqKGu8r3OoqPaERpILVNCIwjhFRxGOKxk5u2QJyF-44,8551
14
14
  aws_durable_execution_sdk_python_testing/token.py,sha256=5rUpWMMcfZUVdOX_ptukRYiIRDsOdUA7faHXNyDGJRU,1530
15
15
  aws_durable_execution_sdk_python_testing/checkpoint/__init__.py,sha256=sPmvbGOTRrDUvjiQwxsN8jS7uEQhpobt84yzYLEHfhk,81
@@ -39,14 +39,14 @@ aws_durable_execution_sdk_python_testing/stores/memory.py,sha256=V9PQTU6r-EHUqWP
39
39
  aws_durable_execution_sdk_python_testing/stores/sqlite.py,sha256=QvMY_BUyg3FAYNDzqQz7BMseGmCTlRLlPa0r02JYvMc,10822
40
40
  aws_durable_execution_sdk_python_testing/web/__init__.py,sha256=S8pemv1lfMXK4Kp8MIOAPEDMaUQGt9MArobCJhUERxw,84
41
41
  aws_durable_execution_sdk_python_testing/web/errors.py,sha256=8elkOXggINFhmfRFSPqu3XvD0msiJ2gd5GD86o7pTQU,340
42
- aws_durable_execution_sdk_python_testing/web/handlers.py,sha256=TBbroWvf_45Xus9zUR0ENVVQJMWWrO1Gt65F3KlTj6c,29129
42
+ aws_durable_execution_sdk_python_testing/web/handlers.py,sha256=5_r6zaoaPujnNJohXQ_iPbXJJCR7vPMJfDT_fXr-VjI,29523
43
43
  aws_durable_execution_sdk_python_testing/web/models.py,sha256=B4SKVcTculNsO4Qd53tqkPnwVxwguP5PjNgnWF7dp8o,8852
44
44
  aws_durable_execution_sdk_python_testing/web/routes.py,sha256=AU9fMhuR0HF9fltNZ6TZMNOueQc8wEeNyJqoBpMctm0,19466
45
45
  aws_durable_execution_sdk_python_testing/web/serialization.py,sha256=qN6qJNgEYVqRQD9uhHupJVxyZfz4sc8KzIwxEqVIEcI,7747
46
46
  aws_durable_execution_sdk_python_testing/web/server.py,sha256=DQWwgyb_GKkHbFw_OYgPG1o7uIZ4-sG7F-GAUclLYu0,8853
47
- aws_durable_execution_sdk_python_testing-1.1.1.dist-info/METADATA,sha256=1ztYF0AAmomzwasmgoPM-hkiDwguejCEvKDOYwQ5muo,8924
48
- aws_durable_execution_sdk_python_testing-1.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
49
- aws_durable_execution_sdk_python_testing-1.1.1.dist-info/entry_points.txt,sha256=fMm3DMHWb7k4-PSseaQ-BjUxNV_3Z7RyJaDVSwqnkEo,87
50
- aws_durable_execution_sdk_python_testing-1.1.1.dist-info/licenses/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
51
- aws_durable_execution_sdk_python_testing-1.1.1.dist-info/licenses/NOTICE,sha256=1CkO1kwu3Q_OHYTj-d-yiBJA_lNN73a4zSntavaD4oc,67
52
- aws_durable_execution_sdk_python_testing-1.1.1.dist-info/RECORD,,
47
+ aws_durable_execution_sdk_python_testing-1.1.2.dist-info/METADATA,sha256=WchFeXSbyfcodoypxrqTScDRwdrfqV2mk410VrSNo9s,8953
48
+ aws_durable_execution_sdk_python_testing-1.1.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
49
+ aws_durable_execution_sdk_python_testing-1.1.2.dist-info/entry_points.txt,sha256=fMm3DMHWb7k4-PSseaQ-BjUxNV_3Z7RyJaDVSwqnkEo,87
50
+ aws_durable_execution_sdk_python_testing-1.1.2.dist-info/licenses/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
51
+ aws_durable_execution_sdk_python_testing-1.1.2.dist-info/licenses/NOTICE,sha256=1CkO1kwu3Q_OHYTj-d-yiBJA_lNN73a4zSntavaD4oc,67
52
+ aws_durable_execution_sdk_python_testing-1.1.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.28.0
2
+ Generator: hatchling 1.29.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any