(events)= (event)=
New to statecharts? See [](concepts.md) 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)
TrueThe 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
TrueFor 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)=
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. Defaults to theidwhen not explicitly set.
>>> TrafficLight.cycle.id
'cycle'
>>> TrafficLight.cycle.name
'cycle'Always use `event.id` for programmatic checks. The `name` property is intended
for UI display and may change format in future versions.
(triggering-events)= (triggering events)=
Once declared, events are triggered on a {ref}StateChart <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
{ref}guard conditions <validators and guards>, executes {ref}actions, and
updates the {ref}configuration <querying-configuration>.
See {ref}`sending-events` for the full runtime API — `send()`, `raise_()`,
delayed events, and cancellation.
(event-parameter)=
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
TrueThe 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
TrueThis 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.
(done-state-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.
When a {ref}compound state's <compound-states> 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"}
TrueFor {ref}parallel states <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'`donedata` can only be specified on `final=True` states. Attempting to use it
on a non-final state raises `InvalidDefinition`.
When a callback raises during a macrostep and
{ref}catch_errors_as_events <behaviour> 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
TrueSee {ref}`error-execution` for the full error handling reference: recovery
patterns, `after` as a finalize hook, and nested error scenarios.
(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-convention)=
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 |
|---|---|
done_state_quest |
"done_state_quest" and "done.state.quest" |
done_state_lonely_mountain |
"done_state_lonely_mountain" and "done.state.lonely_mountain" |
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 |
|---|---|
error_execution |
"error_execution" and "error.execution" |
If you provide an explicit `id=` parameter on the `Event`, it takes precedence
and the naming convention is not applied.