diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 5c496053..fdc758d6 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index eb93ce9f..7e9274fa 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,8 @@ You can now create an instance: This state machine can be represented graphically as follows: ```py +>>> # This example will only run on automated tests if dot is present +>>> getfixture("requires_dot_installed") >>> img_path = "docs/images/readme_trafficlightmachine.png" >>> sm._graph().write_png(img_path) diff --git a/conftest.py b/conftest.py index fcdcaf2c..4c2c0c63 100644 --- a/conftest.py +++ b/conftest.py @@ -1,3 +1,4 @@ +import shutil import sys import pytest @@ -31,3 +32,14 @@ def pytest_ignore_collect(collection_path, path, config): if "django_project" in str(path): return True + + +@pytest.fixture(scope="session") +def has_dot_installed(): + return bool(shutil.which("dot")) + + +@pytest.fixture() +def requires_dot_installed(request, has_dot_installed): + if not has_dot_installed: + pytest.skip(f"Test {request.node.nodeid} requires 'dot' that is not installed.") diff --git a/docs/actions.md b/docs/actions.md index ef8844be..f1c10c52 100644 --- a/docs/actions.md +++ b/docs/actions.md @@ -14,7 +14,7 @@ There are several action callbacks that you can define to interact with a StateMachine in execution. There are callbacks that you can specify that are generic and will be called -when something changes and are not bounded to a specific state or event: +when something changes, and are not bound to a specific state or event: - `before_transition()` @@ -26,7 +26,7 @@ when something changes and are not bounded to a specific state or event: - `after_transition()` -The following example can get you an overview of the "generic" callbacks available: +The following example offers an overview of the "generic" callbacks available: ```py >>> from statemachine import StateMachine, State diff --git a/docs/diagram.md b/docs/diagram.md index 2238bf46..8dae0aee 100644 --- a/docs/diagram.md +++ b/docs/diagram.md @@ -59,9 +59,23 @@ As this one: ![OrderControl](images/order_control_machine_initial.png) +If you find the resolution of the image lacking, you can + +```py +>>> dot.set_dpi(300) + +>>> dot.write_png("docs/images/order_control_machine_initial_300dpi.png") + +``` + +![OrderControl](images/order_control_machine_initial_300dpi.png) + + The current {ref}`state` is also highlighted: ``` py +>>> # This example will only run on automated tests if dot is present +>>> getfixture("requires_dot_installed") >>> from statemachine.contrib.diagram import DotGraphMachine diff --git a/docs/images/order_control_machine_initial.png b/docs/images/order_control_machine_initial.png index bd5cf06d..23f35e6a 100644 Binary files a/docs/images/order_control_machine_initial.png and b/docs/images/order_control_machine_initial.png differ diff --git a/docs/images/order_control_machine_initial_300dpi.png b/docs/images/order_control_machine_initial_300dpi.png new file mode 100644 index 00000000..ac76af90 Binary files /dev/null and b/docs/images/order_control_machine_initial_300dpi.png differ diff --git a/docs/images/order_control_machine_processing.png b/docs/images/order_control_machine_processing.png index 5355f078..a8e23fa9 100644 Binary files a/docs/images/order_control_machine_processing.png and b/docs/images/order_control_machine_processing.png differ diff --git a/docs/images/readme_trafficlightmachine.png b/docs/images/readme_trafficlightmachine.png index f52ea2cc..85f38f45 100644 Binary files a/docs/images/readme_trafficlightmachine.png and b/docs/images/readme_trafficlightmachine.png differ diff --git a/docs/images/test_state_machine_internal.png b/docs/images/test_state_machine_internal.png index 77806cdb..bbe8fb48 100644 Binary files a/docs/images/test_state_machine_internal.png and b/docs/images/test_state_machine_internal.png differ diff --git a/docs/transitions.md b/docs/transitions.md index 1752224c..32d17236 100644 --- a/docs/transitions.md +++ b/docs/transitions.md @@ -131,6 +131,9 @@ Example: Usage: ```py +>>> # This example will only run on automated tests if dot is present +>>> getfixture("requires_dot_installed") + >>> sm = TestStateMachine() >>> sm._graph().write_png("docs/images/test_state_machine_internal.png") @@ -163,7 +166,7 @@ the event name is used to describe the transition. ## Events An event is an external signal that something has happened. -They are send to a state machine and allow the state machine to react. +They are sent to a state machine and allow the state machine to react. An event starts a {ref}`transition`, which can be thought of as a "cause" that initiates a change in the state of the system. @@ -173,7 +176,7 @@ In `python-statemachine`, an event is specified as an attribute of the state mac ### Declaring events -The simplest way to declare an {ref}`event` is by assiging a transitions list to a name at the +The simplest way to declare an {ref}`event` is by assigning a transitions list to a name at the State machine class level. The name will be converted to an {ref}`Event`: ```py @@ -193,7 +196,7 @@ True ``` ```{versionadded} 2.4.0 -You can also explict declare an {ref}`Event` instance, this helps IDEs to know that the event is callable and also with transtation strings. +You can also explictly declare an {ref}`Event` instance, this helps IDEs to know that the event is callable, and also with translation strings. ``` To declare an explicit event you must also import the {ref}`Event`: diff --git a/pyproject.toml b/pyproject.toml index 83ab3fd8..b324b715 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,8 +2,8 @@ name = "python-statemachine" version = "2.5.0" description = "Python Finite State Machines made easy." -authors = [{ name = "Fernando Macedo", email = "fgmacedo@gmail" }] -maintainers = [{ name = "Fernando Macedo", email = "fgmacedo@gmail" }] +authors = [{ name = "Fernando Macedo", email = "fgmacedo@gmail.com" }] +maintainers = [{ name = "Fernando Macedo", email = "fgmacedo@gmail.com" }] license = { text = "MIT License" } readme = "README.md" classifiers = [ diff --git a/statemachine/event.py b/statemachine/event.py index d8fa511d..a82d9186 100644 --- a/statemachine/event.py +++ b/statemachine/event.py @@ -28,9 +28,9 @@ class Event(AddCallbacksMixin, str): - """An event is triggers a signal that something has happened. + """An event triggers a signal that something has happened. - They are send to a state machine and allow the state machine to react. + They are sent to a state machine and allow the state machine to react. An event starts a :ref:`Transition`, which can be thought of as a “cause” that initiates a change in the state of the system. diff --git a/statemachine/statemachine.py b/statemachine/statemachine.py index 378d55b5..e5fe2628 100644 --- a/statemachine/statemachine.py +++ b/statemachine/statemachine.py @@ -75,7 +75,7 @@ def __init__( allow_event_without_transition: bool = False, listeners: "List[object] | None" = None, ): - self.model = model if model else Model() + self.model = model if model is not None else Model() self.state_field = state_field self.start_value = start_value self.allow_event_without_transition = allow_event_without_transition diff --git a/tests/scrape_images.py b/tests/scrape_images.py index 1f57b6aa..2547b536 100644 --- a/tests/scrape_images.py +++ b/tests/scrape_images.py @@ -1,3 +1,4 @@ +import os import re from statemachine.contrib.diagram import DotGraphMachine @@ -13,7 +14,8 @@ class MachineScraper: def __init__(self, project_root): self.project_root = project_root - self.re_machine_module_name = re.compile(f"{self.project_root}/(.*).py$") + sanitized_path = re.escape(os.path.abspath(self.project_root)) + self.re_machine_module_name = re.compile(f"{sanitized_path}/(.*)\\.py$") self.seen = set() def __repr__(self): diff --git a/tests/test_contrib_diagram.py b/tests/test_contrib_diagram.py index b54a43d3..3b2516b3 100644 --- a/tests/test_contrib_diagram.py +++ b/tests/test_contrib_diagram.py @@ -7,6 +7,8 @@ from statemachine.contrib.diagram import main from statemachine.contrib.diagram import quickchart_write_svg +pytestmark = pytest.mark.usefixtures("requires_dot_installed") + @pytest.fixture( params=[ diff --git a/tests/test_statemachine.py b/tests/test_statemachine.py index 9337e025..9ff82897 100644 --- a/tests/test_statemachine.py +++ b/tests/test_statemachine.py @@ -488,3 +488,18 @@ class TrapStateMachine(StateMachine): start = started.to(producing) close = started.to(closed) add_job = producing.to.itself(internal=True) + + +def test_model_with_custom_bool_is_not_replaced(campaign_machine): + class FalseyModel(MyModel): + def __bool__(self): + return False + + model = FalseyModel() + machine = campaign_machine(model) + + assert machine.model is model + assert model.state == "draft" + + machine.produce() + assert model.state == "producing" pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy