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:
- https://en.wikipedia.org/wiki/State_pattern.
- https://en.wikipedia.org/wiki/Finite-state_machine.
- 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
debug mode should log what it does but respect the debug level (?)
Todo
move BaseState and Transition into Structure class (because they are kinda private classes) ??
-
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.
|
-
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
..todo: move check_consistency to Structure class?
-
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.
|
-
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, parent, initial, entry_action=None, exit_action=None)[source]
Add a new node representing a state to the structure.
Parameters : |
- state : 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
|
-
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.
|
-
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 : | Dont change the structure after calling this method (no add/remove states nor transitions). |
Note : | Only the first call will actually optimize the structure, all following calls will do noting. |
-
class symplehfsm.SympleDictHFSM(structure, actions, name=None)[source]
Bases: object
Todo
should transition.action be able to return something to the caller?
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.
|
-
exception NotInitializedException[source]
Bases: exceptions.Exception
Exception raised if it is attemped to process an event before
init has been called.
-
exception SympleDictHFSM.ReentrantEventException[source]
Bases: exceptions.Exception
Exception raised if an event is already processing.
-
SympleDictHFSM.current_state
Current state pointer, could be None if the state machine is not initialized
-
SympleDictHFSM.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.
-
SympleDictHFSM.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).
|
-
SympleDictHFSM.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.
|
-
SympleDictHFSM.set_state(new_state)[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.
Parameters : |
- new_state : state
State to which current state will point afterwards.
|
-
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):
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:
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).
|