@@ -75,10 +75,15 @@ def __init__(self, sm: "StateMachine"):
7575 self ._sentinel = object ()
7676 self .running = True
7777 self ._processing = Lock ()
78+ self ._cache : Dict = {} # Cache for _get_args_kwargs results
7879
7980 def empty (self ):
8081 return self .external_queue .is_empty ()
8182
83+ def clear_cache (self ):
84+ """Clears the cache. Should be called at the start of each processing loop."""
85+ self ._cache .clear ()
86+
8287 def put (self , trigger_data : TriggerData , internal : bool = False , _delayed : bool = False ):
8388 """Put the trigger on the queue without blocking the caller."""
8489 if not self .running and not self .sm .allow_event_without_transition :
@@ -310,7 +315,13 @@ def microstep(self, transitions: List[Transition], trigger_data: TriggerData):
310315 def _get_args_kwargs (
311316 self , transition : Transition , trigger_data : TriggerData , target : "State | None" = None
312317 ):
313- # TODO: Ideally this method should be called only once per microstep/transition
318+ # Generate a unique key for the cache, the cache is invalidated once per loop
319+ cache_key = (id (transition ), id (trigger_data ), id (target ))
320+
321+ # Check the cache for existing results
322+ if cache_key in self ._cache :
323+ return self ._cache [cache_key ]
324+
314325 event_data = EventData (trigger_data = trigger_data , transition = transition )
315326 if target :
316327 event_data .state = target
@@ -321,6 +332,9 @@ def _get_args_kwargs(
321332 result = self .sm ._callbacks .call (self .sm .prepare .key , * args , ** kwargs )
322333 for new_kwargs in result :
323334 kwargs .update (new_kwargs )
335+
336+ # Store the result in the cache
337+ self ._cache [cache_key ] = (args , kwargs )
324338 return args , kwargs
325339
326340 def _conditions_match (self , transition : Transition , trigger_data : TriggerData ):
@@ -329,7 +343,9 @@ def _conditions_match(self, transition: Transition, trigger_data: TriggerData):
329343 self .sm ._callbacks .call (transition .validators .key , * args , ** kwargs )
330344 return self .sm ._callbacks .all (transition .cond .key , * args , ** kwargs )
331345
332- def _exit_states (self , enabled_transitions : List [Transition ], trigger_data : TriggerData ):
346+ def _exit_states (
347+ self , enabled_transitions : List [Transition ], trigger_data : TriggerData
348+ ) -> OrderedSet [State ]:
333349 """Compute and process the states to exit for the given transitions."""
334350 states_to_exit = self ._compute_exit_set (enabled_transitions )
335351
@@ -340,7 +356,7 @@ def _exit_states(self, enabled_transitions: List[Transition], trigger_data: Trig
340356 ordered_states = sorted (
341357 states_to_exit , key = lambda x : x .source and x .source .document_order or 0 , reverse = True
342358 )
343- result = OrderedSet ([info .source for info in ordered_states ])
359+ result = OrderedSet ([info .source for info in ordered_states if info . source ])
344360 logger .debug ("States to exit: %s" , result )
345361
346362 for info in ordered_states :
0 commit comments