GithubHelp home page GithubHelp logo

xworkflows's Introduction

XWorkflows

Latest Version Supported Python versions Wheel status License

XWorkflows is a library to add workflows, or state machines, to Python objects.

It has been fully tested with Python 2.7 and all versions from 3.4 to 3.9

Links

Example

It allows to easilly define a workflow, attach it to a class, and use its transitions:

import xworkflows

class MyWorkflow(xworkflows.Workflow):
    # A list of state names
    states = (
        ('foo', "Foo"),
        ('bar', "Bar"),
        ('baz', "Baz"),
    )
    # A list of transition definitions; items are (name, source states, target).
    transitions = (
        ('foobar', 'foo', 'bar'),
        ('gobaz', ('foo', 'bar'), 'baz'),
        ('bazbar', 'baz', 'bar'),
    )
    initial_state = 'foo'


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

    @xworkflows.transition()
    def foobar(self):
        return 42

    # It is possible to use another method for a given transition.
    @xworkflows.transition('gobaz')
    def blah(self):
        return 13
>>> o = MyObject()
>>> o.state
<StateWrapper: <State: 'foo'>>
>>> o.state.is_foo
True
>>> o.state.name
'foo'
>>> o.state.title
'Foo'
>>> o.foobar()
42
>>> o.state
<StateWrapper: <State: 'bar'>>
>>> o.state.name
'bar'
>>> o.state.title
'Bar'
>>> o.blah()
13
>>> o.state
<StateWrapper: <State: 'baz'>>
>>> o.state.name
'baz'
>>> o.state.title
'Baz'

Hooks

Custom functions can be hooked to transactions, in order to run before/after a transition, when entering a state, when leaving a state, ...:

class MyObject(xworkflows.WorkflowEnabled):

    state = MyWorkflow()

    @xworkflows.before_transition('foobar')
    def my_hook(self, *args, **kwargs):
        # *args and **kwargs are those passed to MyObject.foobar(...)
        pass

    @xworkflows.on_enter_state('bar')
    def my_other_hook(self, result, *args, **kwargs):
        # Will be called just after any transition entering 'bar'
        # result is the value returned by that transition
        # *args, **kwargs are the arguments/keyword arguments passed to the
        # transition.
        pass

xworkflows's People

Contributors

corbinbs avatar metamatik avatar rbarrois avatar srinivasreddy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xworkflows's Issues

Hooks for state changes

It would be nice to have some sort of hook to allow execution of arbitrary Python code upon entering and/or leaving specific states.

xworkflows for python 3.10+ : error with Callable

Hi,

We have an error with python 3.10.

