-
-
Notifications
You must be signed in to change notification settings - Fork 88
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
Implementation feedback: Machine fails to transition to a new state before raising. #517
Comments
Looking at the debug output a little more, I see my assumption on execution order has been refuted from the beginning:
I would expect the |
What I am trying to do is this:
It almost seems like I need to transition to an 'exception raised' state, and then raise the exception for the user to deal with? |
The following appears to work well: from statemachine import StateMachine, State
class ExceptionStateMachineExample(StateMachine):
# States
start = State(initial=True)
running = State()
exception_raised = State()
end = State(final=True)
# Transitions
run = start.to(running, on="_do_action") # <-- As action 'on transition named run'
_pass = running.to(end)
fail = running.to(exception_raised, after="_raise_exception") # <-- We raise an exception in an action _after_ the transition to the new state is complete.
def _do_action(self): # <-- no longer returns anything
try:
# Do work that fails.
raise AttributeError()
# If nothing bad happened.
self._pass() # <-- We only do a state transition here, we don't return anything.
except Exception as e:
# Something bad happened.
self.fail(e)
def _raise_exception(self, exc: Exception):
raise exc
# Debug actions
def before_transition(self, event, state):
print(f"Before '{event}', on the '{state.id}' state.")
return "before_transition_return"
def on_transition(self, event, state):
print(f"On '{event}', on the '{state.id}' state.")
return "on_transition_return"
def on_exit_state(self, event, state):
print(f"Exiting '{state.id}' state from '{event}' event.")
def on_enter_state(self, event, state):
print(f"Entering '{state.id}' state from '{event}' event.")
def after_transition(self, event, state):
print(f"After '{event}', on the '{state.id}' state.")
s = ExceptionStateMachineExample()
try:
s.run()
except AttributeError:
print("Got attribute error")
assert s.current_state.id == "exception_raised" With output $ python.exe exception_example.py
Entering 'start' state from '__initial__' event.
Before 'run', on the 'start' state.
Exiting 'start' state from 'run' event.
On 'run', on the 'start' state.
Entering 'running' state from 'run' event.
After 'run', on the 'running' state.
Before 'fail', on the 'running' state.
Exiting 'running' state from 'fail' event.
On 'fail', on the 'running' state.
Entering 'exception_raised' state from 'fail' event.
Got attribute error |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
OS: Windows 11
python-statemachine: 2.5.0
Unable to transition states at the right time. (
on
does not execute when I expected it to)I am attempting to catch an exception, transition to a new state, and then raise the exception for the user to handle. Here is my minimal reproduction:
And the output appears as:
We enter the run state, encounter an exception in the
on
action, catch the exception in the action, attempt to transition to a new state, and then re-raise the exception.Looking at the debug output from the transition decorators, we see that the
error_encountered
transition is never triggered.If we comment out the re-raise, so that
_do_action
is thus:We see that the machine has already entered the
end
state by the time the action is run:I would expect that the
on
action is executed in order, rather then deferred until after the transition to the next stage.Alternative implementation, but not able to
raise
.One way around this could be making the
on
action acond
, and eliminate theerror_encountered
transition.Which results in:
I am unsure as to how I would raise the original exception here. I suppose I could add an action, ie
start.to(exception_raised, on="_reraise_exception")
, but then I'd need to handle passing the exception object to this action. (Maybe using a class instance variable? Doing it this way has some code-strink about it.)Conclusion
I'd like to be able to transition, then raise from within an action so that the user of my code has to handle the exception as well as recovery from the exception.
Do you have any recommendations as to how I might manage this?
The text was updated successfully, but these errors were encountered: