pytransitions / transitions

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

get_triggers does not work #631

Closed jonra1993 closed 9 months ago

jonra1993 commented 9 months ago

Describe the bug Thanks for creating this package. I am starting to use it and I am looking for a way to detect if there is an initial or end state. I found in your documentation the get_triggers function but does not work as expected. Minimal working example

class Matter(object):
    def say_hello(self): print("hello, new state!")
    def say_goodbye(self): print("goodbye, old state!")

lump = Matter()

transitions = [
    { 'trigger': 'melt', 'source': 'solid', 'dest': 'liquid' },
    { 'trigger': 'evaporate', 'source': 'liquid', 'dest': 'gas' },
    { 'trigger': 'sublimate', 'source': 'solid', 'dest': 'gas' },
    { 'trigger': 'ionize', 'source': 'gas', 'dest': 'plasma' }
]

states = ['solid', 'liquid', 'gas', 'plasma']
m = Machine(lump, states, initial='liquid', transitions=transitions)
m.get_triggers('plasma')

Expected behavior The output on the trigger should be an empty list because it is the last state it always shows the same no matter which state you use.

image

Additional context This is the current output

image
aleneum commented 9 months ago

Hello @jonra1993,

triggers are only available on the source state. In your code example:

transitions = [
    { 'trigger': 'melt', 'source': 'solid', 'dest': 'liquid' },
    { 'trigger': 'evaporate', 'source': 'liquid', 'dest': 'gas' },
    { 'trigger': 'sublimate', 'source': 'solid', 'dest': 'gas' },
    { 'trigger': 'ionize', 'source': 'gas', 'dest': 'plasma' }
]

you can trigger "melt" from state "solid", "evaporate" from "liquid" and so on. Consequently, "melt" is not a trigger for "solid" since it cannot be triggered when in that state. As far as I can tell, "plasma" is not a source state for any of your transitions. Except auto transitions to_<state> there are no valid triggers. When you query triggers for "solid", you get "melt" and "sublimate":

from transitions import Machine

class Matter(object):
    def say_hello(self): print("hello, new state!")
    def say_goodbye(self): print("goodbye, old state!")

lump = Matter()

transitions = [
    { 'trigger': 'melt', 'source': 'solid', 'dest': 'liquid' },
    { 'trigger': 'evaporate', 'source': 'liquid', 'dest': 'gas' },
    { 'trigger': 'sublimate', 'source': 'solid', 'dest': 'gas' },
    { 'trigger': 'ionize', 'source': 'gas', 'dest': 'plasma' }
]

states = ['solid', 'liquid', 'gas', 'plasma']
m = Machine(lump, states, initial='liquid', transitions=transitions)
print(m.get_triggers('solid'))
# >>> ['to_solid', 'to_liquid', 'to_gas', 'to_plasma', 'melt', 'sublimate']
aleneum commented 9 months ago

If it is the auto transitions that you would not expect (which is the case according to your post), you can disable them by passing "auto_transitions=False" to the constructor:

from transitions import Machine

class Matter(object):
    def say_hello(self): print("hello, new state!")
    def say_goodbye(self): print("goodbye, old state!")

lump = Matter()

transitions = [
    { 'trigger': 'melt', 'source': 'solid', 'dest': 'liquid' },
    { 'trigger': 'evaporate', 'source': 'liquid', 'dest': 'gas' },
    { 'trigger': 'sublimate', 'source': 'solid', 'dest': 'gas' },
    { 'trigger': 'ionize', 'source': 'gas', 'dest': 'plasma' }
]

states = ['solid', 'liquid', 'gas', 'plasma']
m = Machine(lump, states, initial='liquid', transitions=transitions, auto_transitions=False)
print(m.get_triggers('plasma'))
# >>> []

They are enabled by default and are valid triggers. That's why get_triggers will return them when they are enabled. The relevant part from the documentation:


In addition to any transitions added explicitly, a to_«state»() method is created automatically whenever a state is added to a Machine instance. This method transitions to the target state no matter which state the machine is currently in:

lump.to_liquid()
lump.state
>>> 'liquid'
lump.to_solid()
lump.state
>>> 'solid'

If you desire, you can disable this behavior by setting auto_transitions=False in the Machine initializer.


jonra1993 commented 9 months ago

Hello @aleneum after using auto_transitions=False it worked as I expected thanks for your help. I have not noticed that before.