Source code for edgy.workflow.transition

# -*- coding: utf-8 -*-
"""
The smallest atom of ``edgy.workflow`` is a ``Transition``, which basically is a regular python
callable with additional metadata to make the system aware of when it can be applied.

"""

from edgy.workflow.constants import WILDCARD
from edgy.workflow.utils import issequence


[docs]class Transition(object): """ Defines when and how to go from one state to another, eventually applying a user-defined side-effect while being applied. Example:: >>> t = Transition(name='sleep', source='awake', target='asleep') >>> class Person(object): ... state = 'awake' >>> me = Person() >>> t(me) >>> me.state 'asleep' This class can also be used as a decorator:: >>> @Transition(source='asleep', target='awake') >>> def wakeup(self, subject): ... print('HEY!') >>> wakeup(me) >>> me.state 'awake' A special wildcard source can make transitions work from any state. Just specify "*" as a transition source and you'll be able to transition from any state. """ # Tracks each time a Transition instance is created. Used to retain order. creation_counter = 0 # Transition handler. If absent, the transition is considered as "partial", and should be called with a handler # callable to be complete. handler = None def __init__(self, handler=None, name=None, source=None, target=None): self.source = tuple(source if issequence(source) else (source,)) self.target = target self._name = name # Increase the creation counter, and save our local copy. self.creation_counter = Transition.creation_counter Transition.creation_counter += 1 if handler: self.handler = handler or self.handler def __call__(self, *args, **kwargs): if self.handler: return self.__call_complete(*args, **kwargs) return self.__call_partial(*args, **kwargs) def __call_partial(self, handler): self.handler = handler return self def __call_complete(self, subject, *args, **kwargs): if not WILDCARD in self.source and not subject.state in self.source: raise RuntimeError( 'This transition cannot be executed on a subject in "{}" state, authorized source ' 'states are {}.'.format(subject.state, ', '.join(['"{}"'.format(state) for state in self.source])) ) try: retval = self.handler(self, subject, *args, **kwargs) subject.state = self.target except Exception as e: raise return retval @property def __name__(self): if self._name: return self._name if self.handler: return self.handler.__name__ return 'partial' # Alias that can be used in django templates, for example. name = __name__ def __repr__(self): return '<{}.{} object "{}" ({} to {}) at {}>'.format( type(self).__module__, type(self).__name__, self.__name__, '/'.join(self.source), self.target, hex(id(self)), )