Gulzar
Gulzar

Reputation: 27946

State history in Pytransitions

I am using Pytransitions and I have some state machine, for example

from transitions import Machine
from transitions import EventData


class Matter(object):
    def __init__(self):
        transitions = [
            {'trigger': 'heat', 'source': 'solid', 'dest': 'liquid'},
            {'trigger': 'heat', 'source': 'liquid', 'dest': 'gas'},
            {'trigger': 'cool', 'source': 'gas', 'dest': 'liquid'},
            {'trigger': 'cool', 'source': 'liquid', 'dest': 'solid'}
        ]
        self.machine = Machine(
                model=self,
                states=['solid', 'liquid', 'gas'],
                transitions=transitions,
                initial='solid',
                send_event=True
        )

    def on_enter_gas(self, event: EventData):
        print(f"entering gas from {event.transition.source}")

    def on_enter_liquid(self, event: EventData):
        print(f"entering liquid from {event.transition.source}")

    def on_enter_solid(self, event: EventData):
        print(f"entering solid from {event.transition.source}")

Is there some library-supported way to keep track of all the history of the states?

What I am manually doing now is on init:

after_state_change=lambda event: self._on_after_state_change(event)

and

def _on_after_state_change(self, event: EventData):
    to_state = event.transition.dest
    self._history_df.append({
        "start_index": self._index,
        "state": to_state
    }, ignore_index=True)

I was wondering if the library supported some query-able logging, meaning not just dumping events to a file, but also being able to keep them in a list for example.

Upvotes: 1

Views: 1043

Answers (1)

aleneum
aleneum

Reputation: 2273

Transitions does not feature a built-in history. If you just want to track the previous states, this GitHub issue might be helpful:

Since the stateful object with transitions is the model you can use the state property to track state changes:

from transitions import Machine
import collections


# if you like your machine to act as a model, let Model inherit from Machine
class Model(object):

    def __init__(self, history_length):
        self.state_history = collections.deque(maxlen=history_length)

    @property
    def state(self):
        return self.state_history[-1]

    @state.setter
    def state(self, value):
        self.state_history.append(value)


model = Model(3)
machine = Machine(model, states=['A', 'B', 'C', 'D'], initial='A')
print(model.state)  # >>> A

model.to_B()
model.to_C()
model.to_A()
model.to_D()

print("->".join(model.state_history))  # >>> C->A->D

This only works with model_attribute='state' (default value). In case you altered the name of the 'stateful' model field, you need to adjust the above mentioned properties accordingly.

If you are interested in all EventData, your approach seems to be the go to solution. You can omit the lambda though. after_state_change=self._on_after_state_change or after_state_change='_on_after_state_change' (if the method is part of the model) should be enough.

The above mentioned dequeue (or queue.Queue) is 'queryable' in a way that you can use filters or list comprehensions with it. If you need something more sophisticated, pandas (which you probably already use considering your history name) or sqlite might be more suitable.

Upvotes: 2

Related Questions