pytransitions / transitions

A lightweight, object-oriented finite state machine implementation in Python with many extensions
MIT License
5.49k stars 525 forks source link

Is it possible to add and trigger a transition within on_enter state callback? #605

Closed matlom closed 1 year ago

matlom commented 1 year ago

Checklist

aleneum commented 1 year ago

Hello @matlom,

first, the recommended (leaner) solution that relies on auto transitions (which I will also post on SO):

    def to_stored(self):
        stored = self.stored.pop()
        self.trigger(f"to_{stored}")

second, the 'quick fix' if you need to work with temporary added transitions:

    def to_stored(self):
        stored = self.stored.pop()
        temp_transition = {
            'trigger': 'jump',
            'source': self.state,
            'dest': stored
        }
        # print(self.states) [1]
        with self():  # <-- add this line and indent the rest
            # print(self.states) [2]
            self.add_transition(**temp_transition)
            self.trigger('jump')
            self.remove_transition(**temp_transition)

I won't post this on SO since it does not feel right that this is necessary but there is a reason why this is currently required. HierarchicalMachine (HSM) works with 'stacks' or 'scopes' which is transparent to self.state (the stateful model part). When you call add_transition within a callback of a nested state, the HSM will only 'see' the states and transitions of the current state. See the output of [1] and [2]:

[1] -> OrderedDict([('standing', <NestedState('standing')@4316328384>), ('walking', <NestedState('walking')@4316328576>), ('caffeinated', <NestedState('caffeinated')@4316328816>)])
[2] -> OrderedDict([('root', <NestedState('root')@4316328048>)])

HSM itself acts as a context manager. By calling with self() you instruct HSM to use its global context.

matlom commented 1 year ago

Thanks @aleneum, second option works for me! I cannot use auto transitions because I have a very large number of states.