def enter(self, event_data): """ Extends transitions.core.State.enter. Throws a `MachineError` if there is no leaving transition from this state and 'accepted' is not in self.tags. """ if not event_data.machine.get_triggers(self.name) and not self.is_accepted: raise MachineError("Error state '%s' reached!" % (self.name,)) super(Error, self).enter(event_data)
async def _process(self, trigger): if not self.has_queue: if not self._transition_queue: return (await trigger()) else: raise MachineError( "Attempt to process events synchronously while transition queue is not empty!" ) self._transition_queue.append(trigger) if len(self._transition_queue) > 1: return True while self._transition_queue: try: callback = self._transition_queue[0]() if asyncio.iscoroutine(callback): await callback self._transition_queue.popleft() except Exception: self._transition_queue.clear() raise return True
async def _process(self, trigger): # default processing if not self.has_queue: if not self._transition_queue: # if trigger raises an Error, it has to be handled by the Machine.process caller return await trigger() else: raise MachineError( "Attempt to process events synchronously while transition queue is not empty!" ) self._transition_queue.append(trigger) # another entry in the queue implies a running transition; skip immediate execution if len(self._transition_queue) > 1: return True # execute as long as transition queue is not empty ToDo: not tested! while self._transition_queue: try: await self._transition_queue[0]() self._transition_queue.popleft() except Exception: # if a transition raises an exception, clear queue and delegate exception handling self._transition_queue.clear() raise return True
def _check_event_result(self, res, model, trigger): if res is None: state_name = getattr(model, self.model_attribute) msg = "%sCan't trigger event %s from state %s!" % ( self.name, trigger, state_name) state = self.get_state(state_name) ignore = state.ignore_invalid_triggers if state.ignore_invalid_triggers is not None \ else self.ignore_invalid_triggers if ignore: _LOGGER.warning(msg) res = False else: raise MachineError(msg) return res
def _trigger(self, model, *args, **kwargs): state = self.machine.get_model_state(model) while state.parent and state.name not in self.transitions: state = state.parent if state.name not in self.transitions: msg = "%sCan't trigger event %s from state %s!" % ( self.machine.name, self.name, self.machine.get_model_state(model)) if self.machine.get_model_state(model).ignore_invalid_triggers: _LOGGER.warning(msg) else: raise MachineError(msg) event_data = EventData(state, self, self.machine, model, args=args, kwargs=kwargs) return self._process(event_data)
async def _trigger(self, model, *args, **kwargs): state = self.machine.get_state(model.state) if state.name not in self.transitions: msg = "%sCan't trigger event %s from state %s!" % ( self.machine.name, self.name, state.name) ignore = state.ignore_invalid_triggers if state.ignore_invalid_triggers is not None \ else self.machine.ignore_invalid_triggers if ignore: _LOGGER.warning(msg) return False else: raise MachineError(msg) event_data = EventData(state, self, self.machine, model, args=args, kwargs=kwargs) return await self._process(event_data)
def to_state(self, model, state_name, *args, **kwargs): """ Helper function to add go to states in case a custom state separator is used. Args: model (class): The model that should be used. state_name (str): Name of the destination state. """ current_state = getattr(model, self.model_attribute) if isinstance(current_state, list): raise MachineError("Cannot use 'to_state' from parallel state") event = EventData(self.get_state(current_state), Event('to', self), self, model, args=args, kwargs=kwargs) event.source_name = current_state event.source_path = current_state.split(self.state_cls.separator) self._create_transition(current_state, state_name).execute(event)
async def _trigger(self, model, *args, **kwargs): state = self.machine.get_state(model.state) while state.parent and state.name not in self.transitions: state = state.parent if state.name not in self.transitions: msg = "%sCan't trigger event %s from state %s!" % ( self.machine.name, self.name, model.state) if self.machine.get_state(model.state).ignore_invalid_triggers: logger.warning(msg) else: raise MachineError(msg) event_data = EventData(self.machine.get_state(model.state), self, self.machine, model, args=args, kwargs=kwargs) for func in self.machine.prepare_event: await self.machine._callback(func, event_data) logger.debug( "Executed machine preparation callback '%s' before conditions." % func) try: for t in self.transitions[state.name]: event_data.transition = t transition_result = await t.execute(event_data) if transition_result: event_data.result = True break except Exception as e: event_data.error = e raise finally: for func in self.machine.finalize_event: await self.machine._callback(func, event_data) logger.debug("Executed machine finalize callback '%s'." % func) return event_data.result