1313from ..exceptions import TransitionNotAllowed
1414from ..orderedset import OrderedSet
1515from ..state import State
16+ from .base import _ERROR_EXECUTION
1617from .base import BaseEngine
1718
1819if TYPE_CHECKING :
@@ -178,6 +179,7 @@ async def _exit_states( # type: ignore[override]
178179 args , kwargs = await self ._get_args_kwargs (info .transition , trigger_data )
179180
180181 if info .state is not None : # pragma: no branch
182+ logger .debug ("%s Exiting state: %s" , self ._log_id , info .state )
181183 await self .sm ._callbacks .async_call (
182184 info .state .exit .key , * args , on_error = on_error , ** kwargs
183185 )
@@ -198,10 +200,24 @@ async def _enter_states( # noqa: C901
198200 self ._prepare_entry_states (enabled_transitions , states_to_exit , previous_configuration )
199201 )
200202
203+ # For transition 'on' content, use on_error only for non-error.execution
204+ # events. During error.execution processing, errors in transition content
205+ # must propagate to microstep() where _send_error_execution's guard
206+ # prevents infinite loops (per SCXML spec: errors during error event
207+ # processing are ignored).
208+ on_error_transition = on_error
209+ if (
210+ on_error is not None
211+ and trigger_data .event
212+ and str (trigger_data .event ) == _ERROR_EXECUTION
213+ ):
214+ on_error_transition = None
215+
201216 result = await self ._execute_transition_content (
202217 enabled_transitions ,
203218 trigger_data ,
204219 lambda t : t .on .key ,
220+ on_error = on_error_transition ,
205221 previous_configuration = previous_configuration ,
206222 new_configuration = new_configuration ,
207223 )
@@ -218,7 +234,7 @@ async def _enter_states( # noqa: C901
218234 target = target ,
219235 )
220236
221- logger .debug ("Entering state: %s" , target )
237+ logger .debug ("%s Entering state: %s" , self . _log_id , target )
222238 self ._add_state_to_configuration (target )
223239
224240 on_entry_result = await self .sm ._callbacks .async_call (
@@ -257,6 +273,14 @@ async def _enter_states( # noqa: C901
257273 return result
258274
259275 async def microstep (self , transitions : "List[Transition]" , trigger_data : TriggerData ):
276+ self ._microstep_count += 1
277+ logger .debug (
278+ "%s macro:%d micro:%d transitions: %s" ,
279+ self ._log_id ,
280+ self ._macrostep_count ,
281+ self ._microstep_count ,
282+ transitions ,
283+ )
260284 previous_configuration = self .sm .configuration
261285 try :
262286 result = await self ._execute_transition_content (
@@ -342,7 +366,7 @@ async def processing_loop( # noqa: C901
342366 return None
343367
344368 _ctx_token = _in_processing_loop .set (True )
345- logger .debug ("Processing loop started: %s" , self .sm .current_state_value )
369+ logger .debug ("%s Processing loop started: %s" , self . _log_id , self .sm .current_state_value )
346370 first_result = self ._sentinel
347371 try :
348372 took_events = True
@@ -353,7 +377,12 @@ async def processing_loop( # noqa: C901
353377
354378 # Phase 1: eventless transitions and internal events
355379 while not macrostep_done :
356- logger .debug ("Macrostep: eventless/internal queue" )
380+ self ._microstep_count = 0
381+ logger .debug (
382+ "%s Macrostep %d: eventless/internal queue" ,
383+ self ._log_id ,
384+ self ._macrostep_count ,
385+ )
357386
358387 self .clear_cache ()
359388 internal_event = TriggerData (self .sm , event = None ) # null object for eventless
@@ -365,7 +394,9 @@ async def processing_loop( # noqa: C901
365394 internal_event = self .internal_queue .pop ()
366395 enabled_transitions = await self .select_transitions (internal_event )
367396 if enabled_transitions :
368- logger .debug ("Enabled transitions: %s" , enabled_transitions )
397+ logger .debug (
398+ "%s Enabled transitions: %s" , self ._log_id , enabled_transitions
399+ )
369400 took_events = True
370401 await self ._run_microstep (enabled_transitions , internal_event )
371402
@@ -380,7 +411,9 @@ async def processing_loop( # noqa: C901
380411 await self ._run_microstep (enabled_transitions , internal_event )
381412
382413 # Phase 3: external events
383- logger .debug ("Macrostep: external queue" )
414+ logger .debug (
415+ "%s Macrostep %d: external queue" , self ._log_id , self ._macrostep_count
416+ )
384417 while not self .external_queue .is_empty ():
385418 self .clear_cache ()
386419 took_events = True
@@ -393,7 +426,14 @@ async def processing_loop( # noqa: C901
393426 # transitions can be processed while we wait.
394427 break
395428
396- logger .debug ("External event: %s" , external_event .event )
429+ self ._macrostep_count += 1
430+ self ._microstep_count = 0
431+ logger .debug (
432+ "%s macrostep %d: event=%s" ,
433+ self ._log_id ,
434+ self ._macrostep_count ,
435+ external_event .event ,
436+ )
397437
398438 # Handle lazy initial state activation.
399439 # Break out of phase 3 so the outer loop restarts from phase 1
@@ -412,7 +452,9 @@ async def processing_loop( # noqa: C901
412452 event_future = external_event .future
413453 try :
414454 enabled_transitions = await self .select_transitions (external_event )
415- logger .debug ("Enabled transitions: %s" , enabled_transitions )
455+ logger .debug (
456+ "%s Enabled transitions: %s" , self ._log_id , enabled_transitions
457+ )
416458 if enabled_transitions :
417459 result = await self .microstep (
418460 list (enabled_transitions ), external_event
@@ -451,6 +493,7 @@ async def processing_loop( # noqa: C901
451493 _in_processing_loop .reset (_ctx_token )
452494 self ._processing .release ()
453495
496+ logger .debug ("%s Processing loop ended" , self ._log_id )
454497 result = first_result if first_result is not self ._sentinel else None
455498 # If the caller has a future, await it (already resolved by now).
456499 if caller_future is not None :
0 commit comments