Closed lucaswhipple-sfn closed 4 months ago
Update:
I made a mock machine that works, but it uses the private method _add_callback to add a callback to a transition list.
This has a disadvantage - it binds the condition to every transition in the transitionlist, which for some cases may be desirable, but I can imagine only wanting it bound to the transitionlist instead of the elements of the transitionlist.
I think this will resolve my issue in the short-term, but I think it would be worthwhile to fix the decorator bug and also perhaps correct the documentation.
from statemachine import StateMachine, State, exceptions
from statemachine.exceptions import TransitionNotAllowed
class ExampleStateMachine(StateMachine):
idle = State("idle", initial = True)
moving = State("Moving!")
stopped = State("Stopped")
forbidden_state = State("Forbidden!")
start_moving = moving.from_(idle,stopped)
move_from_stop = stopped.to(moving)
stop_from_moving = moving.to(stopped)
cycle = move_from_stop | stop_from_moving
#@cycle.cond
def cycle_cond(self):
print("This should run")
return True
def on_transition(self):
print("This should print on a transition!")
def always_return_false(self):
return False
forbidden_transition = idle.to(forbidden_state, cond = always_return_false)
cycle._add_callback(cycle_cond, "cond")
exm = ExampleStateMachine()
try:
exm.forbidden_transition()
except TransitionNotAllowed:
print("Transition not allowed")
exm.start_moving()
exm.cycle()
print(exm.current_state)
exm.move_from_stop()
output:
Transition not allowed
This should print on a transition!
This should run
This should print on a transition!
State('Stopped', id='stopped', value='stopped', initial=False, final=False)
This should run
This should print on a transition!
Well, this is embarrassing - I was using statemachines version 1.0.3. Now that I've upgraded, the decorator works. Sorry!
Working example -
from statemachine import StateMachine, State, exceptions
from statemachine.exceptions import TransitionNotAllowed
class ExampleStateMachine(StateMachine):
idle = State("idle", initial = True)
moving = State("Moving!")
stopped = State("Stopped")
forbidden_state = State("Forbidden!")
start_moving = moving.from_(idle,stopped)
move_from_stop = stopped.to(moving)
stop_from_moving = moving.to(stopped)
cycle = move_from_stop | stop_from_moving
forbidden_transition = idle.to(forbidden_state, cond = "always_return_false")
@cycle.cond
def cycle_cond(self):
print("This should run")
return True
def on_transition(self):
print("This should print on a transition!")
def always_return_false(self):
return False
#cycle._add_callback(cycle_cond, "cond")
exm = ExampleStateMachine()
try:
exm.forbidden_transition()
except TransitionNotAllowed:
print("Transition not allowed")
exm.start_moving()
exm.cycle()
print(exm.current_state)
exm.move_from_stop()
output
Transition not allowed
This should print on a transition!
This should run
This should print on a transition!
State('Stopped', id='stopped', value='stopped', initial=False, final=False)
This should run
This should print on a transition!
Hi @lucaswhipple-sfn , nice it worked as expected!
Description
I am trying to add a cond callback to a transitionlist.
My minimal example SHOULD work - according to this section of the docs the @cycle.cond decorator syntax should add a condition to my transitionlist. However, I always get an error when I try this method (see output below)
More broadly, I have a block of code I want to run each time any transition is called, even if a TransitionNotAllowed error is raised.
Given the resolution order documented in the docs (repeated below) validators() (attached to the transition) conditions() (attached to the transition) unless() (attached to the transition) beforetransition() before()
on_exit_state()
onexit()
ontransition()
on()
on_enter_state()
onenter()
after_()
after_transition()
If I always want to have code run each time a transition is called, I have to associate it with a Validator or a Condition. I don't want to have to define it for each individual state because my actual state machine is quite large.
I could bind the code to the on_transition method, but if I understand correctly this won't run if a transition not allowed is raised (see my second example)
Is there a good way to ensure code runs:
What I Did
Here is a minimal example:
Output:
Alternative version, where I try to run code in on_transition:
Output: