33
44# Behaviour
55
6- The ` StateChart ` class follows the
6+ ``` {seealso}
7+ New to statecharts? See [](concepts.md) for an overview of how states,
8+ transitions, events, and actions fit together.
9+ ```
10+
11+ The {class}` ~statemachine.statemachine.StateChart ` class follows the
712[ SCXML specification] ( https://www.w3.org/TR/scxml/ ) by default. The
8- ` StateMachine ` class extends ` StateChart ` but overrides several defaults to
9- preserve backward compatibility with existing code.
13+ {class}` ~statemachine.statemachine.StateMachine ` class extends ` StateChart `
14+ but overrides several defaults to preserve backward compatibility with
15+ pre-3.0 code.
1016
1117The behavioral differences are controlled by class-level attributes. This
12- design allows a gradual upgrade path: start from ` StateMachine ` and selectively
13- enable spec-compliant behaviors one at a time, or start from ` StateChart ` and
14- get full SCXML compliance out of the box.
18+ design allows a gradual upgrade path: start from ` StateMachine ` and
19+ selectively enable spec-compliant behaviors one at a time, or start from
20+ ` StateChart ` and get full SCXML compliance out of the box.
1521
1622``` {tip}
17- We **strongly recommend** that new projects use `StateChart` directly. Existing
18- projects should consider migrating when possible, as the SCXML-compliant
19- behavior provides more predictable semantics.
23+ We **strongly recommend** that new projects use `StateChart` directly.
24+ Existing projects should consider migrating when possible, as the
25+ SCXML-compliant behavior provides more predictable semantics.
2026```
2127
2228
2329## Comparison table
2430
25- | Attribute | ` StateChart ` | ` StateMachine ` | Description |
26- | ------------------------------------| ---------------| ----------------| -------------|
27- | ` allow_event_without_transition ` | ` True ` | ` False ` | Tolerate events that don't match any transition |
28- | ` enable_self_transition_entries ` | ` True ` | ` False ` | Execute entry/exit actions on self-transitions |
29- | ` atomic_configuration_update ` | ` False ` | ` True ` | When to update {ref}` configuration <querying-configuration> ` during a microstep |
30- | ` error_on_execution ` | ` True ` | ` False ` | Catch runtime errors as ` error.execution ` events |
31+ | Attribute | ` StateChart ` | ` StateMachine ` | Description |
32+ | ---| ---| ---| ---|
33+ | ` allow_event_without_transition ` | ` True ` | ` False ` | Tolerate events that don't match any transition |
34+ | ` enable_self_transition_entries ` | ` True ` | ` False ` | Execute entry/exit actions on self-transitions |
35+ | ` atomic_configuration_update ` | ` False ` | ` True ` | When to update {ref}` configuration <querying-configuration> ` during a microstep |
36+ | ` error_on_execution ` | ` True ` | ` False ` | Catch action errors as ` error.execution ` events |
37+
38+ Each attribute is described below, with cross-references to the pages that
39+ cover the topic in depth.
3140
3241
3342## ` allow_event_without_transition `
@@ -36,69 +45,134 @@ When `True` (SCXML default), sending an event that does not match any enabled
3645transition is silently ignored. When ` False ` (legacy default), a
3746` TransitionNotAllowed ` exception is raised, including for unknown event names.
3847
39- The SCXML spec requires tolerance to unmatched events, as the event-driven model
40- expects that not every event is relevant in every state.
48+ The SCXML spec requires tolerance to unmatched events, as the event-driven
49+ model expects that not every event is relevant in every state.
50+
51+ ``` {seealso}
52+ See {ref}`conditions` for how the engine selects transitions, and
53+ {ref}`checking enabled events` to query which events are currently valid.
54+ ```
4155
4256
4357## ` enable_self_transition_entries `
4458
45- When ` True ` (SCXML default), a {ref}` self-transition <self-transition> ` executes
46- the state's exit and entry actions, just like any other transition. When ` False `
47- (legacy default), self-transitions skip entry/exit actions.
59+ When ` True ` (SCXML default), a {ref}` self-transition <self-transition> `
60+ executes the state's exit and entry actions, just like any other transition.
61+ When ` False ` (legacy default), self-transitions skip entry/exit actions.
4862
4963The SCXML spec treats self-transitions as regular transitions that happen to
5064return to the same state, so entry/exit actions must fire. Use an
5165{ref}` internal transition <internal-transition> ` if you need a transition that
5266stays in the same state ** without** running exit/entry actions.
5367
68+ ``` {seealso}
69+ See {ref}`transitions` for the full reference on self-transitions and
70+ internal transitions.
71+ ```
72+
5473
5574## ` atomic_configuration_update `
5675
57- When ` False ` (SCXML default), a microstep follows the SCXML processing order:
58- first exit all states in the exit set (running exit callbacks), then execute the
59- transition content (` on ` callbacks), then enter all states in the entry set
60- (running entry callbacks). During the ` on ` callbacks, the
61- {ref}` configuration <querying-configuration> ` may be empty or partial.
76+ Controls ** when** the {ref}` configuration <querying-configuration> ` is
77+ updated during a microstep.
78+
79+ When ` False ` (SCXML default), the configuration reflects each phase as it
80+ happens: states are removed during exit and added during entry. This means
81+ that during transition ` on ` callbacks, the configuration may be empty or
82+ partial — the source states have already been exited but the target states
83+ have not yet been entered.
84+
85+ When ` True ` (legacy default), the configuration is updated atomically
86+ ** after** the ` on ` callbacks complete, so ` sm.configuration ` and
87+ ` state.is_active ` always reflect a consistent snapshot during the transition.
88+
89+ ``` py
90+ >> > from statemachine import State, StateChart
91+
92+ >> > class AtomicDemo (StateChart ):
93+ ... atomic_configuration_update = True
94+ ... off = State(initial = True )
95+ ... on = State(final = True )
96+ ...
97+ ... switch = off.to(on, on = " check_config" )
98+ ...
99+ ... def check_config (self ):
100+ ... # With atomic update, configuration is unchanged during 'on'
101+ ... self .off_was_active = self .off.is_active
102+ ... self .on_was_active = self .on.is_active
103+
104+ >> > sm = AtomicDemo()
105+ >> > sm.send(" switch" )
106+ >> > sm.off_was_active # source still in configuration during 'on'
107+ True
108+ >> > sm.on_was_active # target not yet in configuration during 'on'
109+ False
62110
63- When ` True ` (legacy default), the configuration is updated atomically after the
64- ` on ` callbacks, so ` sm.configuration ` and ` state.is_active ` always reflect a
65- consistent snapshot during the transition. This was the behavior of all previous
66- versions.
111+ ```
112+
113+ With ` atomic_configuration_update = False ` (the SCXML default), the result
114+ is different — ` off.is_active ` is ` False ` because exit already removed it,
115+ and ` on.is_active ` is also ` False ` because entry hasn't added it yet.
116+ In this mode, use ` previous_configuration ` and ` new_configuration ` to
117+ inspect the full picture:
118+
119+ ``` py
120+ >> > class SCXMLDemo (StateChart ):
121+ ... off = State(initial = True )
122+ ... on = State(final = True )
123+ ...
124+ ... switch = off.to(on, on = " check_config" )
125+ ...
126+ ... def check_config (self , previous_configuration , new_configuration ):
127+ ... self .prev = {s.id for s in previous_configuration}
128+ ... self .new = {s.id for s in new_configuration}
129+
130+ >> > sm = SCXMLDemo()
131+ >> > sm.send(" switch" )
132+ >> > sm.prev
133+ {' off' }
134+ >> > sm.new
135+ {' on' }
67136
68- ``` {note}
69- When `atomic_configuration_update` is `False`, `on` callbacks can request
70- `previous_configuration` and `new_configuration` keyword arguments to inspect
71- which states were active before and after the microstep. See
72- {ref}`dependency-injection` for the full parameter list .
137+ ```
138+
139+ ``` {seealso}
140+ See {ref}`dependency-injection` for the full list of parameters available
141+ in callbacks .
73142```
74143
75144
76145## ` error_on_execution `
77146
78- When ` True ` (SCXML default), runtime exceptions in callbacks (guards, actions,
79- entry/exit) are caught by the engine and result in an internal ` error.execution `
80- event. When ` False ` (legacy default), exceptions propagate normally to the
81- caller.
147+ When ` True ` (SCXML default), runtime exceptions in action callbacks
148+ (entry/exit, transition ` on ` ) are caught by the engine and dispatched as
149+ internal ` error.execution ` events. When ` False ` (legacy default), exceptions
150+ propagate normally to the caller.
151+
152+ ``` {note}
153+ {ref}`Validators <validators>` are **not** affected by this flag — they
154+ always propagate exceptions to the caller, regardless of the
155+ `error_on_execution` setting. See {ref}`validators` for details.
156+ ```
82157
83158``` {seealso}
84- See {ref}`error-handling` for the full `error.execution` lifecycle, block-level
85- error catching, and the cleanup/finalize pattern.
159+ See {ref}`error-handling` for the full `error.execution` lifecycle,
160+ block-level error catching, and the cleanup/finalize pattern.
86161```
87162
88163
89164## Gradual migration
90165
91- You can override any of these attributes individually. For example, to adopt
92- SCXML error handling in an existing ` StateMachine ` without changing other
93- behaviors:
166+ All behavioral attributes can be overridden individually. This lets you
167+ adopt SCXML semantics incrementally in an existing ` StateMachine ` :
94168
95169``` python
96170class MyMachine (StateMachine ):
97171 error_on_execution = True
98172 # ... everything else behaves as before ...
99173```
100174
101- Or to use ` StateChart ` but keep the legacy atomic configuration update :
175+ Or keep a specific legacy behavior while using ` StateChart ` for the rest :
102176
103177``` python
104178class MyChart (StateChart ):
0 commit comments