Examples

Creating a simple machine

from automaton import machines
m = machines.FiniteMachine()
m.add_state('up')
m.add_state('down')
m.add_transition('down', 'up', 'jump')
m.add_transition('up', 'down', 'fall')
m.default_start_state = 'down'
print(m.pformat())

Expected output:

+---------+-------+------+----------+---------+
|  Start  | Event | End  | On Enter | On Exit |
+---------+-------+------+----------+---------+
| down[^] |  jump |  up  |    .     |    .    |
|    up   |  fall | down |    .     |    .    |
+---------+-------+------+----------+---------+

Transitioning a simple machine

m.initialize()
m.process_event('jump')
print(m.pformat())
print(m.current_state)
print(m.terminated)
m.process_event('fall')
print(m.pformat())
print(m.current_state)
print(m.terminated)

Expected output:

+---------+-------+------+----------+---------+
|  Start  | Event | End  | On Enter | On Exit |
+---------+-------+------+----------+---------+
| down[^] |  jump |  up  |    .     |    .    |
|   @up   |  fall | down |    .     |    .    |
+---------+-------+------+----------+---------+
up
False
+----------+-------+------+----------+---------+
|  Start   | Event | End  | On Enter | On Exit |
+----------+-------+------+----------+---------+
| @down[^] |  jump |  up  |    .     |    .    |
|    up    |  fall | down |    .     |    .    |
+----------+-------+------+----------+---------+
down
False

Running a complex dog-barking machine

from automaton import machines
from automaton import runners


# These reaction functions will get triggered when the registered state
# and event occur, it is expected to provide a new event that reacts to the
# new stable state (so that the state-machine can transition to a new
# stable state, and repeat, until the machine ends up in a terminal
# state, whereby it will stop...)

def react_to_squirrel(old_state, new_state, event_that_triggered):
    return "gets petted"


def react_to_wagging(old_state, new_state, event_that_triggered):
    return "gets petted"


m = machines.FiniteMachine()

m.add_state("sits")
m.add_state("lies down", terminal=True)
m.add_state("barks")
m.add_state("wags tail")

m.default_start_state = 'sits'

m.add_transition("sits", "barks", "squirrel!")
m.add_transition("barks", "wags tail", "gets petted")
m.add_transition("wags tail", "lies down", "gets petted")

m.add_reaction("barks", "squirrel!", react_to_squirrel)
m.add_reaction('wags tail', "gets petted", react_to_wagging)

print(m.pformat())
r = runners.FiniteRunner(m)
for (old_state, new_state) in r.run_iter("squirrel!"):
    print("Leaving '%s'" % old_state)
    print("Entered '%s'" % new_state)

Expected output:

+--------------+-------------+-----------+----------+---------+
|    Start     |    Event    |    End    | On Enter | On Exit |
+--------------+-------------+-----------+----------+---------+
|    barks     | gets petted | wags tail |    .     |    .    |
| lies down[$] |      .      |     .     |    .     |    .    |
|   sits[^]    |  squirrel!  |   barks   |    .     |    .    |
|  wags tail   | gets petted | lies down |    .     |    .    |
+--------------+-------------+-----------+----------+---------+
Leaving 'sits'
Entered 'barks'
Leaving 'barks'
Entered 'wags tail'
Leaving 'wags tail'
Entered 'lies down'

Creating a complex CD-player machine

from automaton import machines


def print_on_enter(new_state, triggered_event):
   print("Entered '%s' due to '%s'" % (new_state, triggered_event))


def print_on_exit(old_state, triggered_event):
   print("Exiting '%s' due to '%s'" % (old_state, triggered_event))


m = machines.FiniteMachine()

m.add_state('stopped', on_enter=print_on_enter, on_exit=print_on_exit)
m.add_state('opened',  on_enter=print_on_enter, on_exit=print_on_exit)
m.add_state('closed',  on_enter=print_on_enter, on_exit=print_on_exit)
m.add_state('playing',  on_enter=print_on_enter, on_exit=print_on_exit)
m.add_state('paused',  on_enter=print_on_enter, on_exit=print_on_exit)

m.add_transition('stopped', 'playing', 'play')
m.add_transition('stopped', 'opened', 'open_close')
m.add_transition('stopped', 'stopped', 'stop')

m.add_transition('opened', 'closed', 'open_close')

m.add_transition('closed', 'opened', 'open_close')
m.add_transition('closed', 'stopped', 'cd_detected')

m.add_transition('playing', 'stopped', 'stop')
m.add_transition('playing', 'paused', 'pause')
m.add_transition('playing', 'opened', 'open_close')

