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.1.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.1-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: micro_statemachine-0.1.1.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.1.tar.gz
Algorithm Hash digest
SHA256 3378406915a25901298bbc10a02e8befcb606a2e8a12cf427e33e811fb805db4
MD5 3bbfeead6ef9edfd68a052f7f63e1d61
BLAKE2b-256 0f28a2c13430e618d5334ab57068de3f735e6b64b170595f53643f862acae1dc

See more details on using hashes here.

File details

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

File metadata

  • Download URL: micro_statemachine-0.1.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c967c040e1a2cff8153d1493f821ca755912fdd8f7c075ad62edd89a453795fe
MD5 f514b9445a6e3753dc828984ab475599
BLAKE2b-256 f4a21d3d9fc38aabfef6fb36fbbc2fc7c74552cc7f6309e8da20c09bd05303e7

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