pytransitions / transitions

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

Running the last example in callbacks crashes #543

Closed Kaju-Bubanja closed 2 years ago

Kaju-Bubanja commented 2 years ago

Describe the bug Copy posting the last example in the callbacks heading crashes instead of producing the documented output:

Minimal working example

from transitions import Machine

class Matter(object):
    def raise_error(self, event): raise ValueError("Oh no")
    def handle_error(self, event):
        print("Fixing things ...")
        del event.error  # it did not happen if we cannot see it ...

states=['solid', 'liquid', 'gas', 'plasma']

lump = Matter()
m = Machine(lump, states, before_state_change='raise_error', on_exception='handle_error', send_event=True)
try:
    lump.to_gas()
except ValueError:
    pass
print(lump.state)

Expected behavior Expecting output:

# >>> Fixing things ...
# >>> initial

Additional context Actual output:

Traceback (most recent call last):
  File "/home/username/app/lib/python3.8/site-packages/transitions/core.py", line 537, in __init__
    super(Machine, self).__init__(**kwargs)
TypeError: object.__init__() takes exactly one argument (the instance to initialize)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/username/test.py", line 12, in <module>
    m = Machine(lump, states, before_state_change='raise_error', on_exception='handle_error', send_event=True)
  File "/home/username/app/lib/python3.8/site-packages/transitions/core.py", line 539, in __init__
    raise ValueError('Passing arguments {0} caused an inheritance error: {1}'.format(kwargs.keys(), err))
ValueError: Passing arguments dict_keys(['on_exception']) caused an inheritance error: object.__init__() takes exactly one argument (the instance to initialize)

Process finished with exit code 1

My actual goal is to stop a transition from within. I have these transitions defined:

transitions_l = [["configure", States.UNCONFIGURED, States.INACTIVE],
                 ["activate", States.INACTIVE, States.ACTIVE],
                 ["deactivate", States.ACTIVE, States.INACTIVE],
                 ["cleanup", States.INACTIVE, States.UNCONFIGURED],
                 ["shutdown", [States.UNCONFIGURED, States.INACTIVE, States.ACTIVE], States.FINALIZED],
                 ["error", "*", States.ERRORPROCESSING],
                 ]
transitions = []
for trigger, source, dest in transitions_l:
    transitions.append({"trigger": trigger, "source": source, "dest": dest, "before": "before_" + trigger, "after": ["on_" + trigger, "after_" + trigger]})

I was adding these transitions to my classes and doing something like this:

def on_configure(self, *args)
    result = self.try_operation_which_can_fail()
    if result:
        log.info("Success")
    else:
        # I don't know how to stop the transition in case of failure, that's why I thought of using the on_exception example, 
        # but I'm not sure if that is the intended way to abort a transition.
        self.stop_transition_somehow()
aleneum commented 2 years ago

Hello @Kaju-Bubanja,

I cannot reproduce this. I ran your example code above and got the expected output. Do you use the latest pytransitions version from master?

Kaju-Bubanja commented 2 years ago

Updating from 0.8.7 to 0.8.8 fixed the issue