Skip to content

Visualization

PyStator can generate diagrams of your state machines from their definitions — no separate diagram tool or manual drawing required. Diagrams stay in sync with the YAML automatically.

Available formats

Function Output format Best for
to_mermaid Mermaid stateDiagram-v2 Markdown docs, GitHub READMEs, MkDocs
to_mermaid_flowchart Mermaid flowchart Alternative visual style
to_dot Graphviz DOT High-quality rendered images (png, svg, pdf)
to_scxml SCXML XML Interoperability with other FSM tools
get_statistics Python dict Programmatic analysis (CI checks, docs metadata)

Mermaid state diagram

from pystator import StateMachine
from pystator.visualization import to_mermaid

machine = StateMachine.from_yaml("order_fsm.yaml")
diagram = to_mermaid(
    machine,
    title="Order Lifecycle",
    show_guards=True,    # default: True
    show_actions=False,  # default: False — keeps diagrams readable
    show_timeouts=True,  # default: True
    direction="LR",      # "TB" (top-bottom), "LR" (left-right), "BT", "RL"
)
print(diagram)

Example output:

---
title: Order Lifecycle
---
stateDiagram-v2
    direction LR
    [*] --> pending
    pending --> confirmed : confirm
    confirmed --> shipped : ship [stock_available]
    shipped --> delivered : deliver
    delivered --> [*]
    pending --> cancelled : cancel
    confirmed --> cancelled : cancel

Paste directly into any Markdown file or MkDocs page and it renders automatically (MkDocs Material has built-in Mermaid support).

Embedding in MkDocs

```mermaid
stateDiagram-v2
    direction LR
    [*] --> pending
    pending --> confirmed : confirm
    ...
```

Embedding in a GitHub README

GitHub renders Mermaid code blocks natively since 2022:

```mermaid
stateDiagram-v2
    [*] --> pending
    pending --> confirmed : confirm
```

Mermaid flowchart

Produces a flowchart diagram instead of stateDiagram-v2 — useful when you prefer a different visual layout:

from pystator.visualization import to_mermaid_flowchart

diagram = to_mermaid_flowchart(machine, title="Order Flow", direction="TB")
print(diagram)

State shapes: - Initial states → stadium shape ([name]) - Terminal states → double-hexagon {{name}} - Normal states → rectangle [name]


Graphviz DOT

For high-quality rendered images (PNG, SVG, PDF) suitable for architecture docs:

from pystator.visualization import to_dot

dot_source = to_dot(
    machine,
    title="Order Lifecycle",
    show_guards=True,
    show_actions=True,
)
print(dot_source)

Render with the graphviz command-line tool:

# Save to file
python -c "
from pystator import StateMachine
from pystator.visualization import to_dot
m = StateMachine.from_yaml('order_fsm.yaml')
print(to_dot(m, title='Order FSM'))
" > order.dot

# Render to PNG
dot -Tpng order.dot -o order.png

# Render to SVG
dot -Tsvg order.dot -o order.svg

Or render programmatically with the graphviz Python package:

import graphviz

dot_source = to_dot(machine, title="Order FSM")
graph = graphviz.Source(dot_source)
graph.render("docs/diagrams/order_fsm", format="png", cleanup=True)

SCXML export

SCXML is a W3C standard format for state machine definitions. Use it for interoperability with other FSM tools or formal verification:

from pystator.visualization import to_scxml

scxml_doc = to_scxml(machine)
with open("order_fsm.scxml", "w") as f:
    f.write(scxml_doc)

Machine statistics

get_statistics returns a dict of counts useful for documentation, monitoring, or CI gates:

from pystator.visualization import get_statistics

stats = get_statistics(machine)
print(stats)
# {
#     "state_count": 5,
#     "transition_count": 7,
#     "terminal_state_count": 2,
#     "initial_state": "pending",
#     "terminal_states": ["delivered", "cancelled"],
#     "trigger_count": 6,
#     "guard_count": 3,
#     "action_count": 4,
#     "has_timeouts": False,
#     "has_parallel_states": False,
#     "has_hierarchical_states": False,
# }

Using statistics in CI

Fail a CI step if the FSM grows unexpectedly complex:

from pystator import StateMachine
from pystator.visualization import get_statistics

machine = StateMachine.from_yaml("order_fsm.yaml")
stats = get_statistics(machine)

assert stats["state_count"] <= 20, f"Too many states: {stats['state_count']}"
assert stats["terminal_state_count"] >= 1, "No terminal states defined"

Generating diagrams for all machines

from pathlib import Path
from pystator import StateMachine
from pystator.visualization import to_mermaid

machines_dir = Path("machines")
docs_dir = Path("docs/diagrams")
docs_dir.mkdir(parents=True, exist_ok=True)

for yaml_file in machines_dir.glob("*.yaml"):
    machine = StateMachine.from_yaml(str(yaml_file))
    diagram = to_mermaid(machine, title=machine.name)
    out = docs_dir / f"{yaml_file.stem}.md"
    out.write_text(f"# {machine.name}\n\n```mermaid\n{diagram}\n```\n")
    print(f"Generated {out}")

See also