Skip to content

woile/pyfsm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

8233b57 · May 11, 2021

History

59 Commits
Apr 29, 2021
Apr 18, 2016
May 11, 2021
May 11, 2021
May 11, 2021
May 11, 2021
Apr 18, 2016
Apr 18, 2016
Apr 18, 2016
Aug 26, 2018
Sep 14, 2017
Aug 26, 2018
Apr 18, 2016
Aug 26, 2018
Sep 14, 2017
Aug 13, 2018
Aug 26, 2018
Sep 6, 2018
May 11, 2021
May 11, 2021
May 11, 2021

Repository files navigation

Overview

docs Read the Docs
tests
Travis-CI Build Status
Coverage Status
package PyPI Package latest release PyPI Wheel Supported versions Supported implementations

Minimal state machine

  • Free software: BSD license
import fsm

class MyTasks(fsm.FiniteStateMachineMixin):
    """An example to test the state machine.

    Contains transitions to everywhere, nowhere and specific states.
    """

    state_machine = {
        'created': '__all__',
        'pending': ('running',),
        'running': ('success', 'failed'),
        'success': None,
        'failed': ('retry',),
        'retry': ('pending', 'retry'),
    }

    def __init__(self, state):
        """Initialize setting a state."""
        self.state = state

    def on_before_pending(self):
        print("I'm going to a pending state")
In [4]: m = MyTasks(state='created')

In [5]: m.change_state('pending')
I'm going to a pending state
Out[5]: 'pending'
In [6]: m.change_state('failed')  # Let's try to transition to an invalid state
---------------------------------------------------------------------------
InvalidTransition                         Traceback (most recent call last)
<ipython-input-6-71d2461eee74> in <module>()
----> 1 m.change_state('failed')

~/pyfsm/src/fsm/fsm.py in change_state(self, next_state, **kwargs)
    90             msg = "The transition from {0} to {1} is not valid".format(previous_state,
    91                                                                        next_state)
---> 92             raise InvalidTransition(msg)
    93
    94         name = 'pre_{0}'.format(next_state)

InvalidTransition: The transition from pending to failed is not valid
pip install fsmpy
  1. Define in a class the state_machine
  2. Initialize state, either with a value, using __init__ or as a django field
  3. Add hooks:
Method Description
on_before_change_state Before transitioning to the state
on_change_state After transitioning to the state, if no failure, runs for every state
pre_<state_name> Runs before a particular state, where state_name is the specified name in the state_machine
post_<state_name> Runs after a particular state, where state_name is the specified name in the state_machine

This hooks will receive any extra argument given to change_state

E.g:

Running m.change_state('pending', name='john') will trigger pre_pending(name='john')

import fsm
from django.db import models


class MyModel(models.Model, fsm.FiniteStateMachineMixin):
    """An example to test the state machine.

    Contains transitions to everywhere, nowhere and specific states.
    """

    CHOICES = (
        ('created', 'CREATED'),
        ('pending', 'PENDING'),
        ('running', 'RUNNING'),
        ('success', 'SUCCESS'),
        ('failed', 'FAILED'),
        ('retry', 'RETRY'),
    )

    state_machine = {
        'created': '__all__',
        'pending': ('running',),
        'running': ('success', 'failed'),
        'success': None,
        'failed': ('retry',),
        'retry': ('pending', 'retry'),
    }

    state = models.CharField(max_length=30, choices=CHOICES, default='created')

    def on_change_state(self, previous_state, next_state, **kwargs):
        self.save()

If you are using serializers, they usually perform the save, so saving inside on_change_state is not necessary.

One simple solution is to do this:

class MySerializer(serializers.ModelSerializer):

    def update(self, instance, validated_data):
        new_state = validated_data.get('state', instance.state)
        try:
            instance.change_state(new_state)
        except fsm.InvalidTransition:
            raise serializers.ValidationError("Invalid transition")
        instance = super().update(instance, validated_data)
        return instance

https://pyfsm.readthedocs.org/

To run the tests run:

tox

Note, to combine the coverage data from all the tox environments run:

Windows
set PYTEST_ADDOPTS=--cov-append
tox
Other
PYTEST_ADDOPTS=--cov-append tox