m.add_transition('paused', 'playing', 'play')
m.add_transition('paused', 'stopped', 'stop')
m.add_transition('paused', 'opened', 'open_close')

m.default_start_state = 'closed'

m.initialize()
print(m.pformat())

for event in ['cd_detected', 'play', 'pause', 'play', 'stop',
              'open_close', 'open_close']:
    m.process_event(event)
    print(m.pformat())
    print("=============")
    print("Current state => %s" % m.current_state)
    print("=============")

Expected output:

+------------+-------------+---------+----------------+---------------+
|   Start    |    Event    |   End   |    On Enter    |    On Exit    |
+------------+-------------+---------+----------------+---------------+
| @closed[^] | cd_detected | stopped | print_on_enter | print_on_exit |
| @closed[^] |  open_close |  opened | print_on_enter | print_on_exit |
|   opened   |  open_close |  closed | print_on_enter | print_on_exit |
|   paused   |  open_close |  opened | print_on_enter | print_on_exit |
|   paused   |     play    | playing | print_on_enter | print_on_exit |
|   paused   |     stop    | stopped | print_on_enter | print_on_exit |
|  playing   |  open_close |  opened | print_on_enter | print_on_exit |
|  playing   |    pause    |  paused | print_on_enter | print_on_exit |
|  playing   |     stop    | stopped | print_on_enter | print_on_exit |
|  stopped   |  open_close |  opened | print_on_enter | print_on_exit |
|  stopped   |     play    | playing | print_on_enter | print_on_exit |
|  stopped   |     stop    | stopped | print_on_enter | print_on_exit |
+------------+-------------+---------+----------------+---------------+
Exiting 'closed' due to 'cd_detected'
Entered 'stopped' due to 'cd_detected'
+-----------+-------------+---------+----------------+---------------+
|   Start   |    Event    |   End   |    On Enter    |    On Exit    |
+-----------+-------------+---------+----------------+---------------+
| closed[^] | cd_detected | stopped | print_on_enter | print_on_exit |
| closed[^] |  open_close |  opened | print_on_enter | print_on_exit |
|   opened  |  open_close |  closed | print_on_enter | print_on_exit |
|   paused  |  open_close |  opened | print_on_enter | print_on_exit |
|   paused  |     play    | playing | print_on_enter | print_on_exit |
|   paused  |     stop    | stopped | print_on_enter | print_on_exit |
|  playing  |  open_close |  opened | print_on_enter | print_on_exit |
|  playing  |    pause    |  paused | print_on_enter | print_on_exit |
|  playing  |     stop    | stopped | print_on_enter | print_on_exit |
|  @stopped |  open_close |  opened | print_on_enter | print_on_exit |
|  @stopped |     play    | playing | print_on_enter | print_on_exit |
|  @stopped |     stop    | stopped | print_on_enter | print_on_exit |
+-----------+-------------+---------+----------------+---------------+
=============
Current state => stopped
=============
Exiting 'stopped' due to 'play'
Entered 'playing' due to 'play'
+-----------+-------------+---------+----------------+---------------+
|   Start   |    Event    |   End   |    On Enter    |    On Exit    |
+-----------+-------------+---------+----------------+---------------+
| closed[^] | cd_detected | stopped | print_on_enter | print_on_exit |
| closed[^] |  open_close |  opened | print_on_enter | print_on_exit |
|   opened  |  open_close |  closed | print_on_enter | print_on_exit |
|   paused  |  open_close |  opened | print_on_enter | print_on_exit |
|   paused  |     play    | playing | print_on_enter | print_on_exit |
|   paused  |     stop    | stopped | print_on_enter | print_on_exit |
|  @playing |  open_close |  opened | print_on_enter | print_on_exit |
|  @playing |    pause    |  paused | print_on_enter | print_on_exit |
|  @playing |     stop    | stopped | print_on_enter | print_on_exit |
|  stopped  |  open_close |  opened | print_on_enter | print_on_exit |
|  stopped  |     play    | playing | print_on_enter | print_on_exit |
|  stopped  |     stop    | stopped | print_on_enter | print_on_exit |
+-----------+-------------+---------+----------------+---------------+
=============
Current state => playing
=============
Exiting 'playing' due to 'pause'
Entered 'paused' due to 'pause'
+-----------+-------------+---------+----------------+---------------+
|   Start   |    Event    |   End   |    On Enter    |    On Exit    |
+-----------+-------------+---------+----------------+---------------+
| closed[^] | cd_detected | stopped | print_on_enter | print_on_exit |
| closed[^] |  open_close |  opened | print_on_enter | print_on_exit |
|   opened  |  open_close |  closed | print_on_enter | print_on_exit |
|  @paused  |  open_close |  opened | print_on_enter | print_on_exit |
|  @paused  |     play    | playing | print_on_enter | print_on_exit |
|  @paused  |     stop    | stopped | print_on_enter | print_on_exit |
|  playing  |  open_close |  opened | print_on_enter | print_on_exit |
|  playing  |    pause    |  paused | print_on_enter | print_on_exit |
|  playing  |     stop    | stopped | print_on_enter | print_on_exit |
|  stopped  |  open_close |  opened | print_on_enter | print_on_exit |
|  stopped  |     play    | playing | print_on_enter | print_on_exit |
|  stopped  |     stop    | stopped | print_on_enter | print_on_exit |
+-----------+-------------+---------+----------------+---------------+
=============
Current state => paused
=============
Exiting 'paused' due to 'play'
Entered 'playing' due to 'play'
+-----------+-------------+---------+----------------+---------------+
|   Start   |    Event    |   End   |    On Enter    |    On Exit    |
+-----------+-------------+---------+----------------+---------------+
| closed[^] | cd_detected | stopped | print_on_enter | print_on_exit |
| closed[^] |  open_close |  opened | print_on_enter | print_on_exit |
|   opened  |  open_close |  closed | print_on_enter | print_on_exit |
|   paused  |  open_close |  opened | print_on_enter | print_on_exit |
|   paused  |     play    | playing | print_on_enter | print_on_exit |
|   paused  |     stop    | stopped | print_on_enter | print_on_exit |
|  @playing |  open_close |  opened | print_on_enter | print_on_exit |
|  @playing |    pause    |  paused | print_on_enter | print_on_exit |
|  @playing |     stop    | stopped | print_on_enter | print_on_exit |
|  stopped  |  open_close |  opened | print_on_enter | print_on_exit |
|  stopped  |     play    | playing | print_on_enter | print_on_exit |
|  stopped  |     stop    | stopped | print_on_enter | print_on_exit |
+-----------+-------------+---------+----------------+---------------+
=============
Current state => playing
=============
Exiting 'playing' due to 'stop'
Entered 'stopped' due to 'stop'
+-----------+-------------+---------+----------------+---------------+
|   Start   |    Event    |   End   |    On Enter    |    On Exit    |
+-----------+-------------+---------+----------------+---------------+
| closed[^] | cd_detected | stopped | print_on_enter | print_on_exit |
| closed[^] |  open_close |  opened | print_on_enter | print_on_exit |
|   opened  |  open_close |  closed | print_on_enter | print_on_exit |
|   paused  |  open_close |  opened | print_on_enter | print_on_exit |
|   paused  |     play    | playing | print_on_enter | print_on_exit |
|   paused  |     stop    | stopped | print_on_enter | print_on_exit |
|  playing  |  open_close |  opened | print_on_enter | print_on_exit |
|  playing  |    pause    |  paused | print_on_enter | print_on_exit |
|  playing  |     stop    | stopped | print_on_enter | print_on_exit |
|  @stopped |  open_close |  opened | print_on_enter | print_on_exit |
|  @stopped |     play    | playing | print_on_enter | print_on_exit |
|  @stopped |     stop    | stopped | print_on_enter | print_on_exit |
+-----------+-------------+---------+----------------+---------------+
=============
Current state => stopped
=============
Exiting 'stopped' due to 'open_close'
Entered 'opened' due to 'open_close'
+-----------+-------------+---------+----------------+---------------+
|   Start   |    Event    |   End   |    On Enter    |    On Exit    |
+-----------+-------------+---------+----------------+---------------+
| closed[^] | cd_detected | stopped | print_on_enter | print_on_exit |
| closed[^] |  open_close |  opened | print_on_enter | print_on_exit |
|  @opened  |  open_close |  closed | print_on_enter | print_on_exit |
|   paused  |  open_close |  opened | print_on_enter | print_on_exit |
|   paused  |     play    | playing | print_on_enter | print_on_exit |
|   paused  |     stop    | stopped | print_on_enter | print_on_exit |
|  playing  |  open_close |  opened | print_on_enter | print_on_exit |
|  playing  |    pause    |  paused | print_on_enter | print_on_exit |
|  playing  |     stop    | stopped | print_on_enter | print_on_exit |
|  stopped  |  open_close |  opened | print_on_enter | print_on_exit |
|  stopped  |     play    | playing | print_on_enter | print_on_exit |
|  stopped  |     stop    | stopped | print_on_enter | print_on_exit |
+-----------+-------------+---------+----------------+---------------+
=============
Current state => opened
=============
Exiting 'opened' due to 'open_close'
Entered 'closed' due to 'open_close'
+------------+-------------+---------+----------------+---------------+
|   Start    |    Event    |   End   |    On Enter    |    On Exit    |
+------------+-------------+---------+----------------+---------------+
| @closed[^] | cd_detected | stopped | print_on_enter | print_on_exit |
| @closed[^] |  open_close |  opened | print_on_enter | print_on_exit |
|   opened   |  open_close |  closed | print_on_enter | print_on_exit |
|   paused   |  open_close |  opened | print_on_enter | print_on_exit |
|   paused   |     play    | playing | print_on_enter | print_on_exit |
|   paused   |     stop    | stopped | print_on_enter | print_on_exit |
|  playing   |  open_close |  opened | print_on_enter | print_on_exit |
|  playing   |    pause    |  paused | print_on_enter | print_on_exit |
|  playing   |     stop    | stopped | print_on_enter | print_on_exit |
|  stopped   |  open_close |  opened | print_on_enter | print_on_exit |
|  stopped   |     play    | playing | print_on_enter | print_on_exit |
|  stopped   |     stop    | stopped | print_on_enter | print_on_exit |
+------------+-------------+---------+----------------+---------------+
=============
Current state => closed
=============

Creating a complex CD-player machine (using a state-space)

This example is equivalent to the prior one but creates a machine in a more declarative manner. Instead of calling add_state and add_transition a explicit and declarative format can be used. For example to create the same machine:

from automaton import machines


def print_on_enter(new_state, triggered_event):
   print("Entered '%s' due to '%s'" % (new_state, triggered_event))


def print_on_exit(old_state, triggered_event):
   print("Exiting '%s' due to '%s'" % (old_state, triggered_event))

# This will contain all the states and transitions that our machine will
# allow, the format is relatively simple and designed to be easy to use.
state_space = [
    {
        'name': 'stopped',
        'next_states': {
            # On event 'play' transition to the 'playing' state.
            'play': 'playing',
            'open_close': 'opened',
            'stop': 'stopped',
        },
        'on_enter': print_on_enter,
        'on_exit': print_on_exit,
    },
    {
        'name': 'opened',
        'next_states': {
            'open_close': 'closed',
        },
        'on_enter': print_on_enter,
        'on_exit': print_on_exit,
    },
    {
        'name': 'closed',
        'next_states': {
            'open_close': 'opened',
            'cd_detected': 'stopped',
        },
        'on_enter': print_on_enter,
        'on_exit': print_on_exit,
    },
    {
        'name': 'playing',
        'next_states': {
            'stop': 'stopped',
            'pause': 'paused',
            'open_close': 'opened',
        },
        'on_enter': print_on_enter,
        'on_exit': print_on_exit,
    },
    {
        'name': 'paused',
        'next_states': {
            'play': 'playing',
            'stop': 'stopped',
            'open_close': 'opened',
        },
        'on_enter': print_on_enter,
        'on_exit': print_on_exit,
    },
]

