Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["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", "3.14"]

steps:
- uses: actions/checkout@v4
Expand All @@ -33,15 +33,11 @@ jobs:
cache-suffix: "python${{ matrix.python-version }}"
- name: Install the project
run: uv sync --all-extras --dev
- name: Install old pydot for 3.7 only
if: matrix.python-version == 3.7
run: |
uv pip install pydot==2.0.0
#----------------------------------------------
# run ruff
#----------------------------------------------
- name: Linter with ruff
if: matrix.python-version == 3.13
if: matrix.python-version == 3.14
run: |
uv run ruff check .
uv run ruff format --check .
Expand All @@ -57,7 +53,7 @@ jobs:
#----------------------------------------------
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
if: matrix.python-version == 3.13
if: matrix.python-version == 3.14
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
directory: .
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
python-version: '3.14'

- name: Setup Graphviz
uses: ts-graphviz/setup-graphviz@v2
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version: 2
build:
os: "ubuntu-22.04"
tools:
python: "3.12"
python: "3.14"
apt_packages:
- graphviz
jobs:
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ uv run mypy statemachine/ tests/

## Code style

- **Formatter/Linter:** ruff (line length 99, target Python 3.13)
- **Formatter/Linter:** ruff (line length 99, target Python 3.14)
- **Rules:** pycodestyle, pyflakes, isort, pyupgrade, flake8-comprehensions, flake8-bugbear, flake8-pytest-style
- **Imports:** single-line, sorted by isort
- **Docstrings:** Google convention
Expand Down
3 changes: 2 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

@pytest.fixture(autouse=True, scope="session")
def add_doctest_context(doctest_namespace): # noqa: PT004
from statemachine.utils import run_async_from_sync

from statemachine import State
from statemachine import StateMachine
from statemachine.utils import run_async_from_sync

