XWorkflows is a library designed to bring a simple approach to workflows in Python.

It provides:

  • Simple workflow definition
  • Running code when performing transitions
  • Hooks for running extra code before/after the transition
  • A hook for logging performed transitions

You can also refer to the django_xworkflows project for integration with Django.

Getting started

First, install the xworkflows package:

pip install xworkflows

Declaring workflows

You can now define a Workflow:

import xworkflows

class MyWorkflow(xworkflows.Workflow):
    states = (
        ('init', "Initial state"),
        ('ready', "Ready"),
        ('active', "Active"),
        ('done', "Done"),
        ('cancelled', "Cancelled"),

    transitions = (
        ('prepare', 'init', 'ready'),
        ('activate', 'ready', 'active'),
        ('complete', 'active', 'done'),
        ('cancelled', ('ready', 'active'), 'cancelled'),

    initial_state = 'init'

Applying a workflow

In order to apply that workflow to an object, you must:

Here is an example:

class MyObject(xworkflows.WorkflowEnabled):
    state = MyWorkflow()

Using the transitions

With the previous definition, some methods have been magically added to your object definition (have a look at WorkflowEnabledMeta to see how).

There is now one method per transition defined in the workflow:

>>> obj = MyObject()
>>> obj.state
<StateWrapper: <State: 'init'>>
>>> obj.state.name
>>> obj.state.title
'Initial state'
>>> obj.prepare()
>>> obj.state
<StateWrapper: <State: 'ready'>>
>>> obj.state.name
>>> obj.state.title

As seen in the example above, calling a transition automatically updates the state of the workflow.

Only transitions compatible with the current state may be called:

>>> obj.state
<StateWrapper: <State: 'ready'>>
>>> obj.complete()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
InvalidTransitionError: Transition 'complete' isn't available from state 'ready'.

Custom transition code

It is possible to define explicit code for a transition:

class MyObject(xworkflows.WorkflowEnabled):
    state = MyWorkflow()

    def activate(self, user):
        self.activated_by = user
        print("State is %s" % self.state.name)

obj = MyObject()

When calling the transition, the custom code is called before updating the state:

>>> obj.state
<StateWrapper: <State: 'init'>>
>>> obj.prepare()
>>> obj.state
<StateWrapper: <State: 'ready'>>
>>> obj.activate('blah')
State is ready
>>> obj.state
<StateWrapper: <State: 'active'>>
>>> obj.activated_by


Other functions can be hooked onto transitions, through the before_transition(), after_transition(), transition_check(), on_enter_state() and on_leave_state() decorators:

class MyObject(xworkflows.WorkflowEnabled):
    state = MyWorkflow()

    @xworkflows.before_transition('foobar', 'gobaz')
    def hook(self, *args, **kwargs):

Indices and tables