pytransitions / transitions

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

Can I transition from A to B automatically ? #591

Closed euri10 closed 1 year ago

euri10 commented 1 year ago

Thank you for taking the time to report a bug! Your support is essential for the maintenance of this project. Please fill out the following fields to ease bug hunting and resolving this issue as soon as possible:

Describe the bug

Sorry it's a mix of a bug report feat request and help support :) Let me expain.

I'm trying to find a way to transition from state A to state B immediately without having to explicitely write the trigger in my code.

It is something that in xstate is achievable by setting the type of the event as always (see https://stately.ai/registry/editor/share/b746ca02-7faf-46e3-b86d-fdbdb4b3ac36 for an example)

At first I thought declaring a transition with a condition that returns True all the time would suffice, but it's not, I still have to write m.always()

In trying to write what I thought would be ok I face this RecursionError I really dont understand.

    if isinstance(state, Enum):
RecursionError: maximum recursion depth exceeded while calling a Python object

But my real interest is in knowing if this is something achievable in transitions ie when the machine reaches state A, it automatically transitions to state B ?

thanks for your help

Minimal working example

# your minimal working example
from enum import Enum

from transitions import Machine

class States(str, Enum):
    A = "A"
    B = "B"

def always():
    return True

transitions = [
    {"trigger": "always", "source": States.A, "dest": States.B, "conditions": "always"}
]

m = Machine(states=States, transitions=transitions, initial=States.A)

assert m.is_A()
print(m.state)
m.always()
assert m.is_B()
print(m.state)

Expected behavior A clear and concise description of what you expected to happen.

I would expect the above to reach te B state after always function, but it crashes, not sure if this is expected

Additional context Add any other context about the problem here.

aleneum commented 1 year ago

Hello @euri10,

But my real interest is in knowing if this is something achievable in transitions ie when the machine reaches state A, it automatically transitions to state B ?

yes, it is possible. there is a variety of ways to achieve this

I would expect the above to reach te B state after always function, but it crashes, not sure if this is expected

I'd expect your code to hit the recursion limit as it triggers an infinite loop. By passing "always" as a string you will call the trigger attached to the machine that has been added with "trigger": "always". Your function always could be referenced (e.g. "conditions": always) but I would discourage this since a condition that is always true should be omitted.

If you want to loop through states without calling triggers explicitly, ordered transitions might be what you are looking for.

from transitions import Machine
from enum import Enum, auto

class States(Enum):
    A = auto()
    B = auto()
    C = auto()
    D = auto()
    E = auto()
    F = auto()

# callbacks in 'after_state_change' are executed after a successful transition
# after each successful transition, a callback 'next' assigned to machine should be called
# 'next' will be added in the next step
m = Machine(states=States, initial="A", after_state_change="next")
# add ordered transitions (based on the sequential order passed to states) with trigger 'next'.
# this will progress through all states unless the lambda passed to `unless` resolves to true.
m.add_ordered_transitions(trigger="next", unless=lambda: m.is_F())
assert m.is_A()
m.next()
assert m.is_F()
# To call a callback/trigger only when a certain state is entered:
# m.on_enter_<state>(cb) -> m.on_enter_B("next")

If you want to 'stack' events, have a look at queued transitions.

As I said there is a variety of ways to achieve this. I'd suggest that Stackoverflow is a better place to ask such questions:

If SO is not your cup of tea, this might be a suitable topic for GitHub discussions.