1

I'm using the pytranstions library to build finite state machines.

Many users are storing the the config of the machine as a YAML file as can be seen in the replies by @wtgee and @limdautohere and here.

Now I can see how one would go about creating a YAML file to store to states and transtions. However, I'm not sure how one would also store the functions associated with the FSM in a YAML file.

Could someone show me how to do that?

Prabhakar
  • 1,138
  • 2
  • 14
  • 30
Parth Sharma
  • 441
  • 4
  • 19

1 Answers1

3

I am not aware of a convenient way to store the actual model and its function in YAML/JSON. I assume that the workflow you mentioned just stores the function names in JSON like this:

{    
  "states": ["A", "B", "C"],
  "initial": "A",
  "transitions": [
    {"trigger": "go", "source": "A", "dest": "B", "after": "func_A"},
    {"trigger": "go", "source": "B", "dest": "C", "after": "func_B"}
  ]
}

or YAML:

--- 
initial: A
states: 
  - A
  - B
  - C
transitions: 
  - 
    after: func_A
    dest: B
    source: A
    trigger: go
  - 
    after: func_B
    dest: C
    source: B
    trigger: go

When you store callbacks as Strings they will be evaluated the moment, the Event (in this case go) is triggered. Let's assume we have loaded the JSON/YAML above into a Python dictionary called d. When the dict keys are identical to transitions keywords, you can initialise a model like this:

from transitions import Machine
class Model:

    def func_A(self):
        print("func A")

    def func_B(self):
        print("func B")


model = Model()
m = Machine(model, **d)
model.go()
model.go()

You could add a description field model to your YAML which specifies which model class to load (e.g. 'module.models.TestModel') and import the model definition dynamically with importlib. Or you can define the required model implicitly (e.g. REST endpoint '/TestModel' initialises a TestModel). If you really want to store your model class definition in YAML, you may serialise the class definition with pickle/dill:

d["model"] = pickle.dumps(Model)
SerialisedModel = pickle.loads(d.pop("model"))
model = SerialisedModel()
m = Machine(model, **d)

If you just want to store the current state, you can serialise your Machine instance directly:

d["machine_object"] = pickle.dumps(m)

This is way less transparent than the previous approach though. In any case you should avoid defining callbacks with function references since this might lead to problems later on. Maybe pickle/dill can handle this as well but I would not count on it.

More sophisticated solutions have been proposed by Dave Kuhlmann here. His post includes approaches to a) export an FSM to JSON (Machine -> JSON), b) generate Python FSM code from JSON and c) inject an FSM into a class from JSON. All of this should be easily transferable to YAML.

aleneum
  • 2,083
  • 12
  • 29
  • Any idea on how to code in outputs for each state? For example, at state A, LED1 = on, state B, LED2 = on, etc – Mtk59 Oct 04 '21 at 14:40