State Machines in Python

In this unit, you will learn how to implement state machines in Python. Achieving this is valuable for several reasons:

For the Semester Project

To have your code in sync with your state machine diagrams and handle concurrency in an effective way, you will program parts of your semester project using STMPY.

Benefits of State Machines in Code

State machines are useful also at the code level when there is a lot of concurrency going on, and a component needs to order a set of events that can come more or less in any order. You may think that there is support for threads in most programming languages, and that this is enough to handle concurrency. However, sometimes also this support is very specific to certain problems and not simple to learn. For example, when learning Java, which has extensive support for concurrency, the concurrency part is considered the most difficult one, and most developers have to look into a book when doing more elaborate tasks. Python has even less mature support for concurrency and synchronization, and also this is hard to get right. Just look at this:

Concurrent behavior starts simple and gets quickly so complex that even the best developers give up when using the wrong approach.  

Many programmers cannot even handle very simple threading problems correctly.

With state machines, we have a simple model that scales nicely with various challenges in concurrency. This does not make concurrency problems vanish, but you have a good tool to solve challenges. State machines are easy to debug, since you can check in which current state they are, and because you can look at a trace of events that explains how they got there.

It turns out, that state machines are also relatively easy to implement, and we can run many different state machines within the same thread. This means we can do a lot of concurrent things in just own thread, and just one busy while loop, which can make our programs quite efficient.

But how can a state machine do it? With state machines, we have a notation that motivates us to define behavior in short pieces, which are the transitions. Think of them as short little pieces of thread, as illustrated in the figure below. Threads of different color belong to different tasks. But because these tasks are executed concurrently, the state machine sorts them and executes one of them at a time. The states in between are just the ordering criteria between them. (If this analogy is not entirely clear to you now, don't worry. You will understand more once we implement state machines.)

A state machine takes concurrent behavior, transformes it into many transitions and sorts their execution, so that only one of them is executed at the same time.  

What you also gain immediately is the ability for parallel computing. State machines can be grouped and execute on a single process or thread, then occupying different CPUs. This is very helpful when we have a lot of tasks that also take some time, to utilize all computing resources we may have.

From Diagram to Code

There are many different possibilities when we want to come from a diagram to code. For this course, we have selected the following strategy:

Despite the manual step with writing the code, our solution still has good traceability. This means it is be relatively easy to see how model (the state machine) and the code (in Python) correspond to each other. We can trace back the code from the model, and vice-versa, look at the code and trace back where in the model it is specified.

Alternatives would be to generate code automatically from diagrams, but this requires a good state machine editor and very formal syntax for it. Yet another possibility would be to feed the state machine diagram directly to an interpreter that then executes it. But again, this would require a very detailed syntax for actions in the state machine. Instead, with STMPY, we have the flexibility to write state machines how we want, and then use all of Python, just with a little manual effort to code the state machines as Python data structures (dicts) and some extra glue code.

State Machine Execution

As a first overview, have a look at the following whiteboard sketch:

The original sketch from a whiteboard when planning the first release for STMPY.  

We will go through machines and drivers and their Python API in the following notebooks.

Jupyter Notebooks Introducing STMPY

To introduce STMPY you step by step and while executing code, we created a series of Python notebooks. You have two possibilities to run them

Alternative 1: Install Notebooks on Your Computer

In this solution, you run Jupyter on your own computer.

python3 -m pip install --upgrade stmpy

We also need our own widgets in the notebooks, so run the following two commands:

python3 -m pip install ipywidgets
jupyter nbextension enable --py widgetsnbextension

Alternative 2: Use Binder

Binder runs notebooks online. Use this method if you have troubles running the notebooks on your own computer.


Run the Notebooks

The following notebooks will introduce you to STMPY step by step. At this time, you don't have to program anything on your own, just execute the cells and observe what's happening. So make sure you are not rushing through the cells too fast.

State Machines in Python - Part 1

State Machines in Python - Part 2

State Machines in Python - Part 3

State Machines in Python - Part 4