Wednesday, May 6, 2009

Creating State Machines in Python

In the UML, state machines help modelers understand the various states the system in question goes through. When the system is in one state, the available behavior differs from when the system is in another state. Visualizing these states by drawing them as UML elements is an extremely powerful tool. State machines are not only powerful for examining the potential states of a given system, but also in helping to understand the transitions that could potentially take place between these states. In object-orientated systems, the concept of objects being in one state or another is often implemented in code, even if implicitly. Given that this is object-oriented code we are working with, and the whole idea behind object orientation is to assist with creating realistic abstractions, why not use the concept of state machines directly in code? The 0.1.6 version of the boduch Python library tries to realize this possibility. Although this is the first iteration of this new feature, with lots of work left to do, it seems promising. Below is an example of the new StateMachine class in use.

#Example; Using boduch state machines.

from boduch.state import StateMachine
from boduch.predicate import Equal

#Define two predicate functions used to return
#the predicate variables.
def get_o1():
return o1

def get_o2():
return o2

#The two predicate variables. Notice they
#aren't equal.
o1=1
o2=2

#Construct a state machine instance.
state_obj=StateMachine()

#Construct a predicate instance using the
#two previously defined predicate functions.
predicate_obj=Equal(get_o1, get_o2)

#Populate the state machine with two states.
state_obj.add_state("RUNNING")
state_obj.add_state("STOPPED")

#Add a state transition to the machine using
#the previously defined predicate. Once true,
#the machine will be in the target state.
state_obj.add_transition("STOPPED", predicate_obj)

if __name__=="__main__":
#Change the machine to a RUNNING state.
state_obj.change_state("RUNNING")
print state_obj.current_state

#Change the value of the predicate variable.
o2=1
print "o2 now equals o1"

#Check for any state changes.
state_obj.transition()
print state_obj.current_state

Be brief overview of the above code follows below.
  • Import the classes required. There are StateMachine and Equal.
  • Define the predicate functions. These functions are used by the predicate associated with the state machine. They are invoked each time the predicate is evaluated.
  • Define the predicate variables. These variables are global and are returned by the predicate functions. Any changes to these variables are reflected by invoking the predicate functions.
  • Create a new StateMachine instance. This is the main state machine involved in the example.
  • Create a new Equal predicate instance and use the predicate functions as operands.
  • Populate the state machine instance with some states. A state can be any Python object.
  • Add a new transition to the state machine using the previously defined Equal instance. When this predicate instance evaluates to true, the target state, in this case, "STOPPED", will become the active state.
  • Set the initial state to "RUNNING".
  • Change the value of one of the predicate variables. The predicate can now be considered equal.
  • Test the state machine for any state changes. Here, there are changes because the transition that was previously added to the state machine will evaluate to true, causing the target of the transition to become the active state.
  • The state machine is now in a "STOPPED" state.