Skip to main content

Lightweight finite state machine with guards and callbacks.

Project description

philiprehberger-state-machine

Tests PyPI version GitHub release Last updated License Bug Reports Feature Requests Sponsor

Lightweight finite state machine with guards and callbacks.

Installation

pip install philiprehberger-state-machine

Usage

from philiprehberger_state_machine import StateMachine

sm = StateMachine(
    states=["pending", "confirmed", "shipped", "delivered"],
    initial="pending",
    transitions=[
        ("pending", "confirmed", "confirm"),
        ("confirmed", "shipped", "ship"),
        ("shipped", "delivered", "deliver"),
    ],
)

sm.trigger("confirm")
print(sm.state)  # "confirmed"

Checking Available Transitions

from philiprehberger_state_machine import StateMachine

sm = StateMachine(
    states=["pending", "confirmed", "shipped"],
    initial="pending",
    transitions=[
        ("pending", "confirmed", "confirm"),
        ("confirmed", "shipped", "ship"),
    ],
)

sm.can("confirm")  # True
sm.can("ship")     # False

Callbacks

from philiprehberger_state_machine import StateMachine

sm = StateMachine(
    states=["pending", "confirmed", "shipped"],
    initial="pending",
    transitions=[
        ("pending", "confirmed", "confirm"),
        ("confirmed", "shipped", "ship"),
    ],
)

sm.on_enter("confirmed", lambda state, event: print(f"Entered {state} via {event}"))
sm.on_exit("pending", lambda state, event: print(f"Left {state} via {event}"))

sm.trigger("confirm")
# Left pending via confirm
# Entered confirmed via confirm

Guard Conditions

Guards are optional callables that receive a context dict and must return True to allow the transition. If a guard returns falsy, InvalidTransitionError is raised.

from philiprehberger_state_machine import StateMachine

sm = StateMachine(
    states=["draft", "published"],
    initial="draft",
    transitions=[],
)

sm.add_transition("draft", "published", "publish", guard=lambda ctx: ctx.get("has_title", False))

sm.trigger("publish", context={"has_title": True})   # succeeds
print(sm.state)  # "published"

Transition Context

Pass a context dict to trigger() to share data with guards and callbacks.

sm.trigger("confirm", context={"user": "alice", "approved": True})

If no context is provided, an empty dict is passed to guards.

History and Reset

from philiprehberger_state_machine import StateMachine

sm = StateMachine(
    states=["pending", "confirmed", "shipped"],
    initial="pending",
    transitions=[
        ("pending", "confirmed", "confirm"),
        ("confirmed", "shipped", "ship"),
    ],
)

sm.trigger("confirm")
sm.trigger("ship")
print(sm.history)  # ["pending", "confirmed"]

sm.reset()
print(sm.state)    # "pending"
print(sm.history)  # []

API

Function / Class Description
StateMachine(states, initial, transitions) Create a state machine with given states, initial state, and transitions
StateMachine.state Current state (read-only property)
StateMachine.history List of past states (read-only property)
StateMachine.trigger(event, context=None) Execute a transition or raise InvalidTransitionError. Pass optional context dict to guards.
StateMachine.can(event) Return whether the event is valid from the current state
StateMachine.add_transition(from_state, to_state, event, guard=None) Add a transition with an optional guard callable
StateMachine.on_enter(state, callback) Register a callback for entering a state
StateMachine.on_exit(state, callback) Register a callback for exiting a state
StateMachine.reset() Reset to initial state and clear history
InvalidTransitionError Raised on invalid transitions; has .state and .event attributes

Development

pip install -e .
python -m pytest tests/ -v

Support

If you find this package useful, consider giving it a star on GitHub — it helps motivate continued maintenance and development.

LinkedIn More packages

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

philiprehberger_state_machine-0.2.0.tar.gz (6.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

philiprehberger_state_machine-0.2.0-py3-none-any.whl (5.5 kB view details)

Uploaded Python 3

File details

Details for the file philiprehberger_state_machine-0.2.0.tar.gz.

File metadata

File hashes

Hashes for philiprehberger_state_machine-0.2.0.tar.gz
Algorithm Hash digest
SHA256 badfb28376ad9c7b6b2837fb18e46e54933aa59ba904ccb6e833ec29603b2577
MD5 4722ab726f7cf251ab2fd665fdf61268
BLAKE2b-256 de156aef201ed4315924136b6b3dfda63171341d6e41ff3c0d30a7e1a6b3a4b5

See more details on using hashes here.

File details

Details for the file philiprehberger_state_machine-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for philiprehberger_state_machine-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7a0fac45ba5ac1c90190de71b1fe37eb63d326dc05016cb719c99242fbef2d39
MD5 5a1910e67f4b05e4520e495e321395e4
BLAKE2b-256 cec335c456b2ecb5f672139a907dc7ab9c860779b87eca961c5238de7ea1c38d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page