Skip to main content

statem

A minimal async state machine engine for Python, built on Pydantic. The state graph is a validated dict; guards and actions are plain Python (sync or async) functions, registered by name.

CI Docs PyPI Python Versions Coverage License: MIT

Full documentation: https://rahuldas-dev.github.io/statem/

Install · Quickstart · Hooks · Visualizing · Why it's built this way · Development · License

Install

pip install statem

The importable package is statem:

from statem import StateMachine, Signal

Quickstart

import asyncio
from statem import Signal, StateMachine

config = {
    "idle": {"on": {"START": {"target": "running", "guard": "can_start"}}},
    "running": {"on": {"STOP": "idle"}},
}


def can_start(ctx, signal) -> bool:
    return True


async def main() -> None:
    machine = StateMachine.from_dict(config, guard_dict={"can_start": can_start})
    state = await machine.run(state_name="idle", events=Signal(event="START"), session={})
    print(state)  # "running"


asyncio.run(main())

See examples/bread.py for a fuller example (guards, actions, error_state, and an always-transition), or examples/bank.py for a richer one that exercises every hook in one conversation, including error_state recovering from a real exception. The guide covers the full config shape.

Hooks

Four lifecycle hooks can carry guards and/or actions on any state:

Hook Fires when Carries
on An external Signal (event) matches this state's on map (or its "*" wildcard) Candidates tried in order: guard (optional, must return bool) gates the candidate; its actions run if it fires
always Right after any state entry, including the initial one — no external signal needed Same shape as on, minus the event name; used to auto-advance once a condition becomes true
entry A state is entered (after the firing transition's own actions) Action names only
exit A state is left, before entering the next one Action names only

A fifth field, error_state, isn't a hook itself but a per-state escape hatch: if an on-transition's action raises, the engine catches it and — if error_state is set — transitions there instead of propagating.

Visualizing

to_mermaid(machine, initial=None) renders machine.config as a Mermaid stateDiagram-v2 string — no extra dependency, just text. GitHub, MkDocs, VS Code, and Jupyter all render it natively:

from statem import to_mermaid

print(to_mermaid(machine, initial="idle"))
stateDiagram-v2
    [*] --> idle
    idle --> running: START [can_start]
    running --> idle: STOP

See the guide for guard chains, always, and error_state rendering.

Why it's built this way

  • Data and behavior stay apart. config is a plain, JSON/YAML-friendly dict — author it by hand, generate it, or load it from a file/database/LLM. Guards and actions are ordinary functions registered by name, independent of the graph.
  • Fails fast. Bad transition targets and unregistered guard/action names raise at construction time, not three hops into a live run.
  • Sync or async, your choice. The engine detects and awaits either correctly — no need to wrap trivial sync logic in async def.
  • session is yours. Any shape you want (dict, dataclass, ORM row) — the engine never inspects it, just threads it through to your guards/actions via ctx.session.
  • No subclassing. StateMachine.from_dict(...) + await machine.run(...). No framework lifecycle to inherit into.
  • Every run is traceable. Each guard/action fired is recorded in order; a run_id (yours, or auto-generated) ties every log line from one run together.

Development

uv sync
uv run python -m unittest discover -s tests
uv run coverage run -m unittest discover -s tests && uv run coverage report --fail-under=100
uv run ruff check tests statem

License

MIT — see LICENSE.

Metadata

Release files for statem 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for statem 0.2.0
File Size Uploaded
statem-0.2.0.tar.gz 78.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for statem 0.2.0
File Interpreter ABI Platform
statem-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 92.4 kB

Release files / statem-0.2.0.tar.gz

Download URL statem-0.2.0.tar.gz
Size 78.1 kB
Tags Source
SHA-256 checksum
How to use checksums
229e744b33e95a8368d0cd79dbe85d90ca885da53c9c3078d185b074b3d3a7eb
BLAKE2b-256 checksum
How to use checksums
095f5326400ef3ccf9da63fe1065303304feae8709d1f1283e8d8461b6266f6f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 1, 2026.

Transparency log

Release files / statem-0.2.0-py3-none-any.whl

Download URL statem-0.2.0-py3-none-any.whl
Size 14.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a660707f72b10350a56e4c824c06b7e8fb906850806a189170cdcce54d792c05
BLAKE2b-256 checksum
How to use checksums
471e5b9ded7d99ad0d5c0cc4dd5368300ad08ce99565efa73c4a7ef2e76819f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page