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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repos:
exclude: docs/auto_examples
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: v0.15.0
rev: v0.15.8
hooks:
# Run the linter.
- id: ruff
Expand Down
32 changes: 17 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,38 @@ requires-python = ">=3.9"
homepage = "https://github.com/fgmacedo/python-statemachine"

[project.optional-dependencies]
diagrams = ["pydot >= 2.0.0"]
diagrams = ["pydot >= 4.0.1"]

[dependency-groups]
dev = [
"ruff >=0.15.0",
"ruff >=0.15.8",
"pre-commit",
"mypy",
"pytest",
"pytest-cov >=6.0.0; python_version >='3.9'",
"pytest-cov >=7.1.0; python_version >='3.9'",
"pytest-cov; python_version <'3.9'",
"pytest-sugar >=1.0.0",
"pytest-mock >=3.14.0",
"pytest-benchmark >=4.0.0",
"pytest-asyncio >=0.25.0",
"pytest-sugar >=1.1.1",
"pytest-mock >=3.15.1",
"pytest-benchmark >=5.2.3",
"pytest-asyncio >=1.3.0; python_version >='3.10'",
"pytest-asyncio >=0.25.0; python_version <'3.10'",
"pydot",
"django >=5.2.11; python_version >='3.10'",
"pytest-django >=4.8.0; python_version >'3.8'",
"django >=6.0.3; python_version >='3.12'",
"django >=5.2.12; python_version >='3.10' and python_version <'3.12'",
"pytest-django >=4.12.0; python_version >='3.10'",
"Sphinx; python_version >'3.8'",
"sphinx-gallery; python_version >'3.8'",
"myst-parser; python_version >'3.8'",
"pillow; python_version >'3.8'",
"sphinx-autobuild; python_version >'3.8'",
"furo >=2024.5.6; python_version >'3.8'",
"furo >=2025.12.19; python_version >'3.8'",
"sphinx-copybutton >=0.5.2; python_version >'3.8'",
"sphinxcontrib-mermaid; python_version >'3.8'",
"pdbr>=0.8.9; python_version >'3.8'",
"babel >=2.16.0; python_version >='3.8'",
"pytest-xdist>=3.6.1",
"pytest-timeout>=2.3.1",
"pyright>=1.1.400",
"pdbr>=0.9.7; python_version >'3.8'",
"babel >=2.18.0; python_version >='3.8'",
"pytest-xdist>=3.8.0",
"pytest-timeout>=2.4.0",
"pyright>=1.1.408",
]

[build-system]
Expand Down
7 changes: 4 additions & 3 deletions statemachine/contrib/diagram/renderers/dot.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass
from dataclasses import field
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
Expand Down Expand Up @@ -210,7 +211,7 @@ def _render_initial_arrow(
initial_node = self._create_initial_node(initial_node_id)
added_to_atomic = False

extra = {}
extra: Dict[str, Any] = {}
if initial_state.children:
extra["lhead"] = f"cluster_{initial_state.id}"

Expand Down Expand Up @@ -483,9 +484,9 @@ def _resolve_edge_endpoints(
self,
transition: DiagramTransition,
target_id: Optional[str],
) -> "tuple[str, str, Dict[str, str]]":
) -> "tuple[str, str, Dict[str, Any]]":
"""Resolve source/destination node IDs and cluster attributes for an edge."""
extra: Dict[str, str] = {}
extra: Dict[str, Any] = {}
source_is_compound = transition.source in self._compound_ids
target_is_compound = target_id is not None and target_id in self._compound_ids

Expand Down
3 changes: 2 additions & 1 deletion tests/test_contrib_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,8 @@ class parent(State.Compound, name="Parent"):
enter = start.to(parent)

dot = DotGraphMachine(SM)().to_string()
assert "lhead=cluster_parent" in dot
assert "lhead=" in dot
assert "cluster_parent" in dot


def test_initial_edge_inside_compound_subgraph():
Expand Down
15 changes: 15 additions & 0 deletions tests/test_scxml_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,21 @@
assert wrapper._instance is handler_instance


class TestInvokeSessionSendToParentAsync:
async def test_send_to_parent_awaitable(self):

Check warning on line 1035 in tests/test_scxml_units.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use asynchronous features in this function or remove the `async` keyword.

See more on https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&issues=AZ1RMq7W1htvfrG2f_uI&open=AZ1RMq7W1htvfrG2f_uI&pullRequest=608
"""send_to_parent calls ensure_future when parent.send returns an awaitable."""
from statemachine.io.scxml.invoke import _InvokeSession

async def async_send(event, **kwargs):

Check failure on line 1039 in tests/test_scxml_units.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a nested comment explaining why this function is empty, or complete the implementation.

See more on https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&issues=AZ1RMq7W1htvfrG2f_uJ&open=AZ1RMq7W1htvfrG2f_uJ&pullRequest=608
pass

parent = Mock()
parent.send = async_send
session = _InvokeSession(parent, invokeid="inv1")
# Should not raise — the coroutine is scheduled via ensure_future
session.send_to_parent("child.done", key="value")


class TestOrderedSetStr:
def test_str_representation(self):
"""OrderedSet.__str__ returns a set-like string."""
Expand Down
Loading
Loading