Events¶
See also
New to statecharts? See Core concepts for an overview of how states, transitions, events, and actions fit together.
An event is a named signal that drives the state machine forward. When you
assign a transition to a class-level name, that name becomes an event — the
library creates an Event object automatically. Events are the external
interface of your machine: callers send event names, and the machine decides
which transitions to take.
Declaring events¶
The simplest way to declare an event is by assigning a transition to a name:
>>> from statemachine import Event, State, StateChart
>>> class SimpleSM(StateChart):
... initial = State(initial=True)
... final = State(final=True)
...
... start = initial.to(final)
>>> isinstance(SimpleSM.start, Event)
True
The name start is automatically converted to an Event with
id="start". Multiple transitions can share the same event using
the | operator:
>>> class TrafficLight(StateChart):
... green = State(initial=True)
... yellow = State()
... red = State()
...
... cycle = green.to(yellow) | yellow.to(red) | red.to(green)
>>> sm = TrafficLight()
>>> sm.send("cycle")
>>> sm.yellow.is_active
True
For better IDE support (autocompletion, type checking) or to set a
human-readable display name, use the Event class explicitly:
>>> class SimpleSM(StateChart):
... initial = State(initial=True)
... final = State(final=True)
...
... start = Event(initial.to(final), name="Start the machine")
>>> SimpleSM.start.name
'Start the machine'
>>> SimpleSM.start.id
'start'
Event identity: id vs name¶
Every event has two string properties:
id— the programmatic identifier, derived from the class attribute name. Use this insend(), guards, and comparisons.name— a human-readable label for display purposes. Auto-generated from theidby replacing_and.with spaces and capitalizing the first word. You can override the automatic name by passingname=explicitly when declaring the event:
>>> TrafficLight.cycle.id
'cycle'
>>> TrafficLight.cycle.name
'Cycle'
>>> class Example(StateChart):
... on = State(initial=True)
... off = State(final=True)
... shut_down = Event(on.to(off), name="Shut the system down")
>>> Example.shut_down.name
'Shut the system down'
Tip
Always use event.id for programmatic checks. The name property is intended
for UI display and may differ from the id.
Triggering events¶
Once declared, events are triggered on a StateChart instance in two ways:
As a method call:
sm.cycle()— when the event name is known at development time.Via
send():sm.send("cycle")— when the event name is dynamic (e.g., from user input, a message queue, or a data file).
Both styles produce the same result. The machine evaluates guard conditions, executes Actions, and updates the configuration.
See also
See Sending events for the full runtime API — send(), raise_(),
delayed events, and cancellation.
The event parameter on transitions¶
Each transition accepts an optional event parameter that binds it to a
specific event, overriding the default (which is the class-level attribute
name). This lets individual transitions within a group respond to their own
event identifiers:
>>> from statemachine import Event, State, StateChart
>>> class TrafficLightMachine(StateChart):
... green = State(initial=True)
... yellow = State()
... red = State()
...
... slowdown = Event(name="Slowing down")
...
... cycle = Event(
... green.to(yellow, event=slowdown)
... | yellow.to(red, event="stop")
... | red.to(green, event="go"),
... name="Loop",
... )
>>> sm = TrafficLightMachine()
>>> sm.send("cycle") # umbrella event — dispatches green→yellow
>>> sm.yellow.is_active
True
>>> sm.send("stop") # individual event — dispatches yellow→red
>>> sm.red.is_active
True
>>> sm.send("go") # individual event — dispatches red→green
>>> sm.green.is_active
True
The event parameter accepts a string, an Event instance, a reference
to a previously declared Event (like slowdown above), or a list of
any of these. A space-separated string is also accepted and split into
individual events automatically:
>>> class MultiEvent(StateChart):
... a = State(initial=True)
... b = State(final=True)
...
... # Both forms are equivalent — the transition responds to "move", "go" and "start"
... move = a.to(b, event=["go", "start"])
>>> sm = MultiEvent()
>>> sm.send("move")
>>> sm.b.is_active
True
>>> sm = MultiEvent()
>>> sm.send("go")
>>> sm.b.is_active
True
>>> sm = MultiEvent()
>>> sm.send("start")
>>> sm.b.is_active
True
Tip
This is an advanced feature. Most state machines only need the simple
name = source.to(target) form. Use the event parameter when you need
fine-grained control over event routing within a composite transition group.
Automatic events¶
The engine generates certain events automatically in response to structural changes. You don’t send these yourself — you define transitions that react to them.
done.state events¶
Added in version 3.0.0.
When a compound state’s final child is entered, the
engine queues a done.state.{parent_id} internal event. Define a transition
for this event to react when a compound’s work is complete:
>>> from statemachine import State, StateChart
>>> class QuestWithDone(StateChart):
... class quest(State.Compound):
... traveling = State(initial=True)
... arrived = State(final=True)
... finish = traveling.to(arrived)
... celebration = State(final=True)
... done_state_quest = quest.to(celebration)
>>> sm = QuestWithDone()
>>> sm.send("finish")
>>> set(sm.configuration_values) == {"celebration"}
True
For parallel states, the done.state event fires
only when all regions have reached a final state:
>>> from statemachine import State, StateChart
>>> class WarWithDone(StateChart):
... class war(State.Parallel):
... class quest(State.Compound):
... start_q = State(initial=True)
... end_q = State(final=True)
... finish_q = start_q.to(end_q)
... class battle(State.Compound):
... start_b = State(initial=True)
... end_b = State(final=True)
... finish_b = start_b.to(end_b)
... peace = State(final=True)
... done_state_war = war.to(peace)
>>> sm = WarWithDone()
>>> sm.send("finish_q")
>>> "war" in sm.configuration_values
True
>>> sm.send("finish_b")
>>> set(sm.configuration_values) == {"peace"}
True
DoneData¶
Final states can carry data to their done.state handlers via the donedata
parameter. The value should be a callable (or method name string) that returns
a dict, which is forwarded as keyword arguments to the transition handler:
>>> from statemachine import Event, State, StateChart
>>> class QuestCompletion(StateChart):
... class quest(State.Compound):
... traveling = State(initial=True)
... completed = State(final=True, donedata="get_result")
... finish = traveling.to(completed)
... def get_result(self):
... return {"hero": "frodo", "outcome": "victory"}
... epilogue = State(final=True)
... done_state_quest = Event(quest.to(epilogue, on="capture_result"))
... def capture_result(self, hero=None, outcome=None, **kwargs):
... self.result = f"{hero}: {outcome}"
>>> sm = QuestCompletion()
>>> sm.send("finish")
>>> sm.result
'frodo: victory'
Note
donedata can only be specified on final=True states. Attempting to use it
on a non-final state raises InvalidDefinition.
error.execution events¶
When a callback raises during a macrostep and
catch_errors_as_events is enabled, the engine dispatches an
error.execution internal event. Define a transition for this event to
recover from errors within the statechart:
>>> from statemachine import State, StateChart
>>> class ResilientChart(StateChart):
... working = State(initial=True)
... failed = State(final=True)
...
... go = working.to.itself(on="do_work")
... error_execution = working.to(failed)
...
... def do_work(self):
... raise RuntimeError("something went wrong")
>>> sm = ResilientChart()
>>> sm.send("go")
>>> "failed" in sm.configuration_values
True
See also
See How errors are caught for the full error handling reference: recovery
patterns, after as a finalize hook, and nested error scenarios.
Dot-notation naming conventions¶
SCXML uses dot-separated event names (done.state.quest, error.execution),
but Python identifiers cannot contain dots. The library provides prefix-based
naming conventions that automatically register both forms:
done_state_ prefix¶
Any event attribute starting with done_state_ matches both the underscore
form and the dot-notation form. Only the prefix is replaced — the suffix is
kept as-is, preserving multi-word state names:
Attribute name |
Matches event names |
|---|---|
|
|
|
|
error_ prefix¶
Any event attribute starting with error_ matches both the underscore form
and the dot-notation form. Unlike done_state_, all underscores after
the prefix are replaced with dots:
Attribute name |
Matches event names |
|---|---|
|
|
Note
If you provide an explicit id= parameter on the Event, it takes precedence
and the naming convention is not applied.