Skip to main content

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

Metadata

Release files for definite 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for definite 1.0.0
File Size Uploaded Provenance Actions
definite-1.0.0.tar.gz 6.1 kB Without provenance Details for definite-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for definite 1.0.0
File Interpreter ABI Platform Actions
definite-1.0.0-py3-none-any.whl Python 3 none any Details for definite-1.0.0-py3-none-any.whl

Total release size: 12.5 kB

Release files / definite-1.0.0.tar.gz

Download URL definite-1.0.0.tar.gz
Size 6.1 kB
Tags Source
SHA-256 checksum
How to use checksums
8cc05c844aca06cb4f83a846515ac34010dd1143e3e5951d9066dffebdbd6a82
BLAKE2b-256 checksum
How to use checksums
44535a3d67b177d58ee9f2d749ba24b746579e066af0f399dc1a33e57f08d270
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.12 CPython/3.9.9 Darwin/21.5.0

Release files / definite-1.0.0-py3-none-any.whl

Download URL definite-1.0.0-py3-none-any.whl
Size 6.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dfdb4c074054507981463fe1ef24f1d33c8f08390c68e2bb21d1f9b1cd1faaf2
BLAKE2b-256 checksum
How to use checksums
966a3da4f304da5ee9228804734f282449fcbfb0b9d9c7e63341028cca7b631b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.12 CPython/3.9.9 Darwin/21.5.0

Security for definite 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page