A flexible state machine framework for Python
Reason this release was yanked:
Major upgrades made in the architecture resulting in breaking changes.
Project description
State Machine Framework
A flexible state machine framework for Python with clean abstractions, ORM integration support, and type-safe workflow management.
Features
- ๐ฏ Clean State Definitions: Minimal boilerplate with decorator-based validators and hooks
- ๐ Integration Agnostic: Works ORM via adapter pattern
- โ Type Safety: Pydantic schema validation and type-checked workflow contexts
- ๐จ External Registration: Register validators and hooks in separate modules for clean code organization
- ๐ Workflow Context: Type-safe context management with automatic validation
Installation
pip install state-machine-framework
Quick Start
1. Define Your Models
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Order(Base):
__tablename__ = 'orders'
id = Column(Integer, primary_key=True)
order_id = Column(String(100), unique=True)
status = Column(String(50))
2. Define States
from state_machine_framework import State
class OrderPending(State):
is_start_state = True
class Meta:
order = 1
Order_status = 'pending'
class OrderProcessing(State):
class Meta:
order = 2
Order_status = 'processing'
class OrderCompleted(State):
is_terminal_state = True
class Meta:
order = 3
Order_status = 'completed'
3. Define State Machine
from state_machine_framework import StateMachine
class OrderStateMachine(StateMachine):
pass
OrderStateMachine.register(
Order,
state_field='status',
identifier_field='order_id'
)
4. Add Validators (External Registration)
from state_machine_framework import validator
@validator(OrderPending, order=1)
def validate_order_amount(state, data, context):
if data.get('amount', 0) <= 0:
raise ValueError("Invalid amount")
return {'validated': True}
5. Add Hooks (External Registration)
from state_machine_framework import hook
@hook(OrderProcessing, order=1)
def send_confirmation_email(state, data, context):
order = data['instances']['Order']
send_email(order.customer_email)
return {'email_sent': True}
6. Define Pydantic Schemas
from pydantic import BaseModel, Field
class OrderData(BaseModel):
amount: float = Field(..., gt=0)
customer_email: str
7. Define Workflow Context
from state_machine_framework import WorkflowContext
class OrderWorkflowContext(WorkflowContext):
def define_requirements(self):
self.require_value('customer_id', str)
self.require_model('order', Order, created_in_state=OrderPending)
self.require_dict('order_data', OrderData)
8. Execute Workflow
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///orders.db')
Session = sessionmaker(bind=engine)
session = Session()
with OrderWorkflowContext(
OrderStateMachine,
customer_id='CUST123',
order_data={'amount': 99.99, 'customer_email': 'user@example.com'},
_session=session
) as workflow:
workflow.transition_to(OrderPending)
workflow.transition_to(OrderProcessing)
workflow.transition_to(OrderCompleted)
print(workflow.get_history_summary())
Package Structure
state_machine_framework/
โโโ __init__.py # Main package exports
โโโ core/
โ โโโ __init__.py
โ โโโ state.py # State base class and metaclass
โ โโโ state_machine.py # StateMachine base class
โ โโโ exceptions.py # Custom exceptions
โโโ decorators/
โ โโโ __init__.py
โ โโโ hooks.py # Decorators for validators, hooks, transitions
โโโ orm/
โ โโโ __init__.py
โ โโโ base.py # Abstract ORM adapter
โ โโโ sqlalchemy_adapter.py # SQLAlchemy implementation
โโโ workflow/
โโโ __init__.py
โโโ context.py # WorkflowContext implementation
โโโ requirements.py # Context requirement classes
Key Concepts
States
States represent points in your workflow. Define them by subclassing State:
class MyState(State):
is_start_state = True # Optional: mark as initial state
is_terminal_state = False # Optional: mark as final state
class Meta:
order = 1 # Execution order
ModelName_status = 'state_value' # State value for model
Validators
Validators run before state transitions and perform validation logic:
@validator(MyState, order=1)
def validate_something(state, data, context):
# Validation logic here
if not valid:
raise ValueError("Validation failed")
return {'result': 'validated'}
Hooks
Hooks run after state transitions and perform side effects:
@hook(MyState, order=1)
def do_something(state, data, context):
# Side effects here (logging, notifications, etc.)
instances = data['instances'] # Access model instances
return {'action': 'completed'}
Workflow Context
Workflow contexts provide type-safe, validated context management:
class MyWorkflowContext(WorkflowContext):
def define_requirements(self):
# Require a simple value
self.require_value('user_id', str, required=True)
# Require a model instance (may be created during workflow)
self.require_model('order', Order, created_in_state=OrderPending)
# Require a dict validated by Pydantic schema
self.require_dict('order_data', OrderDataSchema, required=True)
Examples
See the examples/ directory for a complete vending machine implementation demonstrating:
- SQLAlchemy ORM integration
- Workflow context with validation
- External validator and hook registration
- Type-safe context management
- Pydantic schema validation
- Complete transaction workflow
To run the example:
cd examples/vending_machine
python main.py
Dynamic ORM Adapters
Implement the ORMAdapter interface for your ORM:
from state_machine_framework import ORMAdapter, ORMAdapterFactory
class MyORMAdapter(ORMAdapter):
def create(self, model_cls, **data):
# Your implementation
pass
# ... implement all abstract methods
# Register it
ORMAdapterFactory.register_adapter('myorm', MyORMAdapter)
# Use it
sm = MyStateMachine(data, using_orm='myorm')
Requirements
- Python 3.8+
- pydantic >= 1.8.0
License
MIT License - see LICENSE file for details
Contributing
Contributions are welcome! Please feel free to reach out...
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file state_machine_framework-1.0.0.tar.gz.
File metadata
- Download URL: state_machine_framework-1.0.0.tar.gz
- Upload date:
- Size: 5.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.8.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
021713e330ea6bb837e12d7a34bab03e3f82090b45aa47abc69744a2131b1997
|
|
| MD5 |
ca0001b110719d83154e8affb899ae71
|
|
| BLAKE2b-256 |
29775918fd7fc211b06dda212568c100a6a3b92f5b44a5480f849a6c000ee737
|
File details
Details for the file state_machine_framework-1.0.0-py3-none-any.whl.
File metadata
- Download URL: state_machine_framework-1.0.0-py3-none-any.whl
- Upload date:
- Size: 5.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.8.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
73acbeb1c465b62ca84688f6267265801beda833436b3f87e41eafb6b8a3cdcf
|
|
| MD5 |
def5018b1a826ff2db1375c3846dbc63
|
|
| BLAKE2b-256 |
391cd8a204b748a9375c628f21bc90560c1b654d709959da30ba0ad5196ff19f
|