In my experience, it's a good practise to think of transitions (or better trigger) as events that should be handled depdending on their current state. For instance, when your machine is in the init
state, the event stopping
will not be handled and -- depending on your configuration -- may even raise an exception.
So rather than 'supressing' transitions, one could rephrase your use case to 'my machine should handle events stop
and down
differently when in state running_top_on
'.
Pytransitions
evaluates potential transitions in the following order:
- Transitions defined in children will be evaluated before transitions defined in parents
- Transitions in a state are evaluated in the order they were added
- Transitions will only be conducted when all
conditions
return True
and all unless
callbacks return False
Strategy one: Handle down
/stop
events in running_top
to override handling strategies in parents.
You could for instance add internal transitions that basically do nothing:
state_config = {
'name': 'top',
'children': ['on', 'off'],
'initial': 'off',
'transitions': [['begin', 'off', 'on'],
['end', 'on', 'off'],
['down', 'on', None],
['stop', 'on', None]]
}

However, you might want to add a callback to the transitions to log events when your machine was instructed to stop even though its in a critical state.
Strategy two: Introduce unless
in your transitions.
transitions = [ # ...,
{'trigger': 'stop', 'source': 'running', 'dest': 'stopping', 'unless': 'is_running_top_on'}

Quite conventiently, we do not have to write custom condition callbacks in this case since our model has already been decorated with state check functionality.
Strategy 3: Add transitions that handle special cases BEFORE generic transitions.
transitions = [['start', 'init', 'running'],
['stop', 'running_top_on', None],
['stop', 'running', 'stopping']]
The graph will look like the first graph BUT the transition will actually be defined on the root level which means that it will be evaluated ONLY when the event has not been handled by a child state. This means if we add ['bottom', 'running_top_up', Nome]
here, it will not be called when bottom
handling has been defined in the running
state as ['bottom', 'top', 'down']
.