A library for constructing finite state machines
Python Makefile Shell
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
doc
machinist
.gitattributes
.gitignore
.travis.yml
LICENSE
MANIFEST.in
README.rst
setup.py
versioneer.py

README.rst

https://travis-ci.org/ClusterHQ/machinist.png https://coveralls.io/repos/hybridcluster/machinist/badge.png

Installation

$ pip install machinist

Machinist's automatic structured logging depends on eliot. Logging is declared as a Machinist extra so you can automatically install this dependency:

$ pip install machinist[logging]

Defining Inputs, Outputs, and States

Inputs, outputs, and states are all twisted.python.constants.NamedConstant. Collections of inputs, outputs, and states are twisted.python.constants.Names.

class TurnstileInput(Names):
    FARE_PAID = NamedConstant()
    ARM_UNLOCKED = NamedConstant()
    ARM_TURNED = NamedConstant()
    ARM_LOCKED = NamedConstant()

class TurnstileOutput(Names):
    ENGAGE_LOCK = NamedConstant()
    DISENGAGE_LOCK = NamedConstant()

class TurnstileState(Names):
    LOCKED = NamedConstant()
    UNLOCKED = NamedConstant()
    ACTIVE = NamedConstant()

Defining the Transitions

A transition is defined as an input to a state mapped to a series of outputs and the next state.

These transitions are added to a transition table.

table = TransitionTable()

# Any number of things like this
table = table.addTransitions(
    TurnstileState.UNLOCKED, {
        TurnstileInput.ARM_TURNED:
            ([TurnstileOutput.ENGAGE_LOCK], TurnstileState.ACTIVE),
    })

If an input is received for a particular state for which it is not defined, an machinist.IllegalInput would be raised. In the example above, if FARE_PAID is received as an input while the turnstile is in the UNLOCKED state, machinist.IllegalInput will be raised.

Putting together the Finite State Machine

To build an instance of a finite state machine from the transition, pass the inputs, outputs, states, and table (previously defined) to the function machinist.constructFiniteStateMachine.

turnstileFSM = constructFiniteStateMachine(
    inputs=TurnstileInput,
    outputs=TurnstileOutput,
    states=TurnstileState,
    table=table,
    initial=TurnstileState.LOCKED,
    richInputs=[]
    inputContext={},
    world=MethodSuffixOutputer(Turnstile(hardware)),
)

Note that richInputs must be passed and it must be a list of IRichInput providers mapped to the same input symbols (parameter inputs) the FSM is created with.

Turnstile is a class with methods named output_XXX, where XXX is one of the outputs. There should be one such method for each output defined.

Transitioning the Finite State Machine

To provide an input to the FSM, receive on the FSM must be called with an instance of an IRichInput provider.

turnstileFSM.receive(TurnstileInput.FARE_PAID)

Further Reading

For the rest of the example code, see doc/turnstile.py.

For more discussion of the benefits of using finite state machines, see: