transitions
transitions copied to clipboard
Reflexive + global transitions with hierarchical machines don't return to specific state
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
A reflexive transition =
with global starting state *
initiated within a child state in a hierarchical machine does not return to that initial child state.
Minimal working example
states = ['a', {'name': 'sub', 'children':['b']}]
transitions = [
['reflex', '*', '='],
]
machine = HierarchicalMachine(states=states, transitions=transitions, initial='a')
machine.set_state('sub_b')
print(machine.state)
# -> 'sub_b'
machine.reflex()
# -> True, indicating the transition was successful
print(machine.state)
# -> 'sub'
Expected behavior
I would expect the final state in the example to be the starting state, sub_b
.
Additional context
The problem does not happen without the global *
. If I specify the starting states explicitly, including the specific child state sub_b
, this works as expected:
# These all correctly return to `sub_b`:
transitions = [
['good_reflex_1', 'sub_b', '='],
['good_reflex_2', ['sub', 'sub_b'], '='],
['good_reflex_3', ['a', 'sub', 'sub_b'], '='],
]
Instead, the behavior of *
seems equivalent to only specifying the name of the child machine, without any specific states:
# These all function the same, returning to `sub` instead of `sub_b`:
transitions = [
['bad_reflex_1', 'sub', '='],
['bad_reflex_2', ['a', 'sub'], '='],
['bad_reflex_3', '*', '='],
]
Hello @jnu,
I can confirm this. This case needs to be handled specifically. I started working on a fix for this.
Hi. I'm not sure this is the same bug but here we go. Upgrading from 0.8.9 to 0.8.10, some of our unit tests started to fail. I can reproduce the issue with the following code:
from transitions.extensions import HierarchicalMachine
class Bug:
states = [
"stopped",
"idle",
{
"name": "image",
"children": [
"waiting",
"take",
"results",
]
},
]
def __init__(self) -> None:
# Initialize the state machine
self.__machine = HierarchicalMachine(model=self, states=self.states, initial="stopped")
self.__machine.add_transition(
"trigger_image",
"stopped",
"image_take"
)
self.__machine.add_transition(
"trigger_image",
"image",
"image_take"
)
def on_enter_image(self):
print("Entering state image")
def on_enter_image_take(self):
print("Entering state image_take")
b = Bug()
print(b.state)
b.trigger_image()
print(b.state)
b.trigger_image()
print(b.state)
Here is the output with 0.8.10 and 0.8.11:
stopped
Entering state image
Entering state image_take
image_take
image_take
Here is what we get with 0.8.9 and what is expected:
stopped
Entering state image
Entering state image_take
image_take
Entering state image_take
image_take
that's probably a different issue. thank you for providing an MRE. I'll let you know when I fixed it.
that's probably a different issue. thank you for providing an MRE. I'll let you know when I fixed it.
Thank you. Do you want me to open a new issue?
nah, one issue is fine, thanks for asking.