Python State Machine Library

W3C SCXML 1.0 compliant state machine for Python via pybind11 bindings. Full 202/202 W3C conformance tests passing including HTTP events.

State Machines in Python

Python is the most popular programming language (TIOBE #1, 2026), yet most Python state machine libraries offer only flat FSMs without hierarchical states, parallel regions, or standard compliance. SCE brings the full power of W3C SCXML 1.0 to Python by wrapping the battle-tested C++ interpreter via pybind11.

Existing approaches compared

Approach Pros Cons
transitions Pure Python, simple API Flat states only, no parallel, no standard
python-statemachine Declarative, Django integration No hierarchy, no history, no W3C compliance
sismic YAML statecharts, hierarchical Pure Python (slow), no W3C SCXML support
SCE W3C standard, C++ speed, full hierarchy Requires C++ build step for bindings

Quick Start

Step 1: Build the Python bindings

# Clone and build
git clone --recursive https://github.com/newmassrael/scxml-core-engine.git
cd scxml-core-engine

mkdir build_python && cd build_python
cmake .. -DBUILD_PYTHON_BINDINGS=ON -DCMAKE_BUILD_TYPE=Release \
         -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF
cmake --build . --target _sce

Step 2: Use with context manager

import sce

# Context manager automatically starts/stops the engine
with sce.Engine.from_file("traffic_light.scxml") as engine:
    print(engine.current_state)     # 'red'
    engine.send_event("timer")
    print(engine.current_state)     # 'green'
    engine.send_event("timer")
    print(engine.current_state)     # 'yellow'

Step 3: Or use explicit lifecycle

import sce

engine = sce.Engine.from_file("traffic_light.scxml")
engine.start()

engine.send_event("timer")
print(engine.current_state)    # 'green'
print(engine.active_states)    # ['green']
print(engine.running)          # True

engine.stop()

API Reference

Factory Methods

Method Description
Engine.from_file(path) Create engine from an SCXML file. Raises ValueError on failure.
Engine.from_string(content) Create engine from an SCXML string. Raises ValueError on failure.

Core Operations

Method Description
start() Start the state machine. Returns True if started successfully.
stop() Stop the state machine.
send_event(name, data="") Send an event (internal queue). Returns True on success.
send_external_event(name, data="") Send to external queue (W3C SCXML 5.10). For HTTP/BasicHTTPEventProcessor.
is_in_state(state_id) Check if a specific state is active.
set_variable(name, value) Set datamodel variable (bool, int, float, or str).
get_variable(name) Get datamodel variable as string.

Properties

Property Type Description
current_state str Current active state ID
active_states list[str] All currently active state IDs (for parallel states)
running bool Whether the engine is currently running
last_error str Last error message
statistics Statistics Execution statistics (events, transitions)

W3C SCXML Features

SCE's Python bindings pass all 202 mandatory W3C SCXML conformance tests, including 13 HTTP event tests:

Feature W3C Section Status
Compound (hierarchical) states 3.3 Supported
Parallel states 3.4 Supported
History states (shallow + deep) 3.10 Supported
ECMAScript datamodel B.2 Supported
Invoke (parent-child) 6.4 Supported
Delayed send 6.2 Supported
BasicHTTPEventProcessor C.2 Supported

Architecture

The Python bindings are a thin wrapper over the C++ ReadySCXMLEngine interpreter. No Python reimplementation -- the same C++ code that passes all W3C tests runs under the hood.

Python (sce package)
  |
  v
pybind11 (PyEngine wrapper, GIL management)
  |
  v
C++ ReadySCXMLEngine (Interpreter + EventScheduler + EventDispatcher)
  |
  v
Lua 5.4 / QuickJS (ECMAScript evaluation)

GIL management: All C++ calls release the Python GIL (py::gil_scoped_release) to prevent deadlocks with C++ worker threads (EventRaiser, EventScheduler). This enables safe multi-threaded operation with invoke and delayed send features.

Module structure

Path Description
sce-python/src/bindings.cpp pybind11 wrapper (PyEngine, PyStatistics)
sce-python/python/sce/__init__.py Python package (exports Engine, Statistics)
sce-python/tests/test_w3c.py W3C conformance test runner (202 tests)
sce-python/pyproject.toml scikit-build-core wheel configuration
sce-python/CMakeLists.txt pybind11 v2.13.6 (FetchContent)

Running W3C Tests

# Build Python bindings first (see Quick Start above)

# Run all 202 W3C tests (including HTTP)
SPDLOG_LEVEL=off PYTHONPATH=build_python/sce-python:sce-python/python \
    python3 sce-python/tests/test_w3c.py

# Skip HTTP tests for faster iteration
SPDLOG_LEVEL=off PYTHONPATH=build_python/sce-python:sce-python/python \
    python3 sce-python/tests/test_w3c.py --skip-http

# Run specific test
SPDLOG_LEVEL=off PYTHONPATH=build_python/sce-python:sce-python/python \
    python3 sce-python/tests/test_w3c.py 144