transitions
transitions copied to clipboard
on_exception and before_state_change callbacks fail if created in-class
Using the NarcolepticSuperhero
pattern fails when trying to also set on_exception
or before_state_change
callbacks:
class Automator():
# ... removed for brevity ...
def __init__(self):
"""
Create a new Automator.
"""
self.__machine = Machine(
model = self,
before_state_change = '__check_login',
on_exception = '__handle_exception',
send_event = True,
states = Automator.__states,
transitions = Automator.__transitions,
initial = 'starting'
)
# ... removed for brevity ...
def __check_login(self, event):
"""
Checks if the user is logged in before each call (and logs in, if necessary).
"""
self.__logger.info("CHECKING LOGIN")
def __handle_exception(self, event):
"""
Gracefully handle any exceptions during the automation.
"""
self.__logger.error(f"Exception: {event.error}")
self.to_shutdown()
This creates a cascade of errors when attempting to be called:
Traceback (most recent call last):
File ".../lib/python3.9/site-packages/transitions/core.py", line 1158, in resolve_callable
func = getattr(event_data.model, func)
AttributeError: 'Automator' object has no attribute '__check_login'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File ".../lib/python3.9/site-packages/transitions/core.py", line 1165, in resolve_callable
mod, name = func.rsplit('.', 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File ".../lib/python3.9/site-packages/transitions/core.py", line 435, in _process
if trans.execute(event_data):
File ".../lib/python3.9/site-packages/transitions/core.py", line 272, in execute
event_data.machine.callbacks(itertools.chain(event_data.machine.before_state_change, self.before), event_data)
File ".../lib/python3.9/site-packages/transitions/core.py", line 1123, in callbacks
self.callback(func, event_data)
File ".../lib/python3.9/site-packages/transitions/core.py", line 1140, in callback
func = self.resolve_callable(func, event_data)
File ".../lib/python3.9/site-packages/transitions/core.py", line 1171, in resolve_callable
raise AttributeError("Callable with name '%s' could neither be retrieved from the passed "
AttributeError: Callable with name '__check_login' could neither be retrieved from the passed model nor imported from a module.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File ".../lib/python3.9/site-packages/transitions/core.py", line 1158, in resolve_callable
func = getattr(event_data.model, func)
AttributeError: 'Automator' object has no attribute '__handle_exception'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File ".../lib/python3.9/site-packages/transitions/core.py", line 1165, in resolve_callable
mod, name = func.rsplit('.', 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "...", line 142, in run
self.automator.next()
File ".../lib/python3.9/site-packages/transitions/core.py", line 401, in trigger
return self.machine._process(func)
File ".../lib/python3.9/site-packages/transitions/core.py", line 1188, in _process
return trigger()
File ".../lib/python3.9/site-packages/transitions/core.py", line 426, in _trigger
return self._process(event_data)
File ".../lib/python3.9/site-packages/transitions/core.py", line 441, in _process
self.machine.callbacks(self.machine.on_exception, event_data)
File ".../lib/python3.9/site-packages/transitions/core.py", line 1123, in callbacks
self.callback(func, event_data)
File ".../lib/python3.9/site-packages/transitions/core.py", line 1140, in callback
func = self.resolve_callable(func, event_data)
File ".../lib/python3.9/site-packages/transitions/core.py", line 1171, in resolve_callable
raise AttributeError("Callable with name '%s' could neither be retrieved from the passed "
AttributeError: Callable with name '__handle_exception' could neither be retrieved from the passed model nor imported from a module.
It seems like the Machine
cannot resolve callbacks from __init__
when they're created in the NarcolepticSuperhero
pattern.
Upon further review, it's because I had __
in front of the class names as private methods... not sure if that's resolvable with this scope. This may be closeable.
Hello @andrewvaughan,
when passed as strings, callbacks will be resolved during runtime. This does not work with double underscore functions since their names will get obfuscated. If you NEED double underscore functions you could pass them by reference:
from transitions import Machine
class Automator:
def __init__(self):
self.__machine = Machine(
model=self,
before_state_change=self.__check_login,
on_exception=self.__handle_exception,
send_event=True,
states=['starting'],
initial='starting'
)
# ... removed for brevity ...
def __check_login(self, event):
print("CHECKING LOGIN")
def __handle_exception(self, event):
print(f"Exception: {event.error}")
m = Automator()
m.to_starting()