Skip to content

nazavode/automaton

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Automaton

Pythonic finite-state machines.

Build status Coverage report Documentation Status Codacy Badge PyPI License

Automaton is an easy to use, pythonic finite-state machine module for Python 3.4 or greater. The goal here is to have something to define finite-state machines in a minimal, straightforward and elegant way that enforces clarity and correctness.

Installation

Automaton is available on pypi, so to install it just use:

$ pip3 install python-automaton

Dependencies

Getting started

In order to define an automaton, just subclass a provided base:

from automaton import *

class TrafficLight(Automaton):

    go = Event("red", "green")
    slowdown = Event("green", "yellow")
    stop = Event("yellow", "red")

You're done: you now have a new automaton definition that can be instantiated to an initial state:

crossroads = TrafficLight(initial_state="red")
assert crossroads.state == "red"

Each event (also known as transition) is a class attribute defined by its source state and destination state. When an event fires, the automaton changes its state from the source to the destination: you can fire an event by calling it:

crossroads.go()
assert crossroads.state == "green"
crossroads.slowdown()
assert crossroads.state == "yellow"

An alternative way, more convenient if triggering events progammatically, is to call the event() method:

crossroads.event("stop")
assert crossroads.state == "red"

Correctness

Automaton enforces correctness in two ways:

  1. checking that the requested event is valid, that is a transition from the current state to the destination state exists in the state machine definition;
  2. checking whether the state graph representing the automaton is connected or not (that is it must have only
    one connected component).

So, if you try to trigger an invalid event...

crossroads = TrafficLight(initial_state="red")
crossroads.stop()

...you will end up with an error:

automaton.InvalidTransitionError: The specified event 'Event(source_states=('yellow',), dest_state='red')'
                                  is invalid in current state 'red'.

Again, trying to define a class like this...

class BrokenTrafficLight(Automaton):

   go = Event("red", "green")
   slowdown = Event("green", "yellow")
   # broken!
   stop = Event("black", "purple")

...will trigger an error:

automaton.DefinitionError: The state graph contains 2 connected components:
                           ['green', 'yellow', 'red'], ['purple', 'black']

How to visualize an automaton?

When things are getting complex and it seems that our automata are becoming autonomous life forms grasping to escape our control, it could be useful to have a human friendly representation of their behaviour.

You can ask for the transition table...

transitiontable(TrafficLight, fmt='rst')

...and you will be presented with a nice reStructuredText snippet:

========  ======  ========
Source    Dest    Event
========  ======  ========
green     yellow  slowdown
yellow    red     stop
red       green   go
========  ======  ========

You can ask for the state graph as well...

stategraph(TrafficLight, fmt='plantuml')

...and you'll end up with a proper PlantUML representation...

@startuml
    [*] --> red
    green --> yellow : slowdown
    red --> green : go
    yellow --> red : stop
@enduml

...that can of course be rendered through plantuml:

Traffic Light Graph

Keep your docstrings tidy!

Since automata are classes here, it would be great to have a textual representation of the automaton's behaviour in our docstrings. What about having one that updates itself in order to stay up-to-date with the actual code?

Here you have it:

class TrafficLight(Automaton):
    """ This is a pretty standard traffic light.

    This automaton follows the behaviour defined by
    the following transition table:

    {automaton:rst}

    """

    go = Event("red", "green")
    slowdown = Event("green", "yellow")
    stop = Event("yellow", "red")

Using a standard format specifier with the automaton keyword and the proper output format (e.g.: rst), the automaton representation will be inserted in the docstring during the class definition, just where it should be:

>>> print(TrafficLight.__doc__)
""" This is a pretty standard traffic light.

This automaton follows the behaviour defined by
the following transition table:

========  ======  ========
Source    Dest    Event
========  ======  ========
green     yellow  slowdown
yellow    red     stop
red       green   go
========  ======  ========

"""

Easy!

Documentation

You can find the full documentation at http://automaton.readthedocs.org.

Changes

1.3.1 (2017-02-05)

Changed

  • Fixed README.rst rendering on pypi.

1.3.0 (2017-02-05)

Added

  • Enabled access to all event's attributes from automaton instances.
  • New constructor parameter to initialize an automaton given an initial startup event.

Changed

  • Misc bugs fixed.
  • Tests cleanup.
  • Improved reference and documentation.

1.2.1 (2017-01-30)

Fixed

  • Severe distribution issue: package was missing some files.
  • Tox testing: py.test was running against source files, not against the package installed in tox virtualenv.

1.2.0 (2017-01-29)

Added

  • Custom format specifiers for Automaton definitions (classes and instances).
  • Auto-docstring completion: if requested, the automaton textual representation is automatically added to the __doc__ class attribute.

Changed

  • Refactored formatting functions to more streamlined and coherent interfaces.
  • Removed package, now the whole library lives in one module file.

1.1.0 (2017-01-28)

Added

  • Automaton representation as transition table or state-transition graph.

1.0.0 (2017-01-25)

Added

  • Functions to retrieve incoming and outgoing events from a state or a set of states.