m = machines.FiniteMachine.build(state_space)
m.default_start_state = 'closed'
print(m.pformat())

Expected output:

+-----------+-------------+---------+----------------+---------------+
|   Start   |    Event    |   End   |    On Enter    |    On Exit    |
+-----------+-------------+---------+----------------+---------------+
| closed[^] | cd_detected | stopped | print_on_enter | print_on_exit |
| closed[^] |  open_close |  opened | print_on_enter | print_on_exit |
|   opened  |  open_close |  closed | print_on_enter | print_on_exit |
|   paused  |  open_close |  opened | print_on_enter | print_on_exit |
|   paused  |     play    | playing | print_on_enter | print_on_exit |
|   paused  |     stop    | stopped | print_on_enter | print_on_exit |
|  playing  |  open_close |  opened | print_on_enter | print_on_exit |
|  playing  |    pause    |  paused | print_on_enter | print_on_exit |
|  playing  |     stop    | stopped | print_on_enter | print_on_exit |
|  stopped  |  open_close |  opened | print_on_enter | print_on_exit |
|  stopped  |     play    | playing | print_on_enter | print_on_exit |
|  stopped  |     stop    | stopped | print_on_enter | print_on_exit |
+-----------+-------------+---------+----------------+---------------+

Note

As can be seen the two tables from this example and the prior one are exactly the same.