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.4.tar.gz (6.3 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.4-py3-none-any.whl (6.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fusion_state_machine-0.1.4.tar.gz
  • Upload date:
  • Size: 6.3 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.4.tar.gz
Algorithm Hash digest
SHA256 329f20f7d9c94547f3242910795543b4664b5673d6aea98f555fac0bc0561949
MD5 7549c188b2c151f4f31fa0815c8de5e2
BLAKE2b-256 b44ec5d314eecbfdb1e6d30a5bdb2a1c4c77d06327d30282b4db7bd36d4ff099

See more details on using hashes here.

Provenance

The following attestation bundles were made for fusion_state_machine-0.1.4.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.4-py3-none-any.whl.

File metadata

File hashes

Hashes for Fusion_State_Machine-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 10942bfea1c844da895cf353f84a5f0d23b48b9bb78984073982a44e581dd701
MD5 a2ff25b5bf34ef74f95e7657dff972da
BLAKE2b-256 913bff1d9e75d5cf89c5f9a1ab6fc6f481d2b218654f34e0fe2a5274fda47f5e

See more details on using hashes here.

Provenance

The following attestation bundles were made for Fusion_State_Machine-0.1.4-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