Closed DYFeng closed 3 months ago
Hello @DYFeng,
thank you for the report. I see what you are trying to achieve and how transitions behaviour is confusing here. I agree that the error message is not very helpful and transitions tries to achieve the wrong thing here.
Let's talk about what you are trying to achieve though. In your setup I'd suggest to work with 'explicit' callbacks rather than defining "onenter
from transitions.extensions import HierarchicalMachine as HSM
class P(HSM):
def __init__(self):
HSM.__init__(self, states=[{"name": "A", "on_enter": ["notify", self.notify]}, "B"], # [1]
transitions=[["run", "A", "B"], ["run", "B", "A"]],
initial="A")
def notify(self): # [2]
print("State was entered")
class T(HSM):
def __init__(self):
HSM.__init__(self, states=["Q", {"name": "P", "children": P()}], # [3]
transitions=[["go", "Q", "P"], ["go", "P", "Q"]],
initial="Q")
def notify(self): # [4]
print("notify was overridden")
p = P()
p.to_A() # [5]
# >>> State was entered
# >>> State was entered
t = T()
t.go() # [6]
# >>> notify was overridden
# >>> State was entered
Instead of on_enter
I pass callbacks to the state definition in P
, once by reference and once by name (see [1]). If you enter state A via "p.to_A()" [5], P.notify
[2] will be called twice: Once because the underlying state object has a direct reference and once when the callback name "notify"
is resolved to a method of the model. In your case, both the machines P
and T
also act as their respective model. This is valid but not required. Consequently. T
acts as a model for T
but *P
is not a model of/for T
. What this means is illustrated in the next part.
When you pass an instance of P
to T
, transitions
will reference the state objects owned by the create instance of P
[3] which will behave similarly to our explicitly created p
[5]. When t.go()
is called the state object will process the event in the same way but there is a difference now. The callback name will be resolved on the model of machine T
(self) and transitions will have a look for notify
on t
instead of p
. If you comment [4] you will see that this resolution fails.
The Readme says:
(Nested)State instances are just referenced which means changes in one machine's collection of states and events will influence the other machine instance. Models and their state will not be shared though. Note that events and transitions are also copied by reference and will be shared by both instances if you do not use the remap keyword.
The catch is that machines do not share models. If you define callbacks by name, a model that uses a machine that makes use of nested machines must implement the callbacks. If you explicitly want to call a method of a nested machine then you should use references instead.
Concerning the error/bug: on_enter_A
of machines might be better passed by reference instead of by name when models are added to a machine. This happens during the instantiation. I will check wether this is suitable or cause side effects. Nevertheless, I hope the example above allows you to continue working on your task.
I tested passing on_enter_<state>
by reference instead of by name but this has unwanted side effects: When a model is removed from the machine, its callback is not removed from State
and thus called when any model enterst <state>
the next time. I'd say this causes unfortunately more problems than it solves. For now, I'd say explicitly passing function references to a transition is the better options.
I am closing this issue. Feel free to comment anyway if this issue still persists. I will reopen this issue or create a new one should a feature request or bug emerge.
encountering a problem when inheriting
HierarchicalMachine
.