Closed alexandretanem closed 3 years ago
Hello @alexandretanem,
conditions are resolved during runtime on the model in question. This means that when you pass a condition as a string, it is expected that the model in question (parent) has a fitting method/property definition. You can however pass conditions 'by reference' instead. In this case transitions
does not attempt to find it on the model:
from transitions.extensions.nesting import HierarchicalMachine as HierarchicalGraphMachine
class Child():
def __init__(self):
self.true = True
def ok(self):
return self.true
class Parent():
pass
modelChild = Child()
machineChild = HierarchicalGraphMachine(model=modelChild, states=['init', 'done'], initial='init', auto_transitions=False)
machineChild.add_transition('go', 'init', 'done', conditions=modelChild.ok)
modelParent = Parent()
machineParent = HierarchicalGraphMachine(model=modelParent, states=['init', 'done', {'name':'child', 'children':machineChild}], initial='init', auto_transitions=False)
machineParent.add_model(modelChild)
machineParent.add_transition('go', 'init', 'child')
machineParent.add_transition('finish', 'child_done', 'done')
def printState():
print("# ===")
print("# PARENT : {} => {}".format(modelParent.state, machineParent.get_triggers(modelParent.state)))
print("# CHILD : {} => {}".format(modelChild.state, machineChild.get_triggers(modelChild.state)))
printState()
# ===
# PARENT : init => ['go']
# CHILD : init => ['go']
modelParent.go()
printState()
# ===
# PARENT : child_init => ['go']
# CHILD : init => ['go']
modelParent.go()
printState()
# ===
# PARENT : child_done => ['finish']
# CHILD : init => ['go']
Hello @aleneum, thank you for the quick answer ! Maybe this way of referencing conditions could also appear in this section of the readme ?
Solving this raised another problem with "invalid / False" conditions :
If we add a "False" condition on a child machine's transition and try to trigger this transition, it raises a MachineError
exception only when parent state machine have a same trigger name.
Here is the example :
class Child():
def __init__(self):
self.true = True
def ok(self):
return self.true
def nok(self):
return not self.ok()
class Parent():
pass
modelChild = Child()
machineChild = HierarchicalGraphMachine(model=modelChild, states=['init', 'done', 'nodone'], initial='init', auto_transitions=False)
machineChild.add_transition('go', 'init', 'done', conditions=modelChild.ok)
machineChild.add_transition('nogo', 'init', 'nodone', conditions=modelChild.nok)
modelParent = Parent()
machineParent = HierarchicalGraphMachine(model=modelParent, states=['init', 'done', {'name':'child', 'children':machineChild}, 'nodone'], initial='init', auto_transitions=False)
machineParent.add_model(modelChild)
machineParent.add_transition('go', 'init', 'child')
machineParent.add_transition('finish', 'child_done', 'done')
#machineParent.add_transition('nogo','child_done','nodone') # /!\ Line to uncomment
def printState():
print("# PARENT : {} => {}".format(modelParent.state, machineParent.get_triggers(modelParent.state)))
printState()
# PARENT : init => ['go']
modelParent.go()
printState()
# PARENT : child_init => ['go', 'nogo']
modelParent.nogo()
printState()
# PARENT : child_init => ['go', 'nogo']
# Ok => the transition is not executed as modelChild.nok returns False
Now we execute the same thing with the following line uncommented
machineParent.add_transition('nogo','child_done','nodone')
printState()
# PARENT : init => ['go']
modelParent.go()
printState()
# PARENT : child_init => ['go', 'nogo']
modelParent.nogo()
#Traceback (most recent call last):
# File "testissue.py", line 36, in <module>
# modelParent.nogo()
# File "/usr/local/lib/python3.8/dist-packages/transitions/extensions/nesting.py", line 749, in trigger_event
# return self._check_event_result(res, _model, _trigger)
# File "/usr/local/lib/python3.8/dist-packages/transitions/extensions/nesting.py", line 820, in _check_event_result
# raise MachineError(msg)
#transitions.core.MachineError: "Can't trigger event 'nogo' from state(s) child_init!"
The MachineError
exception is triggered, while calling modelParent.nogo()
should not just simply return False, as with non-HierarchicalMachine ?
Maybe this way of referencing conditions could also appear in this section of the readme ?
This functionality is already documented here. I know, all developers are super busy but explaining the same functionality multiple times for convenience causes redundancy and may lead to out-of-sync and conflicting readme passages in the long run. If you think that a use case isn't covered in the docs, I suggest heading over to Stackoverflow and ask for assistance. Users of transitions
are usually more active there than in this issue tracker. If readme issues pop up frequently (on SO) (e.g. override rules for convenience functions), we will of course address them even if it means redundancy.
The MachineError exception is triggered, while calling modelParent.nogo() should not just simply return False, as with non-HierarchicalMachine?
I agree that a definition in a child should be enough to not trigger an invalid transition exception. 6ad4a4d should fix this.
I understand. Thank you for the quick fix !
Hello,
When using the Reuse of previously created HSMs principle of HierarchicalMachine, I can't make the Parent state machine being aware of Children SM conditions.
In the example below there is no difference with or without the line
machineParent.add_model(modelChild)
. Constraint: I don't want Parent to inherit from Child... in the sense of python inheritance.Is this an issue from
transitions
or there is another way to achieve this ?