symplehfsm Module

This module provides a simple but powerful way to define testable, hierarchical finite state machines. You should know how a state machine works. This implementation provides following features:

References:

  1. https://en.wikipedia.org/wiki/State_pattern.
  2. https://en.wikipedia.org/wiki/Finite-state_machine.
  3. https://en.wikipedia.org/wiki/UML_state_machine.

Todo

tutorial (explaining the abilities, entry/exit/actions/guard, different kinds of transitions)

Todo

set log level through event handling method?? so one could debug one instance of a statemachine at once.

Todo

python debug mode should log what it does but respect the debug level (?)

Versioning scheme based on: http://en.wikipedia.org/wiki/Versioning#Designating_development_stage

  +-- api change, probably incompatible with older versions
  |     +-- enhancements but no api change
  |     |
major.minor[.build[.revision]]
               |
               +-|* 0 for alpha (status)
                 |* 1 for beta (status)
                 |* 2 for release candidate
                 |* 3 for (public) release

Changed in version 2.0.2.0: introduced __versionnumber__ because __version__ should be a string according to PEP8. __versionnumber__ is a tuple containing int as used in the versioning scheme. This way its comparable out of the box, e.g. __versionnumber__ >= (2, 2, 3)

class symplehfsm.BaseHFSMTests(methodName='runTest')[source]

Bases: unittest.TestCase

Base TestCase that already defines test code for testing state machines build using an events and action interface (see: http://accu.org/index.php/journals/1548)

class RecordingActions[source]

Bases: object

This is a class that records the names of the functions called on it. Instead of writing a TestActions class, that records which action was activated, this class can be used. Just use the method names of the Action interface to compare the actually called method with the expected method in the tests.

Instancevariable :
 
captured_actions : list

List of captured method names that where called.

args : list

List of tuples ‘(args, kwargs)’ in the order the action methods where called. For each action method call there is a tuple inserted. If no arguments are passed then a empty tuple is inserted, e.g. ‘( ( ,), ( ,) )’

class BaseHFSMTests.TestVector(title, starting_state, event_func, expected_state, expected_actions)[source]

Bases: object

A TestVector is basically the data container needed to test one transition.

Parameters :
title : string

Description of this TestVector

starting_state : State

the state from which this transition starts

event_func : Func

the function handling the event

expected_state : State

the state that should be the current_state after the transition

expected_actions : list

list of expected actions to be compared with the captured actions

BaseHFSMTests.prove_one_transition(state_machine, resulting_actions, test_vector)[source]

Test one transition.

Parameters :
state_machine : StateMachine

the instance of the state machine to use

resulting_actions : Actions

instance of the class implementing the Actions that captures the actions needs to have an attribute ‘captured_actions’ which is a list of the captured actions

test_vector : TestVector

the TestVector to test

BaseHFSMTests.prove_transition_sequence(title, starting_state, event_funcs, expected_state, expected_actions, state_machine, resulting_actions)[source]

Test a sequence of transitions by passing in a sequence of event and checking the actions.

Parameters :
title : string

Description of this test

starting_state : State

the state from which this transition starts

event_funcs : Func

list of event functions to call

expected_state : State

the state that should be the current_state after the transition

expected_actions : list

list of expected actions to be compared with the captured actions

state_machine : SympleHFSM

the statemachine to test, an instance of SympleHFSM (or inheritet class)

resulting_actions : Actions

the actions used for the statemachine and for this test, has to have an attribute ‘captured_actions’

class symplehfsm.BaseState(name=None, parent=None)[source]

Bases: object

BaseState from which all hirarchical states should inherit. :Note: The state itself is ‘stateless’.

Parameters :
name : string

name of this state

parent : BaseState

Reference to the parent state, for the root state use None (only one state has None as parent since there is only one root)

exception InitialNotReplacedError[source]

Bases: exceptions.Exception

Exception raised if the initial state is not replaced.

exception BaseState.InitialNotSetError[source]

Bases: exceptions.Exception

Exception raised if the initial state is not set.

exception BaseState.InitialStateAlreadySetError[source]

Bases: exceptions.Exception

Exception is raised if initial state is already set.

exception BaseState.ParentAlreadySetError[source]

Bases: exceptions.Exception

Exception raised when a child has already a parent set

exception BaseState.ReplacementStateIsNotChildError[source]

Bases: exceptions.Exception

Exception raised if the replaced initial state is not a child.

exception BaseState.WrongParentError[source]

Bases: exceptions.Exception

Exception raised if the set parent is not the same state containint it as child

BaseState.add(child, initial=False)[source]

Adds another state as child to this state.

Parameters :
child : BaseState

the child state to add

initial : bool

defaults to False, if set, the child state is the initial state.

Raises :

ParentAlreadySetError if the childs parent is already set.

Raises :

InitialStateAlreadySetError if another initial state has already been defined.

BaseState.check_consistency()[source]

Checks the consistency of the state hierarchy. It checks mainly two things:

  • if the initial state is set for each state having a child or

    children, raises InitialNotSetError otherwise

  • if each child of a state has the parent attribute set to that

    state, raises WrongParentError otherwise

Deprecated since version 1.0.3.0: Use Structure.check_consistency() instead.

Raises :InitialNotSetError if no initial state has been set when this state has children.
Raises :WrongParentError if a child has not the parent set where it is a child.
BaseState.has_child(child_state)[source]

Checks if a state has a certain state as a child.

Parameters :
child_state : BaseState

child_state to check

Returns:

bool

BaseState.is_child(parent_state)[source]

Checks if this state is a child state of a parent state.

Parameters :
parent_state : BaseState

the parent state to check if this is its child state.

Returns:

bool

BaseState.remove(child, replace=None)[source]

Removes a child state. If the removed child state was the initial state it has to be replaced.

Parameters :
child : BaseState

child state to be removed.

replace : BaseState

the new initial state if the removed one was the initial state.

Raises :

InitialNotReplacedError if the initial state is removed but no other inital state is defined.

Raises :

ReplacementStateIsNotChildError if the initial replacement isn’t a child of this state.

exception symplehfsm.StateUnknownError[source]

Bases: exceptions.Exception

Exception raised if the state is not known in the structure.

class symplehfsm.Structure(name=None)[source]

Bases: object

This is the class holding the state machine structure, e.g. the number of states and their relationship (hierarchy) and its transitions in between them.

Ths is also the code that is shared by many instances of the same statemachine.

Parameters :
name : string

Optional name for this instance of this class.

exception EventAlreadyDefinedError[source]

Bases: exceptions.Exception

Exception raised when the event is already defined for that state

exception Structure.ParentUnkownError[source]

Bases: exceptions.Exception

Exception raised when the parent is unkown.

exception Structure.RootAlreadySetOrParentMissingError[source]

Bases: exceptions.Exception

Exception raised when the parent is missing or the root has already been set.

exception Structure.StateIdentifierAlreadyUsed[source]

Bases: exceptions.Exception

Exception raised when another state has the same state identifier.

Structure.add_state(state_identifier, parent, initial, entry_action=None, exit_action=None)[source]

Add a new node representing a state to the structure.

Parameters :
state_identifier : State identifier

A hashable identifier for that state (name, id, etc.). Has to be unique.

parent : State identifier

A hashable identifier of the state that is set as parent. The only one state will have set its parent to None, its the root state.

initial : bool

Only one of the children of a state can have this set to true, its the state that is used to descent to a leaf node of the structure.

entry_action : methodcaller

The methodcaller or a function behaving like a methodcaller. That calls the entry function on the actions object for that state. Optional, defaults to: None

exit_action : methodcaller

The methodcaller or a function behaving like a methodcaller. That calls the exit function on the actions object for that state. Optional, defaults to: None

Raises :

ParentUnkownError if the parent state identifier is not already known.

Raises :

RootAlreadySetOrParentMissingError if a second root node is added (maybe the parent is missing).

Raises :

StateIdentifierAlreadyUsed if the chosen state identifier is already in use.

Structure.add_trans(state, event, target, action=None, guard=None, name=None)[source]

Add a transition between two states for a certain event.

Parameters :
state : State identifier

A hashable identifier for that state (name, id, etc.).

event : event identifiert

A hashable event identifier. The same identifiert has to be used when calling handle_event on the state machine.

target : state identifier

The state this transition will lead too.

action : methodcaller

The transition action. Optional, default: None

guard : methodcaller

The guard method. Should return a boolean. If the return value is True, then the transition is carried out. Otherwise the event processing stops and nothing changes.

Raises :

StateUnknownError if either the state- or the target-identifier is not known.

Raises :

EventAlreadyDefinedError if this event is already defined for that state.

Structure.check_consistency()[source]

Checks the consistency of the state hierarchy. It checks mainly two things:

  • if the initial state is set for each state having a child or

    children, raises InitialNotSetError otherwise

  • if each child of a state has the parent attribute set to that

    state, raises WrongParentError otherwise

New in version 2.0.2.0.

Structure.do_optimize()[source]

Optimizes the event processing of the state machine. Call this method before you pass the structure to the constructor to create a state machine instance.

Note

It is not recommended to alter the structure after a call to this method, althought now it will just update the optimization.

Changed in version 2.0.2.0: Does not raise any exception anymore if called multiple times, it rebuilts internal structure used for optimization.

class symplehfsm.SympleDictHFSM[source]

Bases: object

Deprecated since version 1.0.3.0: (use SympleHFSM instead!)

class symplehfsm.SympleHFSM(structure, actions, name=None)[source]

Bases: object

Todo

should transition.action be able to return something to the caller?

Todo

should it be possible to pass in arguments for the transition action through the event handler method?

Base state machine logic. It implements the state transition logic.

Parameters :
structure : Structure

The state machine structure of states and transitions

actions : Actions

The object implementing the actions interface to be used by the state machine.

name : string

Optional, default: None. This name will be used for logging and printing.

New in version 2.0.2.0.

exception InitAlreadyCalledError[source]

Bases: exceptions.Exception

Exception raised if init is calle more than once.

New in version 2.0.2.0: Raised when init is called multiple times.

exception SympleHFSM.NotInitializedException[source]

Bases: exceptions.Exception

Exception raised if it is attemped to process an event before init has been called.

exception SympleHFSM.ReentrantEventException[source]

Bases: exceptions.Exception

Exception raised if an event is already processing.

SympleHFSM.current_state

Current state identifier or None if the state machine is not initialized

SympleHFSM.exit()[source]

Exits the state machine. Starting from the current_state it calls exit along the parent attribute on each state until the root state is exited.

SympleHFSM.handle_event(event)[source]

Handles the event and does a state change if needed. Raises a ‘ReentrantEventException’ if it is currently processing an event.

Parameters :
event_func : operator.methodcaller

A methodcaller instance pointed to the function that should be called on the state. For example if the method ‘a’ should be called on each state, then this should be ‘event_func = operator.methodcaller(‘a’, context)’

context : context

the context of the state machine, where certain methods and data is accesible (like the actions interface).

SympleHFSM.init(use_optimization=True)[source]

Initialize the state machine. It descents along the ‘initial’ attribute of the states and sets the current_state accordingly.

Parameters :
use_optimization : boolean

Default: True. If set to False the event handling method will always compute the entire path through the structure. Otherwise if set to True and the structure has been optmized, then the cached transition information is used.

Raises :

InitAlreadyCalledError if calle more than once.

SympleHFSM.set_state(state_identifier)[source]

Set the state directly as the current state without calling any entry or exit or any other events on any state. Don’t use it unless you need to (like initializing). Use with caution. Raises a ‘ReentrantEventException’ if it is currently processing an event. If the state is not known, then a ‘StateUnkownError’ is raised.

Note :

No actions are called! e.g.: exit, entry, transition action are not called, use init() instead!

Parameters :
state_identifier : state

State to which current state will point afterwards.

..versionchanged:: 2.0.2.0
Define the state to be set by its identifier instead of the state instance.
Raises :StateUnknownError if there is no state defined for given state_identifier.
Raises :ReentrantEventException if this method is called during a event is handled.
class symplehfsm.Transition(target_state, action=None, guard=None, name=None)[source]

Bases: object

This class holds the data needed for a transition.

Represents the transition between (composite) states (just the arrow in the state chart). The transition itself is ‘stateless’.

Parameters :
target_state : State

The state this transition should change to.

action : methodcaller

This should be a methodcaller object or a function behaving like a methodcaller. Such a function would have following signature (return value is ignored):

def f(actions)

A function behaving like a methodcaller looks like this:

f = lambda actions: actions.any_method_of_actions()
Note:only the function knows which function to call on the actions object.
guard : methodcaller

a methodaller of a function that behaves like a methodcaller returning a boolean, its signature is:

guard(actions) -> bool

If True is returned, then the transition will be followed, otherwise the transition will be blocked and event processing stops (no parent states are considered).

Previous topic

Welcome to sympleHFSM’s documentation!

Next topic

examples Package

This Page