Simple finite state machines.
Project description
definite
Simple finite state machines.
Perfect for representing workflows.
Quickstart
from definite import FSM
# You define all the valid states, as well as what their allowed
# transitions are.
class Workflow(FSM):
allowed_transitions = {
"draft": ["awaiting_review", "rejected"],
"awaiting_review": ["draft", "reviewed", "rejected"],
"reviewed": ["published", "rejected"],
"published": None,
"rejected": ["draft"],
}
default_state = "draft"
# Right away, you can use the states/transitions as-is to enforce changes.
workflow = Workflow()
workflow.current_state() # "draft"
workflow.transition_to("awaiting_review")
workflow.transition_to("reviewed")
workflow.is_allowed("published") # True
# Invalid/disallowed transitions will throw an exception.
workflow.current_state() # "reviewed"
# ...which can only go to "published" or "rejected", but...
workflow.transition_to("awaiting_review")
# Traceback (most recent call last):
# ...
# workflow.TransitionNotAllowed: "reviewed" cannot transition to "awaiting_review"
# Additionally, you can set up extra code to fire on given state changes.
class Workflow(FSM):
# Same transitions & default state.
allowed_transitions = {
"draft": ["awaiting_review", "rejected"],
"awaiting_review": ["draft", "reviewed", "rejected"],
"reviewed": ["published", "rejected"],
"published": None,
"rejected": ["draft"],
}
default_state = "draft"
# Define a `handle_<state_name>` method on the class.
def handle_awaiting_review(self, new_state):
spell_check_results = check_spelling(self.obj.content)
msg = (
f"{self.obj.title} ready for review. "
f"{len(spell_check_results)} spelling errors."
)
send_email(to=editor_email, message=msg)
def handle_published(self, new_state):
self.obj.pub_date = datetime.datetime.utcnow()
self.obj.save()
# You can also setup code that fires on **ANY** valid transition with the
# special `handle_any` method.
def handle_any(self, new_state):
self.obj.state = new_state
self.obj.save()
# We can pull in any Python object, like a database-backed model, that we
# want to associate with our FSM.
from news.models import NewsPost
news_post = NewsPost.objects.create(
title="Hello world!",
content="This iz our frist post!",
state="draft",
)
# We start mostly the same, but this time pass an `obj` kwarg!
workflow = Workflow(obj=news_post)
# If you wanted to be explicit, you could also pass along the `initial_state`:
workflow = Workflow(
obj=news_post,
initial_state=news_post.state
)
workflow.current_state() # "draft"
# But when we trigger this change...
workflow.transition_to("awaiting_review")
# ...it triggers the spell check & the email we defined above, as well as
# hitting the `handle_any` method & updating the `state` field in the DB.
news_post.refresh_from_db()
news_post.state # "awaiting_review" !
Installation
pip install definite
Requirements
- Python 3.6+
Testing
$ pytest .
License
New BSD
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
definite-1.0.0.tar.gz
(6.1 kB
view details)
Built Distribution
File details
Details for the file definite-1.0.0.tar.gz
.
File metadata
- Download URL: definite-1.0.0.tar.gz
- Upload date:
- Size: 6.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.1.12 CPython/3.9.9 Darwin/21.5.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8cc05c844aca06cb4f83a846515ac34010dd1143e3e5951d9066dffebdbd6a82 |
|
MD5 | 07acd9770a2d198d03d55d69f12aafa7 |
|
BLAKE2b-256 | 44535a3d67b177d58ee9f2d749ba24b746579e066af0f399dc1a33e57f08d270 |
File details
Details for the file definite-1.0.0-py3-none-any.whl
.
File metadata
- Download URL: definite-1.0.0-py3-none-any.whl
- Upload date:
- Size: 6.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.1.12 CPython/3.9.9 Darwin/21.5.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | dfdb4c074054507981463fe1ef24f1d33c8f08390c68e2bb21d1f9b1cd1faaf2 |
|
MD5 | 0bb797f487189ab804a7741ab1ba1df8 |
|
BLAKE2b-256 | 966a3da4f304da5ee9228804734f282449fcbfb0b9d9c7e63341028cca7b631b |