Code contained in statecharts

A statechart can write code to be executed under some circumstances. For example, the on entry property on a statechart, guard or action on a transition or the on entry and on exit property for a state.

In PySS, these pieces of code can be evaluated and executed by Evaluator instances.

Code evaluator

An Evaluator must provide two methods and an attribute:

class pyss.evaluator.Evaluator

Base class for any evaluator.

An instance of this class defines what can be done with piece of codes contained in a statechart (condition, action, etc.).

context

The context of this evaluator. A context is a dict-like mapping between variables and values that is expected to be exposed through evaluate_condition and execute_action.

evaluate_condition(condition: str, event: pyss.model.Event) → bool

Evaluate the condition of a guarded transition.

Parameters:
  • condition – A one-line Boolean expression
  • event – The event (if any) that could fire the transition.
Returns:

True or False

execute_action(action: str, event: pyss.model.Event=None) → list

Execute given action (multi-lines code) and return a (possibly empty) list of internal events to be considered by a statechart simulator.

Parameters:
  • action – A (possibly multi-lined) code to execute.
  • event – an Event instance in case of a transition action.
Returns:

A possibly empty list of Event instances

By default, PySS provides two built-in Evaluator subclasses:

  • A DummyEvaluator that always evaluate a guard to True and silently ignores action, on entry and on exit. Its context is an empty dictionary.
  • A PythonEvaluator that brings Python into our statecharts and which is used by default.

Built-in Python code evaluator

An instance of PythonEvaluator can evaluate and execute Python code expressed in the statechart. The key point to understand how it works is the concept of context, which is a dictionary-like structure that contains the data that are exposed to the pieces of code of the statechart (ie. override __locals__).

As an example, consider the following partial statechart definition.

statechart:
# ...
on entry: x = 1
states:
  - name: s1
    on entry: x += 1

When the statechart is initialized, the context of a PythonEvaluator is {'x': 1}. When s1 is entered, the code will be evaluated with this context. After the execution of x += 1, the context associates 2 to x.

When a PythonEvaluator instance is initialized, a prepopulated context can be specified:

from pyss.evaluator import PythonEvaluator
import math as my_favorite_module

evaluator = PythonEvaluator({'x': 1, 'math': my_favorite_module})

By default, the context exposes an Event and a send function. They can be used to send internal event to the simulator, eg., on entry: send(Event('Hello World!')).

Unless you override its entry in the context, the __builtins__ of Python are automatically exposed. This implies you can use nearly everything from Python in your code.

class pyss.evaluator.PythonEvaluator(initial_context: dict=None)

Evaluator that interprets Python code.

An initial context can be provided, as a dictionary (will be used as __locals__). Unless overridden, the context also exposes:

  • The __builtins__ of Python,
  • The Event class and,
  • A send method that takes Event instances and send them to the statechart.

When one of evaluate_condition or execute_action method is called with an event parameter, it is also exposed by the context through the key event.

Parameters:initial_context – a dictionary that will be used as __locals__

If an exception occurred while executing or evaluating a piece of code, it is propagated by the evaluator.

Examples

Consider the following statechart that performs simple arithmetic operations.

statechart:
  name: statechart with executable content
  on entry: |
    x = 1
    y = 2
  initial: s1

  states:
  - name: s1
    transitions:
    - target: s2
      action: |
        x += 1
  - name: s2
    on_entry: |
      y += 1
    transitions:
    - target: s3
  - name: s3
    type: final  # x = 2, y = 3

The statechart here shows a more intricate example.