transitions order workflow with conditions and callbacks: working versions (tested 2026-10-18)
An order workflow state machine built with transitions, covering states, triggers, guarded conditions, before/after callbacks via EventData and MachineError on invalid moves, tested offline.
passed5 passed · 0 failed2026-10-18 03:57 UTC- Status
- passed
- Last passed
- 2026-10-18 03:57 UTC
- Tests
- 5 / 5
- Language
- Python
- Runtime
- Python 3.13.16
pinned versions2
transitions@0.9.3pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
test environment
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 39095370682
The code
Exactly the files that passed. Your agent can pull them with get_recipe("transitions-fsm-callbacks") (install), or see them on GitHub.
requirements.txt
transitions==0.9.3 pytest==9.1.1
order.py
from transitions import Machine
class Order:
states = ["cart", "placed", "paid", "shipped", "cancelled"]
def __init__(self, items=0):
self.items = items
self.amount = 0
self.log = []
self.machine = Machine(
model=self,
states=self.states,
initial="cart",
send_event=True,
)
self.machine.add_transition(
"place", "cart", "placed",
conditions="has_items",
before="before_place",
after="after_place",
)
self.machine.add_transition(
"pay", "placed", "paid",
before="charge",
after="paid",
)
self.machine.add_transition(
"ship", "paid", "shipped",
after="shipped",
)
self.machine.add_transition(
"cancel", ["cart", "placed"], "cancelled",
after="cancelled",
)
def has_items(self, event):
return self.items > 0
def before_place(self, event):
self.log.append(("before_place", self.state))
def after_place(self, event):
self.log.append(("after_place", self.state))
def charge(self, event):
self.amount = event.kwargs.get("amount", 0)
self.log.append(("charge", self.state))
def paid(self, event):
self.log.append(("paid", self.state))
def shipped(self, event):
self.log.append(("shipped", self.state))
def cancelled(self, event):
self.log.append(("cancelled", self.state))
tests/test_order.py
import pytest
from transitions import MachineError
from order import Order
def test_before_sees_old_state_after_sees_new_state():
order = Order(items=2)
assert order.place() is True
order.pay(amount=25)
assert order.ship() is True
assert order.state == "shipped"
assert order.amount == 25
assert ("before_place", "cart") in order.log
assert ("after_place", "placed") in order.log
assert ("charge", "placed") in order.log
assert ("paid", "paid") in order.log
def test_wrong_source_raises_machine_error():
order = Order(items=1)
with pytest.raises(MachineError, match="Can't trigger event ship"):
order.ship()
assert order.state == "cart"
def test_failed_condition_returns_false_without_raising():
order = Order(items=0)
assert order.may_place() is False
assert order.place() is False
assert order.state == "cart"
assert order.log == []
def test_cancel_only_from_cart_or_placed():
order = Order(items=1)
order.place()
order.pay()
assert order.may_cancel() is False
with pytest.raises(MachineError):
order.cancel()
assert order.state == "paid"
def test_auto_transitions_bypass_sources_and_conditions():
order = Order(items=0)
assert "to_shipped" in order.machine.get_triggers("cart")
assert order.to_shipped() is True
assert order.state == "shipped"
assert order.log == []
pytest.ini
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.