Skip to main content

Utility to turn your class into a state machine

Project description

Fusion StateMachine

Many thanks to the original creator of this wonderful library, Fusion StateMachine intended for the development of applications for real-time event lighting via DMX, the starting point is a fork from the simple-state-machine library

Upload Python Package It is a python library with which you can decorate your class to make it a state machine.

Installation

pip install simple-state-machine

State Machine

A state machine which has a finite set of states is called a finite state machine. The one which doesn't have finite states is called a non-finite state machine.

A finite state machine is a mathematical concept in which the machine has a state and uses transitions to move from one state to another. Finite state machines can be of 2 types - deterministic and non-deterministic

A deterministic finite state machine produces the same result after given transitions, hence we can "determine" and so the name. In a non-deterministic finite state machine, for some state and input symbol, the next state may be nothing or one or two or more possible states.

This library provides a deterministic finite state machine.

Basic Usage

from state_machine.machine import machine, transition


@machine(states=["earth", "space"])
class Rocket(object):
    def __init__(self):
        self.moving = False
          
    # Mandatory to define this method
    def load_state(self):
        return "space" if self.moving else "earth"

    @transition(sources=["earth"], destination="space")
    def launch(self):
        self.moving = True
            
rocket = Rocket()
# assert m.current_state == "earth"
rocket.launch()
# assert m.current_state == "space"

rocket.launch()
# InvalidMoveError: Current state - space is not in source states - earth

Note

A data member current_state is added to your class' object. You may use its value to assert in decisions or in your tests.

What if my function has 2(or more) possible transitions?

It is quite common to have functions which have 2 or more possible transitions.

For eg. You are collecting a payment for an order. If the payment is valid, you want to complete the payment, else you want to fail it.

from state_machine.machine import machine, transition


@machine(states=["INIT", "PAYMENT_IN_PROGRESS", "PAYMENT_COMPLETE", "PAYMENT_FAILED"])
class Order:
    def __init__(self):
        self.payment = ""
        
    def load_state(self):
        if not self.payment:
            return "INIT"
        
        if self.payment == "SUCCESS":
            return "PAYMENT_COMPLETE"
        
        if self.payment == "FAILED":
            return "PAYMENT_FAILED"
        
        return "PAYMENT_IN_PROGRESS"
        
    @transition(sources=["INIT"], destination="PAYMENT_IN_PROGRESS")
    def create_payment_request(self):
        self.payment = "IN_PROGRESS"
        
    def collect_payment(self, payment):
        assert self.load_state() == "PAYMENT_IN_PROGRESS"
        
        if payment == "SUCCESS":
            self.payment_success()
        elif payment == "FAILED":
            self.payment_failed()
            
    @transition(sources=["PAYMENT_IN_PROGRESS"], destination="PAYMENT_COMPLETE")
    def payment_success(self):
        self.payment = "SUCCESS"
        
    @transition(sources=["PAYMENT_IN_PROGRESS"], destination="PAYMENT_FAILED")
    def payment_failed(self):
        self.payment = "FAILED"
        
        
order1 = Order()
# assert order.current_state == "INIT"
order1.create_payment_request()
# assert order.current_state == "PAYMENT_IN_PROGRESS"
order1.collect_payment("SUCCESS")
# assert order.current_state == "PAYMENT_COMPLETE"


order2 = Order()
# assert order.current_state == "INIT"
order2.create_payment_request()
# assert order.current_state == "PAYMENT_IN_PROGRESS"
order2.collect_payment("FAILED")
# assert order.current_state == "PAYMENT_FAILED"

Note

Decision is not a concept in deterministic FSMs, hence we won't be supporting it. This means you will have to assert the current state for a decision accordingly.

Error Handling

The library raises the following errors.

  1. LoaderNotFound - If your class doesn't implement a load_state method.
  2. LoaderException - If the load_state method threw an exception.
  3. MachineNotFound - If you used a transition decorator on a method whose class is not machine decorated.
  4. InvalidStateError - This is raised when the state machine encounters an unknown state. The state machine raises this error in 2 situations. One, when load_state returns an invalid state. And, when you pass invalid source or destination in a transition.
  5. InvalidMoveError - It is raised when you enter a transition with a non-source state.
  6. UnintendedOperationError - The state machine calls load_state function after each transition to ensure that the state has been updated to the destination. If a transition doesn't update the state of the machine to destination, this error is raised. For eg-
@transition(source=["earth"], destination="sky")
def launch(self):
    pass # the function didn't do anything.

Developer's Guide

Setup

We use pipenv to manage python packages. To setup your dev environment, locate yourself in the project directory and execute

pipenv install --dev
pipenv shell

Testing mode

make tests

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fusion_state_machine-0.1.3.tar.gz (6.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

Fusion_State_Machine-0.1.3-py3-none-any.whl (6.3 kB view details)

Uploaded Python 3

File details

Details for the file fusion_state_machine-0.1.3.tar.gz.

File metadata

  • Download URL: fusion_state_machine-0.1.3.tar.gz
  • Upload date:
  • Size: 6.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.0.1 CPython/3.12.8

File hashes

Hashes for fusion_state_machine-0.1.3.tar.gz
Algorithm Hash digest
SHA256 27e99e3e4160cc958d90d03b7d433fafaa6864268d3068d927575aed4d16667e
MD5 aefbc5ac1faa82c3b2e59f423cd7a21f
BLAKE2b-256 f6626d9fe0766403bfe04efcae1efd583b375607e1dfeea1648a61f5af7f585b

See more details on using hashes here.

Provenance

The following attestation bundles were made for fusion_state_machine-0.1.3.tar.gz:

Publisher: python-publish.yml on Jisus17/Fusion-State-Machine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file Fusion_State_Machine-0.1.3-py3-none-any.whl.

File metadata

File hashes

Hashes for Fusion_State_Machine-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 d312135077194949109c085e6ece98cdf4fd995695ee35f4a29c284bc0403db7
MD5 3a4ccf58077e11dc94275c036a7652b2
BLAKE2b-256 90ca0bb3372886093496157586f848ef74d39cdaaa0d9fc5f405a90d6c55c2e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for Fusion_State_Machine-0.1.3-py3-none-any.whl:

Publisher: python-publish.yml on Jisus17/Fusion-State-Machine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page