We want to update Django 3.2 to 4.2 with python 3.10 (from image python:3.10-slim-bullseye), we use xworkflows and unfortunately, there is an error with python 3.10 with "collections.Callable" when django is starting, probable reason : collections.Callable has been moved to collections.abc.Callable in python 3.10+ (source : https://stackoverflow.com/a/70641487/55465 )

xworkflows version : 1.0.4
django-xworkflows : 1.0.0
python : 3.10
error with collections.Callable

Error we have :

File "/code/api_dar/models.py", line 494, in Dossier
  @xworkflows.transition('*')
File "/usr/local/lib/python3.10/site-packages/xworkflows/base.py", line 510, in transition
  if is_callable(trname):
File "/usr/local/lib/python3.10/site-packages/xworkflows/compat.py", line 26, in is_callable
  return isinstance(var, collections.Callable)
AttributeError: module 'collections' has no attribute 'Callable'

Can you, please upgrading xworkflows for python 3.10

Deprecation warning for collections.Callable under Python 3.7

While running tests for a project that uses xworkflows version 1.0.4 under Python 3.7 I recevied the following warning:

/Users/myusername/.virtualenvs/myrepo/lib/python3.7/site-packages/xworkflows/compat.py:26: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  return isinstance(var, collections.Callable)

This is the line where the warning originates:

return isinstance(var, collections.Callable)

The deprecation warning is for using collections.Callable instead of collections.abc.Callable. The Callable class has moved in Python 3.3:

New in version 3.3: Formerly, this module was part of the collections module.

Source: https://docs.python.org/3.7/library/collections.abc.html#module-collections.abc

Because xworkflows does not claim support for Python 3.0, 3.1, 3.2, and 3.3 (see setup.py, it should be safe to simply update this line. Alternatively, replace the import with

try:
    from collections.abc import Callable
except ImportError:
    from collections import Callable

Saving and loading states

Hi.

In case a workflow is interrupted, state names can be saved to a file. But how can states be loaded to resume a workflow?

I tried to set the state of an xworkflows.WorkflowEnabled object instance like this:

self.state = xworkflows.base.StateWrapper(xworkflows.base.State(loadedstate, ''), MyWorkflow)

This results in xworkflows.base.InvalidTransitionError where there shouldn't be any.

Is there any workaround or a clean way to save and load states?

Update: This is done easily with @deontologician's pull request: #6

Same event cannot trigger a different transition based on current state

Hello,

I was about to use Xworkflows in my project, with its native support for Django. But once implemented, I discovered a limitation that I cannot overcome: the same event cannot trigger a different transition based on current state.

So, the following implementation does not work (I have commented out lines NOK).
Xworkflows definitions appear to be more transition-based than event-based...
I previously checked (and run) Fysom package, which has support for this feature, not so well integrated to Django models though.

STEP_STATES = (
('idle', _(u"Idle")),
('pending', _(u"Pending")),
('running', _(u"Running")),
('pause_requested', _(u"Pause requested")),
('stop_requested', _(u"Stop requested")),
('bypassed', _(u"Bypassed")),
('revoked', _(u"Revoked")),
('waiting', _(u"Waiting")),
('stopped', _(u"Stopped")),
('paused', _(u"Paused")),
('success', _(u"Success")),
('error', _(u"Error")),
('last_success', _(u"Last execution success")),
('last_error', _(u"Last execution error")),
)

STEP_TRANSITIONS = (
('init', 'idle', 'pending'),
('ignore', 'idle', 'idle'),
#('ignore', 'success', 'last_success'),
#('ignore', 'error', 'last_error'),
('start', 'pending', 'running'),
#('pause', 'pause_requested', 'paused'),
#('start', 'stop_requested', 'stopped'),
#('start', 'bypassed', 'bypassed'),
('bypass', ('idle', 'pending'), 'bypassed'),
('pause', ('idle', 'pending'), 'pause_requested'),
('stop', ('idle', 'pending'), 'stop_requested'),
('revoke', 'stop_requested', 'stopped'),
('wait', 'running', 'waiting'),
('continue', 'waiting', 'running'),
('success', 'running', 'success'),
('error', 'running', 'error')
)

STEP_INITIAL_STATE = 'idle'

InvalidTransitionError raised after being pickled

Hi

I've been experimenting with 'pausing' a workflow so that it might be resumed at a later time (and possibly a different machine). The workflow moves between transitions normally when the WorkflowEnabled object is used in the python interpreter it was created within. If the WorkflowEnabled object is pickled and loaded back up in another interpreter to be 'resumed', it raises an InvalidTransitionError on the first attempt to transition to a different State. It looks like this is due to two different state instances being compared (that have the same name and title)

https://github.com/rbarrois/xworkflows/blob/master/xworkflows/base.py#L159

since nothing is returned from the call to available_from, the InvalidTransitionError is raised. I was going to send over a pull request to allow States to be considered equal if they had the same name and title, but it looks like there are test cases that explicitly check to ensure that different State instances with the same name and title are treated as not equal. I'm wondering how best to proceed. Is it possible to reconsider the equality of States with the same name and title? Maybe it's better to investigate how to pickle/unpickle to ensure States are linked back up using the same State instance where appropriate?

Thanks,

Brian

Calling the parent method for a transition

Hi,

With the following use case:

class Parent(xworkflows.WorkflowEnabled):
    @xworkflows.transition()
    def accept(self):
        # Default behaviour


class Child(Parent):
    @xworkflows.transition()
    def accept(self):
        if child_condition:
            # Child behaviour

        return super(Child, self).accept()

child = Child()
child.accept()

If child_condition is not met, it calls the original accept method. But it leads to two "accept" transitions in the log, the second one being already from the "accepted" state, although it's not allowed by the workflow.

I found out I can access the undecorated Parent.accept method via return super(Child, self).accept.func().

The real issue happened on a Django model with django-xworkflows.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.