class ContribAsyncio:
"""
Expand Down
11 changes: 7 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
Expand All @@ -37,7 +38,8 @@ dev = [
"pre-commit",
"mypy",
"pytest",
"pytest-cov",
"pytest-cov >=6.0.0; python_version >='3.9'",
"pytest-cov; python_version <'3.9'",
"pytest-sugar >=1.0.0",
"pytest-mock >=3.10.0",
"pytest-benchmark >=4.0.0",
Expand Down Expand Up @@ -113,7 +115,7 @@ directory = "tmp/htmlcov"
show_contexts = true

[tool.mypy]
python_version = "3.13"
python_version = "3.14"
warn_return_any = true
warn_unused_configs = true
disable_error_code = "annotation-unchecked"
Expand All @@ -127,7 +129,7 @@ ignore_missing_imports = true
src = ["statemachine"]

line-length = 99
target-version = "py313"
target-version = "py314"

# Exclude a variety of commonly ignored directories.
exclude = [
Expand Down Expand Up @@ -166,7 +168,8 @@ select = [
ignore = [
"UP006", # `use-pep585-annotation` Requires Python3.9+
"UP035", # `use-pep585-annotation` Requires Python3.9+
"UP038", # `use-pep585-annotation` Requires Python3.9+
"UP037", # `remove-quotes-from-type-annotation` Not safe without `from __future__ import annotations`
"UP042", # `use-str-enum` Requires Python3.11+
]

# Allow unused variables when underscore-prefixed.
Expand Down
3 changes: 1 addition & 2 deletions statemachine/signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ def bind_expected(self, *args: Any, **kwargs: Any) -> BoundArguments: # noqa: C
elif param.name in kwargs:
if param.kind == Parameter.POSITIONAL_ONLY:
msg = (
"{arg!r} parameter is positional only, "
"but was passed as a keyword"
"{arg!r} parameter is positional only, but was passed as a keyword"
)
msg = msg.format(arg=param.name)
raise TypeError(msg) from None
Expand Down
4 changes: 2 additions & 2 deletions statemachine/statemachine.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def _add_listener(self, listeners: "Listeners", allowed_references: SpecReferenc
return self

def _register_callbacks(self, listeners: List[object]):
self._listeners.update({listener: None for listener in listeners})
self._listeners.update(dict.fromkeys(listeners))
self._add_listener(
Listeners.from_listeners(
(
Expand Down Expand Up @@ -223,7 +223,7 @@ def add_listener(self, *listeners):

:ref:`listeners`.
"""
self._listeners.update({o: None for o in listeners})
self._listeners.update(dict.fromkeys(listeners))
return self._add_listener(
Listeners.from_listeners(Listener.from_obj(o) for o in listeners),
allowed_references=SPECS_SAFE,
Expand Down
1 change: 0 additions & 1 deletion tests/django_project/workflow/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from django.contrib.auth import get_user_model
from django.db import models

from statemachine.mixins import MachineMixin

User = get_user_model()
Expand Down
3 changes: 2 additions & 1 deletion tests/django_project/workflow/statemachines.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from statemachine import StateMachine
from statemachine.states import States

from statemachine import StateMachine

from .models import WorkflowSteps


Expand Down
2 changes: 1 addition & 1 deletion tests/django_project/workflow/tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from statemachine.exceptions import TransitionNotAllowed

from workflow.models import WorkflowSteps
from workflow.statemachines import WorfklowStateMachine

Expand Down
3 changes: 2 additions & 1 deletion tests/examples/air_conditioner_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

import random

from statemachine.utils import run_async_from_sync

from statemachine import State
from statemachine import StateMachine
from statemachine.utils import run_async_from_sync


def sensor_temperature_reader(seed: int, lower: int = 15, higher: int = 35):
Expand Down
3 changes: 2 additions & 1 deletion tests/examples/enum_campaign_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@

from enum import Enum

from statemachine import StateMachine
from statemachine.states import States

from statemachine import StateMachine


class CampaignStatus(Enum):
DRAFT = 1
Expand Down
3 changes: 2 additions & 1 deletion tests/examples/lor_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

"""

from statemachine.exceptions import TransitionNotAllowed

from statemachine import State
from statemachine import StateMachine
from statemachine.exceptions import TransitionNotAllowed


class LordOfTheRingsQuestStateMachine(StateMachine):
Expand Down
3 changes: 2 additions & 1 deletion tests/examples/order_control_rich_model_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

"""

from statemachine.exceptions import InvalidDefinition

from statemachine import State
from statemachine import StateMachine
from statemachine.exceptions import InvalidDefinition


class Order:
Expand Down
3 changes: 2 additions & 1 deletion tests/examples/user_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
from dataclasses import dataclass
from enum import Enum

from statemachine.states import States

from statemachine import State
from statemachine import StateMachine
from statemachine.states import States


class UserStatus(str, Enum):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_async.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import re

import pytest
from statemachine.exceptions import InvalidStateValue

from statemachine import State
from statemachine import StateMachine
from statemachine.exceptions import InvalidStateValue


@pytest.fixture()
Expand Down
6 changes: 3 additions & 3 deletions tests/test_callbacks.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
from unittest import mock

import pytest

from statemachine import State
from statemachine import StateMachine
from statemachine.callbacks import CallbackGroup
from statemachine.callbacks import CallbackSpec
from statemachine.callbacks import CallbackSpecList
from statemachine.callbacks import CallbacksRegistry
from statemachine.dispatcher import resolver_factory_from_objects
from statemachine.exceptions import InvalidDefinition

from statemachine import State
from statemachine import StateMachine


@pytest.fixture()
def ObjectWithCallbacks():
Expand Down
2 changes: 1 addition & 1 deletion tests/test_conditions_algebra.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import pytest
from statemachine.exceptions import InvalidDefinition

from statemachine import State
from statemachine import StateMachine
from statemachine.exceptions import InvalidDefinition


class AnyConditionSM(StateMachine):
Expand Down
1 change: 0 additions & 1 deletion tests/test_contrib_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from unittest import mock

import pytest

from statemachine.contrib.diagram import DotGraphMachine
from statemachine.contrib.diagram import main
from statemachine.contrib.diagram import quickchart_write_svg
Expand Down
4 changes: 2 additions & 2 deletions tests/test_copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from enum import auto

import pytest
from statemachine.exceptions import TransitionNotAllowed
from statemachine.states import States

from statemachine import State
from statemachine import StateMachine
from statemachine.exceptions import TransitionNotAllowed
from statemachine.states import States

logger = logging.getLogger(__name__)
DEBUG = logging.DEBUG
Expand Down
1 change: 0 additions & 1 deletion tests/test_dispatcher.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import pytest

from statemachine.callbacks import CallbackGroup
from statemachine.callbacks import CallbackSpec
from statemachine.dispatcher import Listener
Expand Down
4 changes: 2 additions & 2 deletions tests/test_events.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import pytest
from statemachine.event import Event
from statemachine.exceptions import InvalidDefinition

from statemachine import State
from statemachine import StateMachine
from statemachine.event import Event
from statemachine.exceptions import InvalidDefinition


def test_assign_events_on_transitions():
Expand Down
1 change: 0 additions & 1 deletion tests/test_listener.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import pytest

from statemachine.state import State
from statemachine.statemachine import StateMachine

Expand Down
2 changes: 1 addition & 1 deletion tests/test_mixins.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from statemachine.mixins import MachineMixin

from tests.models import MyModel


Expand Down
2 changes: 1 addition & 1 deletion tests/test_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class CampaignMachine(StateMachine):
assert "CampaignMachine" in registry._REGISTRY
assert registry.get_machine_cls("tests.test_registry.CampaignMachine") == CampaignMachine

with pytest.warns(DeprecationWarning):
with pytest.warns(DeprecationWarning, match="fully qualified names"):
assert registry.get_machine_cls("CampaignMachine") == CampaignMachine


Expand Down
4 changes: 2 additions & 2 deletions tests/test_rtc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
from unittest import mock

import pytest
from statemachine.exceptions import InvalidDefinition
from statemachine.exceptions import TransitionNotAllowed

from statemachine import State
from statemachine import StateMachine
from statemachine.exceptions import InvalidDefinition
from statemachine.exceptions import TransitionNotAllowed


@pytest.fixture()
Expand Down
1 change: 0 additions & 1 deletion tests/test_signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from functools import partial

import pytest

from statemachine.dispatcher import callable_method


Expand Down
1 change: 0 additions & 1 deletion tests/test_signature_positional_only.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import inspect

import pytest

from statemachine.dispatcher import callable_method


Expand Down
1 change: 0 additions & 1 deletion tests/test_spec_parser.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging

import pytest

from statemachine.spec_parser import operator_mapping
from statemachine.spec_parser import parse_boolean_expr

Expand Down
4 changes: 2 additions & 2 deletions tests/test_transition_list.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import pytest

from statemachine import State
from statemachine.callbacks import CallbacksRegistry
from statemachine.dispatcher import resolver_factory_from_objects

from statemachine import State


def test_transition_list_or_operator():
s1 = State("s1", initial=True)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_transitions.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import pytest
from statemachine.exceptions import InvalidDefinition
from statemachine.transition import Transition

from statemachine import State
from statemachine import StateMachine
from statemachine.exceptions import InvalidDefinition
from statemachine.transition import Transition

from .models import MyModel

Expand Down
Loading
Loading