Following the documentation on github, it is not evident how to create a parameterized transition.
They define transitions like
self.machine.add_transition(trigger='wake_up', source='asleep', dest='hanging out')
And I want something like
self.machine.add_transition(trigger='wake_up_parameterized', source='asleep', dest='hanging out', ...)
where ... is somehow to pass the required parameters wake_up_parameterized
would require.
Usage would be
batman.wake_up_parameterized(my_param1, my_param2)
Is that supported on Transisions?
If not, how do I go around this? Seems quite basic.
The documentation you reference, states:
Sometimes you need to pass the callback functions registered at machine initialization some data that reflects the model's current state. Transitions allows you to do this in two different ways.
First (the default), you can pass any positional or keyword arguments directly to the trigger methods (created when you call add_transition() [...] You can pass any number of arguments you like to the trigger. There is one important limitation to this approach: every callback function triggered by the state transition must be able to handle all of the arguments. This may cause problems if the callbacks each expect somewhat different data.
When changing states, work can be conducted in callbacks. transitions
offers multiple occasions when callbacks can be invoked. From the documentation:
The most common use case is to determine whether a transition should take place based on passed parameters. The right callback location for this is conditions
. If more than one transition should be checked but data preprocessing is somewhat intensive, prepare
can be used instead. For now, let's assume that only one transition should be checked.
from transitions import Machine
import random
class Superhero:
def world_is_in_danger(self, chocolate_bars, *args, **kwargs):
return chocolate_bars < 3
def business_hours(self, time, *args, **kwargs):
return 9 < time < 17
def restock(self, chocolate_bars, *args, **kwargs):
new_stock = chocolate_bars + random.randrange(1, 4, 1) # heroes cannot carry more than 3 bars at a time
print(f"I am {self.state} and went to the store at {time}. The stock is now {new_stock}.")
hero = Superhero()
machine = Machine(model=hero, states=['resting', 'awake'], initial='resting')
machine.add_transition('call_for_help', 'resting', 'awake',
conditions=['world_is_in_danger', hero.business_hours],
after='restock')
hero.call_for_help(chocolate_bars=10, time=5) # everything looks good
assert hero.state == 'resting' # hero isn't moving a bit
hero.call_for_help(chocolate_bars=2, time=8) # we are about to run out! but it's too early...
assert hero.is_resting()
hero.call_for_help(chocolate_bars=1, time=10, weather='sunny') # someone must help us!
# >>> I am awake and went to the store at 10. The stock is now 3.
assert hero.is_awake()
As mentioned in the documentation, every parameter passed to a trigger function will be passed to all registered callbacks. The standard way to register callbacks is to pass them in add_transition
. We passed two conditions
callbacks: one as a string which is the standard way to do it and one as a function reference. Passing callbacks as strings will assume that our model (our hero) has a method (or will have a method at the time of calling) with that name. A function reference does not have to be assigned to the model in question. We also registered one callback for after
which will be called, wenn the transition has been conducted. If we evaluate hero.state
in that callback, it's already set to awake
. By adding *args
and **kwargs
to our callback functions, we made sure that the callbacks are flexible enough to deal with a varying amount of passed parameters and only consider relevant information.