Not released yet
State machines now support Python's built-in format() protocol. Use f-strings
or format() to get text representations — on both classes and instances:
f"{TrafficLightMachine:md}"
f"{sm:mermaid}"
format(sm, "rst")Supported formats:
| Format | Output | Requires |
|---|---|---|
dot |
Graphviz DOT source | pydot |
svg |
SVG markup (via Graphviz) | pydot + graphviz |
mermaid |
Mermaid stateDiagram-v2 | — |
md |
Markdown transition table | — |
rst |
RST transition table | — |
See {ref}diagram:Text representations for details.
A new Formatter facade with decorator-based registration unifies all text
format rendering behind a single API. Adding a new format requires only
registering a render function — no changes to __format__, the CLI, or the
Sphinx directive:
from statemachine.contrib.diagram import formatter
formatter.render(sm, "mermaid")
formatter.supported_formats()
@formatter.register_format("custom")
def _render_custom(machine_or_class):
...See {ref}formatter-api for details.
State machines can now be rendered as
Mermaid stateDiagram-v2
source text — no Graphviz installation required. Supports compound states,
parallel regions, history states, guards, and active-state highlighting.
Three ways to use it:
- f-strings:
f"{sm:mermaid}" - CLI:
python -m statemachine.contrib.diagram MyMachine - --format mermaid - Sphinx directive:
:format: mermaidrenders viasphinxcontrib-mermaid.
See {ref}diagram:Mermaid format for details.
Use {statechart:FORMAT} placeholders in your class docstring to embed a
live representation of the state machine. The placeholder is replaced at
class definition time, so the docstring always stays in sync with the code:
class TrafficLight(StateChart):
"""A traffic light.
{statechart:md}
"""
green = State(initial=True)
yellow = State()
red = State()
cycle = green.to(yellow) | yellow.to(red) | red.to(green)Any registered format works: md, rst, mermaid, dot, etc.
Works with Sphinx autodoc — the expanded docstring is what gets rendered.
See {ref}diagram:Auto-expanding docstrings for details.
A new Sphinx extension renders state machine diagrams directly in your documentation from an importable class path — no manual image generation needed.
Add "statemachine.contrib.diagram.sphinx_ext" to your conf.py
extensions, then use the directive in any MyST Markdown page:
```{statemachine-diagram} myproject.machines.OrderControl
:events: receive_payment
:caption: After payment
:target:
```The directive supports the same options as the standard image/figure
directives (:width:, :height:, :scale:, :align:, :target:,
:class:, :name:), plus :events: to instantiate the machine and send
events before rendering (highlighting the current state).
Using :target: without a value makes the diagram clickable, opening the
full SVG in a new browser tab for zooming — useful for large statecharts.
The :format: mermaid option renders via sphinxcontrib-mermaid instead of
Graphviz.
See {ref}diagram:Sphinx directive for full documentation.
#589.
The python -m statemachine.contrib.diagram command now accepts:
--eventsto instantiate the machine and send events before rendering, highlighting the current active state.--formatto choose the output format (mermaid,md,rst,dot,svg, or image formats via Graphviz). Use-as the output path to write text formats to stdout.
See {ref}diagram:Command line for details.
#593.
The engine's hot paths have been systematically profiled and optimized, resulting in 4.7x–7.7x faster event throughput and 1.9x–2.6x faster setup across all machine types. All optimizations are internal — no public API changes. See #592 for details.
The sync engine is thread-safe: multiple threads can send events to the same state
machine instance concurrently. This is now documented in the
{ref}processing model <thread-safety> and verified by stress tests.
#592.
-
Fixes silent misuse of
Event()with multiple positional arguments. Passing more than one transition toEvent()(e.g.,Event(t1, t2)) now raisesInvalidDefinitionwith a clear message suggesting the|operator. Previously, the second argument was silently interpreted as the eventid, leaving the extra transitions eventless (auto-firing). #588. -
Event.nameis now auto-humanized from theid(e.g.,cycle→Cycle,pick_up→Pick up). Diagrams, Mermaid output, and text tables all display the human-readable name. Explicitname=values are preserved. The samehumanize_id()helper is now shared byEventandState. #601, fixes #600. -
current_statesetter now emitsDeprecationWarningconsistently with the getter. Previously only readingcurrent_statetriggered the warning; assigning to it was silent. The docstring also now includes adeprecateddirective for Sphinx autodoc. #604. -
Configuration.add()/discard()now write through the model setter, ensuring state changes are persisted correctly on domain models (e.g., Django models with custom setters). Previously the configuration was updated in-place without notifying the model layer. #596. -
States.from_enum()now works correctly inside compound and parallel states. Previously the states were silently not collected when used as a nested state declaration. #607, fixes #606.