Skip to main content

A tiny generic state machine with implicit states and C#-style events.

Project description

micro-statemachine logo

micro-statemachine

A tiny generic state machine with implicit states.

  • No boilerplate — states are never declared. Calling sm.to_<state>() moves into that state, creating it on the fly.
  • Everything allowed by default — every transition is permitted unless you explicitly forbid it.
  • Event hooks — subscribe to enter, exit, and remain per state with the += / -= operator, just like C# events.
  • Zero dependencies, fully type-hinted, single small module.

Install

pip install micro-statemachine

Quick start

import logging
from micro_statemachine import u_states

logger = logging.getLogger(__name__)

sm = u_states["connection"]

# Subscribe handlers with +=
sm.happy_enter += lambda: logger.info("entered happy state")
sm.failed_enter += lambda: logger.warning("Failed!")
# `remain` fires when you transition into the state you're already in.
sm.failed_remain += lambda: logger.warning("still in failed")

sm.to_failed()   # fires failed_enter
sm.to_failed()   # already failed -> fires failed_remain
sm.to_happy()    # fires failed_exit, then happy_enter

Restricting transitions

By default any transition is allowed. Forbid specific ones with set_transitions, using the <source>_to_<target> naming convention:

sm.set_transitions(happy_to_failed=True, failed_to_happy=False)

sm.to_failed()   # ok
sm.to_happy()    # raises TransitionError

A forbidden transition raises TransitionError.

Current state

Read the active state with sm.state or the sm.current_state alias (both are None until the first transition):

sm.to_happy()
sm.current_state  # "happy"

Assigning to current_state sets the state directly — no transition is run, no enter/exit/remain events fire, and blocking rules are ignored. Handy for restoring a machine to a known state:

sm.current_state = "happy"   # jump straight to happy, silently
sm.current_state = None      # reset to the initial state

none and any in transition rules

Either side of a <source>_to_<target> rule may also be one of two reserved words:

  • none — the initial (pre-transition) state, so you can constrain the very first move.
  • any — a wildcard matching every state (including the initial one).

Explicit rules always override wildcard ones, which lets you flip the default from "everything allowed" to an allowlist:

sm.set_transitions(
    state_1_to_state_2=True,   # plain explicit rule
    none_to_state_1=True,      # the first move may go to state_1...
    none_to_any=False,         # ...and nowhere else
)

sm.to_state_2()   # raises TransitionError (none -> any is forbidden)
sm.to_state_1()   # ok — the explicit rule wins over the wildcard

When several rules could apply, the most specific wins: <source>_to_<target> > <source>_to_any > any_to_<target> > any_to_any.

Events

Each state exposes three events, resolved lazily on first access:

Attribute Fires when…
sm.<state>_enter a transition into <state> from a different state
sm.<state>_exit a transition out of <state>
sm.<state>_remain to_<state>() is called while already in <state>

Handlers are plain zero-argument callables. Add with +=, remove with -=:

def on_enter():
    print("hi")

sm.happy_enter += on_enter
sm.happy_enter -= on_enter

Registry

u_states is a lazy registry — indexing it creates the machine on first access, so different modules can share one machine by name:

from micro_statemachine import u_states

u_states["connection"]  # same StateMachine everywhere

You can also construct machines directly with StateMachine(name).

Diagram

sm.generate_chart() returns a Mermaid state diagram as a fenced markdown block — paste it into a README or GitHub issue and it renders, with the current state highlighted and transition rules drawn as labelled edges (none becomes the initial marker, any a wildcard node):

sm.happy_enter += lambda: None
sm.failed_enter += lambda: None
sm.set_transitions(happy_to_failed=False)
sm.to_happy()
print(sm.generate_chart())
```mermaid
stateDiagram-v2
    [*] --> happy
    failed
    happy
    happy --> failed: ✗ forbidden
    classDef current fill:#ffd54f,stroke:#333,font-weight:bold;
    class happy current
```

States are inferred from subscribed handlers, transition rules, and the current state (they're never declared explicitly).

Naming rules

State names may contain _ but must not be empty or contain the reserved words to, enter, exit, or remain (they'd collide with the transition and event syntax). none and any are reserved for transition rules, so they can't be a state name on their own — but they're fine as parts of one (any_state, none_selected). Invalid names raise ValueError.

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

micro_statemachine-0.1.2.tar.gz (3.5 MB view details)

Uploaded Source

Built Distribution

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

micro_statemachine-0.1.2-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

Details for the file micro_statemachine-0.1.2.tar.gz.

File metadata

  • Download URL: micro_statemachine-0.1.2.tar.gz
  • Upload date:
  • Size: 3.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for micro_statemachine-0.1.2.tar.gz
Algorithm Hash digest
SHA256 95826351e42e8532386355e02c2cf098e33f7a80d80b623b4fda6114aff58a9b
MD5 b377984452dfa761d6d79e11fd6bed5a
BLAKE2b-256 9090be76e167f6a2088c6e9854b972217df54cfa51f3533e569e54331d9b610e

See more details on using hashes here.

File details

Details for the file micro_statemachine-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: micro_statemachine-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 7.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for micro_statemachine-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 468c1270c05e4b82751737196c7ef89a1f2ce5329ca39ea5e126732e13ff2005
MD5 22e58c15def2f96cc34ab151e6865350
BLAKE2b-256 c4c166e867274ed84fdf3ffb23961ebaf1d7620333f045254a21590938256cec

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