Module documentation
Public imports
The top-level package intentionally exports only the classic core runtime:
StateMachine, State, Event, StateMachineException, Stack,
any_event, AnyEvent, logger, __version__, and
__version_info__.
Optional modules are documented below, but they are not imported by
import pysm. Import them explicitly when you need their behavior.
Core
Python State Machine
The goal of this library is to give you a close to the State Pattern simplicity with much more flexibility. And, if needed, the full state machine functionality, including FSM, HSM, PDA and other tasty things.
- Goals:
Provide a State Pattern-like behavior with more flexibility
Be explicit and don’t add any code to objects
Handle directly any kind of event (not only strings) - parsing strings is cool again!
Keep it simple, even for someone who’s not very familiar with the FSM terminology
- class pysm.pysm.AnyEvent[source]
Bases:
objecthash(object()) doesn’t work in MicroPython therefore the need for this class.
- exception pysm.pysm.StateMachineException[source]
Bases:
ExceptionAll
StateMachineexceptions are of this type.- add_note(object, /)
Exception.add_note(note) – add a note to the exception
- with_traceback(object, /)
Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
- class pysm.pysm.Event(name, input=None, **cargo)[source]
Bases:
objectTriggers actions and transition in
StateMachine.Events are also used to control the flow of data propagated to states within the states hierarchy.
Event objects have the following attributes set after an event has been dispatched:
Attributes:
- state_machine
A
StateMachineinstance that is handling the event (the one whosepysm.pysm.StateMachine.dispatch()method is called)
- propagate
An event is propagated from a current leaf state up in the states hierarchy until it encounters a handler that can handle the event. To propagate it further, it has to be set to True in a handler.
- Parameters:
name (
Hashable) – Name of an event. It may be anything as long as it’s hashable.input (
Hashable) – Optional input. Anything hashable.**cargo – Keyword arguments for an event, used to transport data to handlers. It’s added to an event as a cargo property of type dict. For enter and exit events, the original event that triggered a transition is passed in cargo as source_event entry.
Example Usage:
state_machine.dispatch(Event('start')) state_machine.dispatch(Event('start', key='value')) state_machine.dispatch(Event('parse', input='#', entity=my_object)) state_machine.dispatch(Event('%')) state_machine.dispatch(Event(frozenset([1, 2])))
- class pysm.pysm.State(name)[source]
Bases:
objectRepresents a state in a state machine.
enter and exit handlers are called whenever a state is entered or exited respectively. These action names are reserved only for this purpose.
It is encouraged to extend this class to encapsulate a state behavior, similarly to the State Pattern.
Once it’s extended, the preferred way of adding an event handlers is through the
register_handlers()hook. Usually, there’s no need to create the__init__()in a subclass.- Parameters:
name (str) – Human readable state name
Example Usage:
# Extending State to encapsulate state-related behavior. Similar to the # State Pattern. class Running(State): def on_enter(self, state, event): print('Running state entered') def on_jump(self, state, event): print('Jumping') def on_dollar(self, state, event): print('Dollar found!') def register_handlers(self): self.handlers = { 'enter': self.on_enter, 'jump': self.on_jump, '$': self.on_dollar }
# Different way of attaching handlers. A handler may be any function as # long as it takes `state` and `event` args. def another_handler(state, event): print('Another handler') running = State('running') running.handlers = { 'another_event': another_handler }
- register_handlers()[source]
Hook method to register event handlers.
It is used to easily extend
Stateclass. The hook is called from within the baseState.__init__(). Usually, the__init__()doesn’t have to be created in a subclass.Event handlers are kept in a dict, with events’ names as keys, therefore registered events may be of any hashable type.
Handlers take two arguments:
- state: The current state that is handling an event. The same
handler function may be attached to many states, therefore it is helpful to get the handling state’s instance.
- event: An event that triggered the handler call. If it is an
enter or exit event, then the source event (the one that triggered the transition) is passed in event’s cargo property as cargo.source_event.
Example Usage:
class On(State): def handle_my_event(self, state, event): print('Handling an event') def register_handlers(self): self.handlers = { 'my_event': self.handle_my_event, '&': self.handle_my_event, frozenset([1, 2]): self.handle_my_event }
- class pysm.pysm.StateMachine(name)[source]
Bases:
StateState machine controls actions and transitions.
To provide the State Pattern-like behavior, the formal state machine rules may be slightly broken, and instead of creating an internal transition for every action that doesn’t require a state change, event handlers may be added to states. These are handled first when an event occurs. After that the actual transition is called, calling enter/exit actions and other transition actions. Nevertheless, internal transitions are also supported.
So the order of calls on an event is as follows:
State’s event handler
condition callback
before callback
exit handlers
action callback
enter handlers
after callback
If there’s no handler in states or transition for an event, it is silently ignored.
If using nested state machines, all events should be sent to the root state machine.
Attributes:
- stack
Stack that can be used if the Pushdown Automaton (PDA) functionality is needed.
- state_stack
Stack of previous local states in a state machine. With every transition, a previous state (instance of
State) is pushed to the state_stack. OnlyStateMachine.STACK_SIZE(32 by default) are stored and old values are removed from the stack.
- leaf_state_stack
Stack of previous leaf states in a state machine. With every transition, a previous leaf state (instance of
State) is pushed to the leaf_state_stack. OnlyStateMachine.STACK_SIZE(32 by default) are stored and old values are removed from the stack.
- leaf_state
See the
leaf_stateproperty.- root_machine
See the
root_machineproperty.
- Parameters:
name (str) – Human readable state machine name
Note
StateMachineextendsStateand therefore it is possible to always use aStateMachineinstance instead of theState. This wouldn’t be a good practice though, as theStateclass is designed to be as small as possible memory-wise and thus it’s more memory efficient. It is valid to replace aStatewith aStateMachinelater on if there’s a need to extend a state with internal states.Note
For the sake of speed thread safety isn’t guaranteed.
Example Usage:
state_machine = StateMachine('root_machine') state_on = State('On') state_off = State('Off') state_machine.add_state('Off', initial=True) state_machine.add_state('On') state_machine.add_transition(state_on, state_off, events=['off']) state_machine.add_transition(state_off, state_on, events=['on']) state_machine.initialize() state_machine.dispatch(Event('on'))
- add_state(state, initial=False)[source]
Add a state to a state machine.
If states are added, one (and only one) of them has to be declared as initial.
- Parameters:
state (
State) – State to be added. It may be an anotherStateMachineinitial (bool) – Declare a state as initial
- add_states(*states)[source]
Add states to the
StateMachine.To set the initial state use
set_initial_state().- Parameters:
states (
State) – A list of states to be added
- set_initial_state(state)[source]
Set an initial state in a state machine.
- Parameters:
state (
State) – Set this state as initial in a state machine
- property initial_state
Get the initial state in a state machine.
- Returns:
Initial state in a state machine
- Return type:
- property root_machine
Get the root state machine in a states hierarchy.
- Returns:
Root state in the states hierarchy
- Return type:
- add_transition(from_state, to_state, events, input=None, action=None, condition=None, before=None, after=None)[source]
Add a transition to a state machine.
All callbacks take two arguments - state and event. See parameters description for details.
It is possible to create conditional if/elif/else-like logic for transitions. To do so, add many same transition rules with different condition callbacks. First met condition will trigger a transition, if no condition is met, no transition is performed.
- Parameters:
from_state (
State) – Source stateto_state (
State, None) –Target state. If None, then it’s an internal transition
events (
IterableofHashable) – List of events that trigger the transitioninput (None,
IterableofHashable) – List of inputs that trigger the transition. A transition event may be associated with a specific input. i.e.: An event may beparseand an input associated with it may be$. May be None (default), then every matched event name triggers a transition.action (
Callable) –Action callback that is called during the transition after all states have been left but before the new one is entered.
action callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
condition (
Callable) –Condition callback - if returns True transition may be initiated.
condition callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
before (
Callable) –Action callback that is called right before the transition.
before callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
after (
Callable) –Action callback that is called just after the transition
after callback takes two arguments:
state: Leaf state after transition
event: Event that triggered the transition
- property leaf_state
Get the current leaf state.
The
stateproperty gives the current, local state in a state machine. The leaf_state goes to the bottom in a hierarchy of states. In most cases, this is the property that should be used to get the current state in a state machine, even in a flat FSM, to keep the consistency in the code and to avoid confusion.- Returns:
Leaf state in a hierarchical state machine
- Return type:
- initialize(fire_events_on_init=False)[source]
Initialize states in the state machine.
After a state machine has been created and all states are added to it,
initialize()has to be called.If using nested state machines (HSM),
initialize()has to be called on a root state machine in the hierarchy.
- dispatch(event)[source]
Dispatch an event to a state machine.
If using nested state machines (HSM), it has to be called on a root state machine in the hierarchy.
- Parameters:
event (
Event) – Event to be dispatched
- set_previous_leaf_state(event=None)[source]
Transition to a previous leaf state. This makes a dynamic transition to a historical state. The current leaf_state is saved on the stack of historical leaf states when calling this method.
- Parameters:
event (
Event) – (Optional) event that is passed to states involved in the transition
- revert_to_previous_leaf_state(event=None)[source]
Similar to
set_previous_leaf_state()but the current leaf_state is not saved on the stack of states. It allows to perform transitions further in the history of states.
- is_substate(state)
Check whether the state is a substate of self.
Also self is considered a substate of self.
- register_handlers()
Hook method to register event handlers.
It is used to easily extend
Stateclass. The hook is called from within the baseState.__init__(). Usually, the__init__()doesn’t have to be created in a subclass.Event handlers are kept in a dict, with events’ names as keys, therefore registered events may be of any hashable type.
Handlers take two arguments:
- state: The current state that is handling an event. The same
handler function may be attached to many states, therefore it is helpful to get the handling state’s instance.
- event: An event that triggered the handler call. If it is an
enter or exit event, then the source event (the one that triggered the transition) is passed in event’s cargo property as cargo.source_event.
Example Usage:
class On(State): def handle_my_event(self, state, event): print('Handling an event') def register_handlers(self): self.handlers = { 'my_event': self.handle_my_event, '&': self.handle_my_event, frozenset([1, 2]): self.handle_my_event }
Queued runtime
Optional queued execution layers for pysm.
The classes in this module deliberately live outside pysm.__init__ so the
classic core import remains tiny and suitable for MicroPython-oriented builds.
- class pysm.queued.QueuedStateMachine(name, max_internal_steps=None)[source]
Bases:
StateMachineState machine with run-to-completion event scheduling.
External events enter an external FIFO queue. Events dispatched while the machine is already processing enter a separate internal FIFO queue. The internal queue is always drained before the next external event is handled.
- initialize(fire_events_on_init=False)[source]
Initialize states in the state machine.
After a state machine has been created and all states are added to it,
initialize()has to be called.If using nested state machines (HSM),
initialize()has to be called on a root state machine in the hierarchy.
- add_state(state, initial=False)
Add a state to a state machine.
If states are added, one (and only one) of them has to be declared as initial.
- Parameters:
state (
State) – State to be added. It may be an anotherStateMachineinitial (bool) – Declare a state as initial
- add_states(*states)
Add states to the
StateMachine.To set the initial state use
set_initial_state().- Parameters:
states (
State) – A list of states to be added
- add_transition(from_state, to_state, events, input=None, action=None, condition=None, before=None, after=None)
Add a transition to a state machine.
All callbacks take two arguments - state and event. See parameters description for details.
It is possible to create conditional if/elif/else-like logic for transitions. To do so, add many same transition rules with different condition callbacks. First met condition will trigger a transition, if no condition is met, no transition is performed.
- Parameters:
from_state (
State) – Source stateto_state (
State, None) –Target state. If None, then it’s an internal transition
events (
IterableofHashable) – List of events that trigger the transitioninput (None,
IterableofHashable) – List of inputs that trigger the transition. A transition event may be associated with a specific input. i.e.: An event may beparseand an input associated with it may be$. May be None (default), then every matched event name triggers a transition.action (
Callable) –Action callback that is called during the transition after all states have been left but before the new one is entered.
action callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
condition (
Callable) –Condition callback - if returns True transition may be initiated.
condition callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
before (
Callable) –Action callback that is called right before the transition.
before callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
after (
Callable) –Action callback that is called just after the transition
after callback takes two arguments:
state: Leaf state after transition
event: Event that triggered the transition
- property initial_state
Get the initial state in a state machine.
- Returns:
Initial state in a state machine
- Return type:
- is_substate(state)
Check whether the state is a substate of self.
Also self is considered a substate of self.
- property leaf_state
Get the current leaf state.
The
stateproperty gives the current, local state in a state machine. The leaf_state goes to the bottom in a hierarchy of states. In most cases, this is the property that should be used to get the current state in a state machine, even in a flat FSM, to keep the consistency in the code and to avoid confusion.- Returns:
Leaf state in a hierarchical state machine
- Return type:
- register_handlers()
Hook method to register event handlers.
It is used to easily extend
Stateclass. The hook is called from within the baseState.__init__(). Usually, the__init__()doesn’t have to be created in a subclass.Event handlers are kept in a dict, with events’ names as keys, therefore registered events may be of any hashable type.
Handlers take two arguments:
- state: The current state that is handling an event. The same
handler function may be attached to many states, therefore it is helpful to get the handling state’s instance.
- event: An event that triggered the handler call. If it is an
enter or exit event, then the source event (the one that triggered the transition) is passed in event’s cargo property as cargo.source_event.
Example Usage:
class On(State): def handle_my_event(self, state, event): print('Handling an event') def register_handlers(self): self.handlers = { 'my_event': self.handle_my_event, '&': self.handle_my_event, frozenset([1, 2]): self.handle_my_event }
- revert_to_previous_leaf_state(event=None)
Similar to
set_previous_leaf_state()but the current leaf_state is not saved on the stack of states. It allows to perform transitions further in the history of states.
- property root_machine
Get the root state machine in a states hierarchy.
- Returns:
Root state in the states hierarchy
- Return type:
- set_initial_state(state)
Set an initial state in a state machine.
- Parameters:
state (
State) – Set this state as initial in a state machine
- set_previous_leaf_state(event=None)
Transition to a previous leaf state. This makes a dynamic transition to a historical state. The current leaf_state is saved on the stack of historical leaf states when calling this method.
- Parameters:
event (
Event) – (Optional) event that is passed to states involved in the transition
- class pysm.queued.ThreadSafeQueuedStateMachine(name, max_internal_steps=None)[source]
Bases:
QueuedStateMachineQueued state machine protected by a reentrant execution lock.
Long-running or blocking handlers hold the lock until the current run-to-completion cycle finishes. Async execution is intentionally kept out of this class.
- initialize(fire_events_on_init=False)[source]
Initialize states in the state machine.
After a state machine has been created and all states are added to it,
initialize()has to be called.If using nested state machines (HSM),
initialize()has to be called on a root state machine in the hierarchy.
- add_state(state, initial=False)
Add a state to a state machine.
If states are added, one (and only one) of them has to be declared as initial.
- Parameters:
state (
State) – State to be added. It may be an anotherStateMachineinitial (bool) – Declare a state as initial
- add_states(*states)
Add states to the
StateMachine.To set the initial state use
set_initial_state().- Parameters:
states (
State) – A list of states to be added
- add_transition(from_state, to_state, events, input=None, action=None, condition=None, before=None, after=None)
Add a transition to a state machine.
All callbacks take two arguments - state and event. See parameters description for details.
It is possible to create conditional if/elif/else-like logic for transitions. To do so, add many same transition rules with different condition callbacks. First met condition will trigger a transition, if no condition is met, no transition is performed.
- Parameters:
from_state (
State) – Source stateto_state (
State, None) –Target state. If None, then it’s an internal transition
events (
IterableofHashable) – List of events that trigger the transitioninput (None,
IterableofHashable) – List of inputs that trigger the transition. A transition event may be associated with a specific input. i.e.: An event may beparseand an input associated with it may be$. May be None (default), then every matched event name triggers a transition.action (
Callable) –Action callback that is called during the transition after all states have been left but before the new one is entered.
action callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
condition (
Callable) –Condition callback - if returns True transition may be initiated.
condition callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
before (
Callable) –Action callback that is called right before the transition.
before callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
after (
Callable) –Action callback that is called just after the transition
after callback takes two arguments:
state: Leaf state after transition
event: Event that triggered the transition
- property initial_state
Get the initial state in a state machine.
- Returns:
Initial state in a state machine
- Return type:
- is_substate(state)
Check whether the state is a substate of self.
Also self is considered a substate of self.
- property leaf_state
Get the current leaf state.
The
stateproperty gives the current, local state in a state machine. The leaf_state goes to the bottom in a hierarchy of states. In most cases, this is the property that should be used to get the current state in a state machine, even in a flat FSM, to keep the consistency in the code and to avoid confusion.- Returns:
Leaf state in a hierarchical state machine
- Return type:
- register_handlers()
Hook method to register event handlers.
It is used to easily extend
Stateclass. The hook is called from within the baseState.__init__(). Usually, the__init__()doesn’t have to be created in a subclass.Event handlers are kept in a dict, with events’ names as keys, therefore registered events may be of any hashable type.
Handlers take two arguments:
- state: The current state that is handling an event. The same
handler function may be attached to many states, therefore it is helpful to get the handling state’s instance.
- event: An event that triggered the handler call. If it is an
enter or exit event, then the source event (the one that triggered the transition) is passed in event’s cargo property as cargo.source_event.
Example Usage:
class On(State): def handle_my_event(self, state, event): print('Handling an event') def register_handlers(self): self.handlers = { 'my_event': self.handle_my_event, '&': self.handle_my_event, frozenset([1, 2]): self.handle_my_event }
- revert_to_previous_leaf_state(event=None)
Similar to
set_previous_leaf_state()but the current leaf_state is not saved on the stack of states. It allows to perform transitions further in the history of states.
- property root_machine
Get the root state machine in a states hierarchy.
- Returns:
Root state in the states hierarchy
- Return type:
- set_initial_state(state)
Set an initial state in a state machine.
- Parameters:
state (
State) – Set this state as initial in a state machine
- set_previous_leaf_state(event=None)
Transition to a previous leaf state. This makes a dynamic transition to a historical state. The current leaf_state is saved on the stack of historical leaf states when calling this method.
- Parameters:
event (
Event) – (Optional) event that is passed to states involved in the transition
Async runtime
Optional asyncio execution layer for pysm.
This module is intentionally not imported from pysm.__init__. It requires
CPython’s asyncio runtime and keeps the classic core import untouched.
- class pysm.aio.AsyncQueuedStateMachine(name, max_internal_steps=None)[source]
Bases:
StateMachineAsync state machine with run-to-completion event scheduling.
The machine is intended to be used from one asyncio event loop. External idle dispatches are serialized with an
asyncio.Lock. Events dispatched from the currently running transition task are queued as internal events. Events dispatched by other tasks while the machine is already processing are queued as external events and return after enqueueing.- initialize(fire_events_on_init=False)[source]
Initialize states in the state machine.
After a state machine has been created and all states are added to it,
initialize()has to be called.If using nested state machines (HSM),
initialize()has to be called on a root state machine in the hierarchy.
- async async_initialize(fire_events_on_init=False)[source]
Initialize and optionally await entry handlers on the initial path.
- async set_previous_leaf_state(event=None)[source]
Async version of
StateMachine.set_previous_leaf_state.
- async revert_to_previous_leaf_state(event=None)[source]
Async version of
StateMachine.revert_to_previous_leaf_state.
- add_state(state, initial=False)
Add a state to a state machine.
If states are added, one (and only one) of them has to be declared as initial.
- Parameters:
state (
State) – State to be added. It may be an anotherStateMachineinitial (bool) – Declare a state as initial
- add_states(*states)
Add states to the
StateMachine.To set the initial state use
set_initial_state().- Parameters:
states (
State) – A list of states to be added
- add_transition(from_state, to_state, events, input=None, action=None, condition=None, before=None, after=None)
Add a transition to a state machine.
All callbacks take two arguments - state and event. See parameters description for details.
It is possible to create conditional if/elif/else-like logic for transitions. To do so, add many same transition rules with different condition callbacks. First met condition will trigger a transition, if no condition is met, no transition is performed.
- Parameters:
from_state (
State) – Source stateto_state (
State, None) –Target state. If None, then it’s an internal transition
events (
IterableofHashable) – List of events that trigger the transitioninput (None,
IterableofHashable) – List of inputs that trigger the transition. A transition event may be associated with a specific input. i.e.: An event may beparseand an input associated with it may be$. May be None (default), then every matched event name triggers a transition.action (
Callable) –Action callback that is called during the transition after all states have been left but before the new one is entered.
action callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
condition (
Callable) –Condition callback - if returns True transition may be initiated.
condition callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
before (
Callable) –Action callback that is called right before the transition.
before callback takes two arguments:
state: Leaf state before transition
event: Event that triggered the transition
after (
Callable) –Action callback that is called just after the transition
after callback takes two arguments:
state: Leaf state after transition
event: Event that triggered the transition
- property initial_state
Get the initial state in a state machine.
- Returns:
Initial state in a state machine
- Return type:
- is_substate(state)
Check whether the state is a substate of self.
Also self is considered a substate of self.
- property leaf_state
Get the current leaf state.
The
stateproperty gives the current, local state in a state machine. The leaf_state goes to the bottom in a hierarchy of states. In most cases, this is the property that should be used to get the current state in a state machine, even in a flat FSM, to keep the consistency in the code and to avoid confusion.- Returns:
Leaf state in a hierarchical state machine
- Return type:
- register_handlers()
Hook method to register event handlers.
It is used to easily extend
Stateclass. The hook is called from within the baseState.__init__(). Usually, the__init__()doesn’t have to be created in a subclass.Event handlers are kept in a dict, with events’ names as keys, therefore registered events may be of any hashable type.
Handlers take two arguments:
- state: The current state that is handling an event. The same
handler function may be attached to many states, therefore it is helpful to get the handling state’s instance.
- event: An event that triggered the handler call. If it is an
enter or exit event, then the source event (the one that triggered the transition) is passed in event’s cargo property as cargo.source_event.
Example Usage:
class On(State): def handle_my_event(self, state, event): print('Handling an event') def register_handlers(self): self.handlers = { 'my_event': self.handle_my_event, '&': self.handle_my_event, frozenset([1, 2]): self.handle_my_event }
- property root_machine
Get the root state machine in a states hierarchy.
- Returns:
Root state in the states hierarchy
- Return type:
Serialization
Optional snapshot/restore helpers for pysm state machines.
- pysm.serialization.snapshot(machine, metadata=None)[source]
Return a plain-data snapshot of a configured state machine graph.
- pysm.serialization.restore(machine, data)[source]
Restore
machinefrom a snapshot created bysnapshot().The machine graph must already be constructed and initialized. Restore is strict about topology so stale snapshots fail loudly instead of silently restoring to the wrong state.
Builder
Optional fluent builder for pysm state machines.