Closed spagh-eddie closed 2 years ago
Hello @spagh-eddie,
the documentation states:
If any callback raises an exception, the processing of callbacks is not continued. This means that when an error occurs before the transition (in state.on_exit or earlier), it is halted. In case there is a raise after the transition has been conducted (in state.on_enter or later), the state change persists and no rollback is happening.
since you raise your error in on_enter
I'd expect B_D
as a resulting state as well. I will have a look.
I had a look and I would say that your machine ending in state A
is the currently expected behavior because of
['next', 'B', 'A'],
The relevant part of the documentation is:
In addition to the processing order of transitions known from Machine where transitions are considered in the order they were added, HierarchicalMachine considers hierarchy as well. Transitions defined in substates will be evaluated first ...
The transition B_C->B_D
is considered invalid since it raises an exception (result is False
). Transition will then proceed to look for other valid transitions. This will continue until a transition returns True
or no valid transition can be found any longer. For your use case the one and only valid transition left is B->A
and this will process successfully. While this transition is evaluated, your model Matter
is actually in state B_D
! So if you change it into
['next', 'B_C', 'A']`
you will end up in B_D
.
The fact, that you can prevent transition 'bubbling' by setting EventData.result = True
was not intended but it's a neat little trick. I will have a look if we can consider return values of exception callbacks easily. We could then add the behavior that when any exception callback returns True
the transition is considered 'successful'. It will be halted anyway but no further transition is evaluated (for that particular source state; when evaluating parallel states things will be more complicated). What do you think, @spagh-eddie?
Thank you for your detailed response.
The transition B_C->B_D is considered invalid... will then proceed to look for other valid transitions.
Are you saying that transitions does not distinguish between no-available-transition and transition-raised-or-failed? and so it assumes no-available-transition and goes up the hierarchy in search of its parent's transition?
Is the raised MachineError
enough to distinguish between these two?
I will have a look if we can consider return values of exception callbacks easily
This seems similar enough to context manager's __exit__
behavior. However, given that del event.error
is already documented, putting event.result = True
next to it would have precedent. Alternatively, handle both error
and result
with the __exit__
-like return value.
I do not feel too strongly on this and would be happy with either as long as it is documented.
Are you saying that transitions does not distinguish between no-available-transition and transition-raised-or-failed? and so it assumes no-available-transition and goes up the hierarchy in search of its parent's transition?
as of now, it does not distinguish between a failed (as in exception raised) transition and a transition that does not fulfill required conditions. If there is no valid transition at all for a source state, transitions
will raise an Exception if ignore_invalid_triggers
is not set to True
.
As the documentation states, you can model conditional transitions like this:
transitions = [
{"trigger": "next", "source": "A", "dest": "B", "conditions": "check_A"},
{"trigger": "next", "source": "A", "dest": "C", "conditions": "check_B"}
{"trigger": "next", "source": "A", "dest": "D"}
]
When a next
event appears, transitions will evaluate transitions in the order they were added. So, when check_A
returns True
, the resulting state will be B
. If neither check_A
or check_B
return True
, A->D
will act similar to an else
statement. HierarchicalMachine
applies the same concept to the order of potential transitions implied by hierarchy.
I guess how errors are currently handled is also related to #552. Increasing the try/catch scope might change the behavior to what you would have expected (stopping the evaluation of transitions). I will leave this issue open and come back to it, when #552 is resolved.
As expected, your example will end up in B_D
with transitions 0.9 (currently dev-0.9
branch) and the increased try/catch scope for processing transitions.
This issue has been resolved with the aforementioned commit and should be fixed in transitions 0.9.0 which was recently pushed to the master
branch. Feel free to comment if you still face this problem. I will reopen the issue if necessary.
Describe the bug
I am unsure if this:
Expected behavior
I would have expected the end state to be
B_D
instead ofA
. Addingevent.result = True
afterdel event.error
gets me my expected behavior.Minimal working example
Additional context
transitions version 0.8.10