Skip to content

Commit f9dec36

Browse files
author
Rares Polenciuc
committed
refactor: replace dataclasses.replace with explicit factory methods
- Add Operation.from_existing() classmethod - Add StepDetails.from_existing() and CallbackDetails.from_existing() factory methods - Replace all dataclasses.replace() calls with explicit constructors - Add test coverage for new factory methods
1 parent fea8882 commit f9dec36

File tree

3 files changed

+28
-21
lines changed

3 files changed

+28
-21
lines changed

examples/src/wait_for_callback/wait_for_callback.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def external_system_call(_callback_id: str) -> None:
1010
"""Simulate calling an external system with callback ID."""
1111
# In real usage, this would make an API call to an external system
1212
# passing the callback_id for the system to call back when done
13+
print(_callback_id)
1314

1415

1516
@durable_execution

src/aws_durable_execution_sdk_python_testing/execution.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
import json
4-
from dataclasses import replace
4+
55
from datetime import UTC, datetime
66
from enum import Enum
77
from threading import Lock
@@ -13,12 +13,14 @@
1313
InvocationStatus,
1414
)
1515
from aws_durable_execution_sdk_python.lambda_service import (
16+
CallbackDetails,
1617
ErrorObject,
1718
ExecutionDetails,
1819
Operation,
1920
OperationStatus,
2021
OperationType,
2122
OperationUpdate,
23+
StepDetails,
2224
)
2325

2426
from aws_durable_execution_sdk_python_testing.exceptions import (
@@ -289,7 +291,7 @@ def complete_wait(self, operation_id: str) -> Operation:
289291
with self._state_lock:
290292
self._token_sequence += 1
291293
# Build and assign updated operation
292-
self.operations[index] = replace(
294+
self.operations[index] = Operation.from_existing(
293295
operation,
294296
status=OperationStatus.SUCCEEDED,
295297
end_timestamp=datetime.now(UTC),
@@ -316,12 +318,12 @@ def complete_retry(self, operation_id: str) -> Operation:
316318
# Build updated step_details with cleared next_attempt_timestamp
317319
new_step_details = None
318320
if operation.step_details:
319-
new_step_details = replace(
321+
new_step_details = StepDetails.from_existing(
320322
operation.step_details, next_attempt_timestamp=None
321323
)
322324

323325
# Build updated operation
324-
updated_operation = replace(
326+
updated_operation = Operation.from_existing(
325327
operation, status=OperationStatus.READY, step_details=new_step_details
326328
)
327329

@@ -342,12 +344,12 @@ def complete_callback_success(
342344
self._token_sequence += 1
343345
updated_callback_details = None
344346
if operation.callback_details:
345-
updated_callback_details = replace(
347+
updated_callback_details = CallbackDetails.from_existing(
346348
operation.callback_details,
347349
result=result.decode() if result else None,
348350
)
349351

350-
self.operations[index] = replace(
352+
self.operations[index] = Operation.from_existing(
351353
operation,
352354
status=OperationStatus.SUCCEEDED,
353355
end_timestamp=datetime.now(UTC),
@@ -369,11 +371,11 @@ def complete_callback_failure(
369371
self._token_sequence += 1
370372
updated_callback_details = None
371373
if operation.callback_details:
372-
updated_callback_details = replace(
374+
updated_callback_details = CallbackDetails.from_existing(
373375
operation.callback_details, error=error
374376
)
375377

376-
self.operations[index] = replace(
378+
self.operations[index] = Operation.from_existing(
377379
operation,
378380
status=OperationStatus.FAILED,
379381
end_timestamp=datetime.now(UTC),
@@ -386,7 +388,7 @@ def _end_execution(self, status: OperationStatus) -> None:
386388
execution_op: Operation = self.get_operation_execution_started()
387389
if execution_op.operation_type == OperationType.EXECUTION:
388390
with self._state_lock:
389-
self.operations[0] = replace(
391+
self.operations[0] = Operation.from_existing(
390392
execution_op,
391393
status=status,
392394
end_timestamp=datetime.now(UTC),

src/aws_durable_execution_sdk_python_testing/model.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from __future__ import annotations
44

55
import datetime
6-
from dataclasses import dataclass, replace
6+
from dataclasses import dataclass
77
from enum import Enum
88
from typing import Any
99

@@ -2547,7 +2547,7 @@ def events_to_operations(events: list[Event]) -> list[Operation]:
25472547
# Merge with previous operation if it exists
25482548
# Most fields are immutable, so they get preserved from previous events
25492549
if previous_operation:
2550-
operation = replace(
2550+
operation = Operation.from_existing(
25512551
operation,
25522552
name=operation.name or previous_operation.name,
25532553
parent_id=operation.parent_id or previous_operation.parent_id,
@@ -2564,9 +2564,13 @@ def events_to_operations(events: list[Event]) -> list[Operation]:
25642564

25652565
# Set timestamps based on event configuration
25662566
if event_config.is_start_event:
2567-
operation = replace(operation, start_timestamp=event.event_timestamp)
2567+
operation = Operation.from_existing(
2568+
operation, start_timestamp=event.event_timestamp
2569+
)
25682570
if event_config.is_end_event:
2569-
operation = replace(operation, end_timestamp=event.event_timestamp)
2571+
operation = Operation.from_existing(
2572+
operation, end_timestamp=event.event_timestamp
2573+
)
25702574

25712575
# Add operation-specific details incrementally
25722576
# Each event type contributes only the fields it has
@@ -2577,7 +2581,7 @@ def events_to_operations(events: list[Event]) -> list[Operation]:
25772581
and event.execution_started_details
25782582
and event.execution_started_details.input
25792583
):
2580-
operation = replace(
2584+
operation = Operation.from_existing(
25812585
operation,
25822586
execution_details=ExecutionDetails(
25832587
input_payload=event.execution_started_details.input.payload
@@ -2613,7 +2617,7 @@ def events_to_operations(events: list[Event]) -> list[Operation]:
26132617
):
26142618
error = event.callback_timed_out_details.error.payload
26152619

2616-
operation = replace(
2620+
operation = Operation.from_existing(
26172621
operation,
26182622
callback_details=CallbackDetails(
26192623
callback_id=callback_id,
@@ -2655,7 +2659,7 @@ def events_to_operations(events: list[Event]) -> list[Operation]:
26552659
seconds=event.step_failed_details.retry_details.next_attempt_delay_seconds
26562660
)
26572661

2658-
operation = replace(
2662+
operation = Operation.from_existing(
26592663
operation,
26602664
step_details=StepDetails(
26612665
result=result_val,
@@ -2667,7 +2671,7 @@ def events_to_operations(events: list[Event]) -> list[Operation]:
26672671

26682672
# WAIT details
26692673
if operation_type == OperationType.WAIT and event.wait_started_details:
2670-
operation = replace(
2674+
operation = Operation.from_existing(
26712675
operation,
26722676
wait_details=WaitDetails(
26732677
scheduled_end_timestamp=event.wait_started_details.scheduled_end_timestamp
@@ -2680,15 +2684,15 @@ def events_to_operations(events: list[Event]) -> list[Operation]:
26802684
event.context_succeeded_details
26812685
and event.context_succeeded_details.result
26822686
):
2683-
operation = replace(
2687+
operation = Operation.from_existing(
26842688
operation,
26852689
context_details=ContextDetails(
26862690
result=event.context_succeeded_details.result.payload,
26872691
error=None,
26882692
),
26892693
)
26902694
elif event.context_failed_details and event.context_failed_details.error:
2691-
operation = replace(
2695+
operation = Operation.from_existing(
26922696
operation,
26932697
context_details=ContextDetails(
26942698
result=None,
@@ -2702,7 +2706,7 @@ def events_to_operations(events: list[Event]) -> list[Operation]:
27022706
event.chained_invoke_succeeded_details
27032707
and event.chained_invoke_succeeded_details.result
27042708
):
2705-
operation = replace(
2709+
operation = Operation.from_existing(
27062710
operation,
27072711
chained_invoke_details=ChainedInvokeDetails(
27082712
result=event.chained_invoke_succeeded_details.result.payload,
@@ -2713,7 +2717,7 @@ def events_to_operations(events: list[Event]) -> list[Operation]:
27132717
event.chained_invoke_failed_details
27142718
and event.chained_invoke_failed_details.error
27152719
):
2716-
operation = replace(
2720+
operation = Operation.from_existing(
27172721
operation,
27182722
chained_invoke_details=ChainedInvokeDetails(
27192723
result=None,

0 commit comments

Comments
 (0)