1+ import dataclasses
12import logging
23import types
34import typing
@@ -102,9 +103,7 @@ def __init__(
102103 container ,
103104 self ._state_manager ,
104105 )
105- self ._fallback_executor : FallbackStepExecutor [ContextT ] = FallbackStepExecutor [
106- ContextT
107- ](
106+ self ._fallback_executor : FallbackStepExecutor [ContextT ] = FallbackStepExecutor [ContextT ](
108107 context ,
109108 container ,
110109 self ._state_manager ,
@@ -145,11 +144,7 @@ async def __aexit__(
145144 # If an exception occurred, compensate all completed steps.
146145 # Do not compensate on GeneratorExit: consumer stopped iteration intentionally
147146 # (e.g. to resume later), which is not a failure.
148- if (
149- exc_val is not None
150- and exc_type is not GeneratorExit
151- and not self ._compensated
152- ):
147+ if exc_val is not None and exc_type is not GeneratorExit and not self ._compensated :
153148 self ._error = exc_val
154149 await self ._compensate ()
155150 return False # Don't suppress the exception
@@ -213,25 +208,19 @@ async def __aiter__(
213208 # POINT OF NO RETURN: Strict Backward Recovery Strategy
214209 if status in (SagaStatus .COMPENSATING , SagaStatus .FAILED ):
215210 logger .warning (
216- f"Saga { self ._saga_id } is in { status } state. "
217- "Resuming compensation immediately." ,
211+ f"Saga { self ._saga_id } is in { status } state. " "Resuming compensation immediately." ,
218212 )
219213
220214 # Restore completed steps from history for compensation
221- completed_act_steps = (
222- await self ._recovery_manager .load_completed_step_names ()
223- )
224- reconstructed_steps = (
225- await self ._recovery_manager .reconstruct_completed_steps (
226- completed_act_steps ,
227- )
215+ completed_act_steps = await self ._recovery_manager .load_completed_step_names ()
216+ reconstructed_steps = await self ._recovery_manager .reconstruct_completed_steps (
217+ completed_act_steps ,
228218 )
229219 # Type cast is safe here because steps are reconstructed from the same saga
230220 # that uses ContextT, so they have the correct context type
231221 # We need to rebuild the list to satisfy type checker's invariance requirements
232222 self ._completed_steps = [
233- typing .cast (SagaStepHandler [ContextT , typing .Any ], step )
234- for step in reconstructed_steps
223+ typing .cast (SagaStepHandler [ContextT , typing .Any ], step ) for step in reconstructed_steps
235224 ]
236225
237226 if not self ._completed_steps :
@@ -252,9 +241,7 @@ async def __aiter__(
252241 )
253242
254243 # For RUNNING/PENDING status, load history to skip completed steps
255- completed_step_names = (
256- await self ._recovery_manager .load_completed_step_names ()
257- )
244+ completed_step_names = await self ._recovery_manager .load_completed_step_names ()
258245 except ValueError :
259246 # If loading fails but ID was provided, create it
260247 await self ._state_manager .create_saga (
@@ -278,7 +265,10 @@ async def __aiter__(
278265 if step_result is not None and executed_step is not None :
279266 # Track completed step for compensation
280267 self ._completed_steps .append (executed_step )
281- yield step_result
268+ yield dataclasses .replace (
269+ step_result ,
270+ saga_id = self ._saga_id ,
271+ )
282272 elif executed_step is None :
283273 # Step was skipped (already completed), restore it for compensation
284274 primary_name = step_item .step .__name__
@@ -313,7 +303,10 @@ async def __aiter__(
313303 step = await self ._container .resolve (step_type )
314304 self ._completed_steps .append (step )
315305
316- yield step_result
306+ yield dataclasses .replace (
307+ step_result ,
308+ saga_id = self ._saga_id ,
309+ )
317310
318311 # Update context one final time before marking as completed
319312 await self ._state_manager .update_context (self ._context )
0 commit comments