-
Notifications
You must be signed in to change notification settings - Fork 524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HSM: on_enter inside nested machine calls top level machine methods #248
Comments
Hi @Grey-Bit,
string callbacks are always looked up in the model which in your case is the When you pass a
The 'easiest' way would be to pass callbacks by reference rather than by name. Instead of from transitions.extensions import HierarchicalMachine as Machine
class Nested(Machine):
def print_msg(self):
print("Nested")
def __init__(self):
self.states = ['n1', {'name': 'n2', 'on_enter': self.print_msg}]
Machine.__init__(self, states=self.states, initial='n1')
self.add_transition(trigger='goto_n2',
source='*',
dest='n2')
class Top(Machine):
def print_msg(self):
print("Top")
def __init__(self):
self.nested = Nested()
self.states = ['t1',
{'name': 't2',
'children': self.nested}]
Machine.__init__(self, states=self.states, initial='t1')
self.add_transition(trigger='goto_t2',
source='*',
dest='t2_n1')
top_machine = Top()
top_machine.goto_t2()
print(top_machine.state) # >>> t2_n1
top_machine.goto_n2() # >>> Nested
print(top_machine.state) # >>> t2_n1 As mentioned above, I'd also suggest splitting machine and model. This way you can use MixIns to decorate your main model and keep the code less cluttered. This way you can also use every tool OOP has to offer to alter the state machine's behaviour. |
Thanks @aleneum - changing to direct references instead of strings worked well. However, now it introduced another problem - inside the callback, if I try to issue any trigger in the Nested or Top, then it fails with an AttributeError - can't find the trigger functions.
I suspect it's the artifact of the copying process and order of transition initializations, but can't figure out the exact cause and how to make it work. |
Sorry, my bad. Overlooked the transition which is called |
The problem is this line in state = copy.deepcopy(state) This destroys all the previously created cross-references. Remark: |
Thanks @aleneum, The copy process introduces another substantial issue: In the following example, I am calling print_msg directly, trying to access variable "var",
May be instead of playing with the copy methods, it's better to create a "parent" variable in the nested machines that would reference the original containing machine? This would allow anybody to issue triggers or access any information stored in the containing machines? For symmetry, we could add an ability to access nested machines through the state that references them. |
Hello @Grey-Bit,
This might work for a scenario with just one level of embedded machines but will not solve the issue for deeper nested configurations. The 'problem' that I guess I put you on the wrong path by mentioning references to bound functions. They seem to NOT work well with from transitions.extensions.nesting import HierarchicalMachine as Machine
class Nested(object):
def __init__(self):
states = ['n1', {'name': 'n2', 'on_enter': 'print_msg'}]
transitions = [['goto_n2', '*', 'n2']]
self.machine = Machine(self, states=states, transitions=transitions, initial='n1')
def print_msg(self):
print("Nested")
class Top(Nested):
def __init__(self):
super(Top, self).__init__()
self.nested = Nested()
states = ['t1',
{'name': 't2', 'children': self.machine}]
transitions = [dict(trigger='print', source='*', dest='=', after='print_msg'),
['goto_t2', '*', 't2_n1']]
self.machine = Machine(self, states=states, transitions=transitions, initial='t1')
def print_msg(self):
# call super method in case we are in a substate
if self.is_t2(allow_substates=True):
super(Top, self).print_msg()
else:
print("Top")
top_machine = Top()
top_machine.print() # will call Top.print_msg
top_machine.goto_t2()
top_machine.print() # will call Top.print_msg and forward to Nested.print_msg
top_machine.goto_n2() # same as above, but triggered by 'on_enter' instead of 'after' I acknowledge this is an issue since it reduces the flexibility of |
Thanks @aleneum In the meantime, I changed deepcopy to copy and at least in my case of two level nesting of machines everything works as expected. Where should I expect it to fail due to the lack of deep copy? |
I am currently aware of the issue that it breaks the imported machine. The above mentioned test_blueprint_remap will fail in case |
@Grey-Bit: If you mind, you can pull the new changes from |
Thanks @aleneum, I will test over the weekend and will report back here if there are any issues. |
Sorry for the late response: I validated this change on a more complex scenario that included three level hierarchy and it works well. Thanks @aleneum for taking care of this. |
awesome, thanks for the feedback! |
In the snippet below, transition is triggered from one nested state to another.
on_enter callback is attached to the destination nested state, but the library is searching for this callback in the top level machine.
The output is "Top"
If I remove print_msg() from the Top class, then I am getting AttributeError.
While in theory it's possible to have the callbacks in the top machine, it would be better to have states and their accompanying callbacks in the same class for better encapsulation and reuse.
Is there any way to achieve the encapsulation by some other means?
The text was updated successfully, but these errors were encountered: