Skip to main content

Lightweight, decorator-based implementation of a Finite State Machine

Project description

Finite State Machine Banner

Finite State Machine

Latest Release Supports Python 3.6+ License: MIT Code Style: Black

Build Status codecov

Lightweight, decorator-based Python implementation of a Finite State Machine.

Table of Contents

Installation

pip install finite-state-machine

Usage

Subclass StateMachine and set the state instance variable:

from finite_state_machine import StateMachine, transition

class LightSwitch(StateMachine):
    def __init__(self):
        self.state = "off"
        super().__init__()

The transition decorator can be used to specify valid state transitions with an optional parameter for conditions. States can be of type: string, int, bool, Enum, or IntEnum. Can specify a single sate or a list of states for the source parameter; can only specify a single state as the target target. All condition functions need to return True for the transition to occur, else a ConditionsNotMet exception will be raised. Condition functions require the same positional position and keyword arguments present in the transition function.

    @transition(source="off", target="on", conditions=[light_is_off])
    def turn_on(self):
        # specify side effects

def light_is_off(machine):
    return machine.state == "off"

Can also specify an on_error parameter to handle situations where the transition function raises an exception:

    @transition(source="off", target="on", on_error="failed")
    def turn_on(self):
        raise ValueError

Example

from finite_state_machine import StateMachine, transition

class Turnstile(StateMachine):
    initial_state = "close"

    def __init__(self):
        self.state = self.initial_state
        super().__init__()

    @transition(source=["close", "open"], target="open")
    def insert_coin(self):
        pass

    @transition(source="open", target="close")
    def pass_thru(self):
        pass

REPL

In [2]: turnstile = Turnstile()

In [3]: turnstile.state
Out[3]: 'close'

In [4]: turnstile.insert_coin()

In [5]: turnstile.state
Out[5]: 'open'

In [6]: turnstile.insert_coin()

In [7]: turnstile.state
Out[7]: 'open'

In [8]: turnstile.pass_thru()

In [9]: turnstile.state
Out[9]: 'close'

In [10]: turnstile.pass_thru()
---------------------------------------------------------------------------
InvalidStartState                         Traceback (most recent call last)
<ipython-input-10-6abc6f4be1cd> in <module>
----> 1 turnstile.pass_thru()

~/state_machine.py in _wrapper(*args, **kwargs)
     32
     33             if self.state not in source:
---> 34                 raise InvalidStartState
     35
     36             for condition in conditions:

InvalidStartState:

The examples folder contains additional workflows.

State Diagram

State Machine workflows can be visualized using a state diagram.

finite-state-machine generates diagrams using Mermaid Markdown syntax, which can be viewed using the Mermaid Live Editor.

Use the fsm_draw_state_diagram command and point to State Machine workflow class that inheritences from StateMachine.

# class parameter is required
$ fsm_draw_state_diagram --class examples.turnstile:Turnstile

# initial_state parameter is optional
$ fsm_draw_state_diagram --class examples.turnstile:Turnstile --initial_state close

Contributing

  1. Clone repo
  2. Create a virtual environment
  3. pip install -r requirements_dev.txt
  4. Install pre-commit
  5. Set up pre-commit hooks in repo: pre-commit install

To install a package locally for development, run:

flit install [--symlink] [--python path/to/python]

Running Tests

pytest

Inspiration

This project is inspired by django-fsm. I wanted a decorator-based state machine without having to use Django.

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

finite-state-machine-0.5.0.tar.gz (2.4 MB view hashes)

Uploaded Source

Built Distribution

finite_state_machine-0.5.0-py3-none-any.whl (7.1 kB view hashes)

Uploaded Python 3

Supported by

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