pytransitions / transitions

A lightweight, object-oriented finite state machine implementation in Python with many extensions
MIT License
5.49k stars 524 forks source link

confusion with on_exception handler #552

Closed kpihus closed 2 years ago

kpihus commented 2 years ago

Hello,

I have little confusion with on_exeption handler. Did some digging in documentation and in source code, but didnt find any possible solution Lets take following code for example:

from transitions import Machine

class Matter(object):
    def __init__(self):
        transitions = [
            {"trigger": "melt", "source": "solid", "dest": "liquid"},
            {"trigger": "evaporate", "source": "liquid", "dest": "gas"}
        ]
        states = ['solid', 'liquid', 'gas', 'plasma']
        self.machine = Machine(self,
                               initial='solid',
                               states=states,
                               transitions=transitions,
                               on_exception='handle_error',
                               send_event=True
                               )

    def handle_error(self, event):
        print("Fixing things ...")
        del event.error  # it did not happen if we cannot see it ...

lump = Matter()

print("Start state", lump.state)
lump.melt()
print("State", lump.state)
lump.evaporate()
print("State", lump.state)
lump.melt()
print("State", lump.state)

Code tries to execute transition what is not allowed by transitions definitions. One would assume that transitions.core.MachineError: "Can't trigger event melt from state gas!" error would be catched and handled by handle_error callback, but seems it does not work so. Is there any other mechanism to catch and handle this kind of transition errors?

aleneum commented 2 years ago

Hello @kpihus,

Is there any other mechanism to catch and handle this kind of transition errors?

as of right now all callbacks are tied to the processing of transitions which means only errors happening during transitions are considered. The MachineError in question is raised outside of this scope. This being said I don't see why this scope cannot be increased to also include this particular MachineError. I will have a look whether this can be changed without serious side effects.

kpihus commented 2 years ago

Tnx @aleneum i reached that conclusion by myself also when reading the source, but i wasn't sure about that. Anyway i moved my errors inside transitions and this solved the problem. Tho including MachineErrors into the scope wouldn't hurt i guess, because developers do make mistakes and not all possible errors are handeled properly.

joecabezas commented 2 years ago

this is also confusing for me, at some points my code just does not work and I can't understand why, just to realize I called the trigger twice (like a bot trying to walk more than once) without having a reflexive transition on walk state, but the library does not inform me of this, why not just let developers catch does errors instead of suppressing them and give them an option to handle them, which by the way only works on certain cases?

aleneum commented 2 years ago

@kpihus:

FYI: MachineError caused by invalid triggers can now also be handled in on_exception callbacks. The change is currently in the dev-0.9 branch and will be part of the upcoming 0.9 release

Hello @joecabezas,

without having a reflexive transition on walk state, but the library does not inform me of this, why not just let developers catch does errors instead of suppressing them and give them an option to handle them, which by the way only works on certain cases?

I am not sure what your observation of transitions 'suppressing' errors is referring to. Do you have a minimal working example of this? If you don't provide on_exception callbacks every actual exception is raised and can/must be handled by the library user. If you pass ignore_invalid_triggers=True to the constructor, this will -- as the name suggest -- not raise exceptions when an event/trigger is not valid for the current model state. You can always let ignore_invalid_trigger be False which is the default value.

aleneum commented 2 years ago

Closing this since on_exception callbacks can handle MachineError since version 0.9.0 which was recently pushed to the master branch