• Stars
    star
    103
  • Rank 333,046 (Top 7 %)
  • Language
    Python
  • License
    MIT License
  • Created over 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

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):
        # transition function
        # logic to define what happens to "complete a transition"
        # ex. update database record,
        ...

def light_is_off(machine):
    # condition function, first param will always be the state machine class
    # return a boolean to specify if a transition is valid
    return machine.state == "off"

Can also specify an on_error state 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.

Asynchronous Support

finite-state-machine can be used to build both synchronous and asynchronous State Machines. The @transition decorator supports transition functions and condition functions as follows:

Synchronous transition function Asynchronous transition function
Synchronous condition function βœ… ❌
Asynchronous condition function βœ… βœ…

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 inherits 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.

More Repositories

1

talks

Code, slides, and documentation for the talks I have given.
Jupyter Notebook
106
star
2

falcon-apispec

apispec plugin that generates OpenAPI specification (aka Swagger Docs) for Falcon web applications.
Python
45
star
3

notes

Notes from tech books I'm reading.
33
star
4

linux-dash-docker

linux-dash in a container
Dockerfile
17
star
5

falcon-batteries-included

Building out an opinionated Falcon REST API for a movie recommendation project.
Python
15
star
6

reddit-top-posts-scrapy

Scrape top posts from list of subreddits and insert into MongoDB
Python
13
star
7

mute-microphone-mac-streamdeck-plugin

Stream Deck plugin to allow users to mute their microphone on MacOS. Written in Python.
Python
11
star
8

historical-results-dashboard

Historical Results Soccer Dashboard
Python
7
star
9

reddit-top-posts-website-flask

CSS
6
star
10

sivtools

Aly Sivji's Python toolbox
Python
6
star
11

flask-family-tree-api

Practice project to create a Family Tree API using Flask, SQLAlchemy, Alembic, Marshmallow, and pytest.
Python
5
star
12

divvy-station-heatmap-folium

Jupyter Notebook
4
star
13

flask-mega-tutorial

Working thru https://blog.miguelgrinberg.com/
Python
4
star
14

appointment-manager

Flask-based project for managing appointments in a healthcare setting.
Python
4
star
15

crud-api-example

Falcon-based CRUD backend with tests via pytest and CI via drone.
Python
3
star
16

blog-notebooks

Blog posts, in Jupyter notebook form.
Jupyter Notebook
3
star
17

advent-of-code

Python
3
star
18

javascript-practice

Deliberate pratice is the only way to get beter.
JavaScript
2
star
19

flask-rq-example

Practice setting up flask with rq for async jobs.
Python
2
star
20

alysivji.github.io

Result of generating site using Pelican
HTML
2
star
21

soccer-visualizations

Practice project using Airflow.
Python
2
star
22

data-science-package

This is an sample repository that demonstrates how to package data science code for reuse.
Python
2
star
23

reddit-top-posts-email-from-mongodb

Sending a HTML emails of scraped reddit data from MongoDB using MongoEngine, Jinja2, and Requests
Python
1
star
24

sivmetrics-backend

Jupyter Notebook
1
star
25

sam-cookiecutter

Cookiecutter for new AWS SAM Project
Python
1
star
26

circleci-trigger

1
star
27

word-count

This project will list top word frequencies for given text.
Python
1
star
28

javascript-games

Practicing JavaScript skills by making games
JavaScript
1
star
29

coveralls-example

Integrate with Coveralls
Makefile
1
star
30

mermaid-charts

Generate charts using a markdown-like syntax.
1
star
31

sqlalchemy-orm-cookiecutter

Bootstrap project for containerized application that requires Postgres, SQLAlchemy ORM, and Alembic.
Python
1
star
32

alysivji

1
star
33

tic-tac-toe

Creating tic-tac-toe
Python
1
star
34

twitter-oauth1-flow

Example of Twitter 3-legged flow with OAuth1
Python
1
star
35

street-team

Tools to simplify work related to event marketing
Python
1
star
36

sqlalchemy-core-cookiecutter

Bootstrap project for containerized application that requires Postgres (database), SQLAlchemy (Core), and Alembic (migrations).
Makefile
1
star