Skip to content

Commit cac607a

Browse files
authored
fix: validate Event() id parameter to prevent silent transition loss (#588)
* fix: validate Event() id parameter to prevent silent transition loss Passing multiple transitions as positional args to Event() (e.g., Event(t1, t2)) caused the second argument to be silently interpreted as the event id, leaving those transitions eventless (auto-firing). Now raises InvalidDefinition with a message suggesting the | operator.
1 parent b4897d7 commit cac607a

7 files changed

Lines changed: 840 additions & 798 deletions

File tree

docs/releases/3.1.0.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# StateChart 3.1.0
2+
3+
*Not released yet*
4+
5+
## What's new in 3.1.0
6+
7+
### Bugfixes in 3.1.0
8+
9+
- Fixes silent misuse of `Event()` with multiple positional arguments. Passing more than one
10+
transition to `Event()` (e.g., `Event(t1, t2)`) now raises {ref}`InvalidDefinition` with a
11+
clear message suggesting the `|` operator. Previously, the second argument was silently
12+
interpreted as the event `id`, leaving the extra transitions eventless (auto-firing).
13+
[#588](https://github.com/fgmacedo/python-statemachine/pull/588).
14+
15+
## Misc in 3.1.0

docs/releases/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Requires Python 3.9+.
1616
```{toctree}
1717
:maxdepth: 2
1818
19+
3.1.0
1920
3.0.0
2021
2122
```

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "python-statemachine"
3-
version = "3.0.0"
3+
version = "3.1.0"
44
description = "Python Finite State Machines made easy."
55
authors = [{ name = "Fernando Macedo", email = "fgmacedo@gmail.com" }]
66
maintainers = [{ name = "Fernando Macedo", email = "fgmacedo@gmail.com" }]

statemachine/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
__author__ = """Fernando Macedo"""
1010
__email__ = "fgmacedo@gmail.com"
11-
__version__ = "3.0.0"
11+
__version__ = "3.1.0"
1212

1313
__all__ = [
1414
"StateChart",

statemachine/event.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ def __new__(
8888
id = transitions
8989
transitions = None
9090

91+
if id is not None and not isinstance(id, str):
92+
raise InvalidDefinition(
93+
_(
94+
"Event() received a non-string 'id' ({cls_name}). "
95+
"To combine multiple transitions under one event, "
96+
"use the | operator: t1 | t2."
97+
).format(cls_name=type(id).__name__)
98+
)
99+
91100
_has_real_id = id is not None
92101
id = str(id) if _has_real_id else f"__event__{uuid4().hex}"
93102

tests/test_events.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,18 @@ def test_events_match_none_with_empty():
330330

331331
events = Events()
332332
assert events.match(None) is True
333+
334+
335+
def test_event_raises_on_non_string_id():
336+
"""Event() should raise InvalidDefinition when id is not a string.
337+
338+
This catches a common mistake where users pass multiple transitions as
339+
positional args (e.g. Event(t1, t2)) instead of combining them with |.
340+
"""
341+
s1 = State(initial=True)
342+
s2 = State(final=True)
343+
t1 = s1.to(s2)
344+
t2 = s2.to(s1)
345+
346+
with pytest.raises(InvalidDefinition, match="non-string 'id'.*use the \\| operator"):
347+
Event(t1, t2)

uv.lock

Lines changed: 798 additions & 796 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)