Skip to content

Commit ee3607a

Browse files
authored
fix: render human-readable Event name in diagrams (#601)
* chore: add tmp/ to .gitignore * fix: render human-readable Event name in diagrams (#600) Event.name is now auto-generated as a humanized form of the id (split by _ and . separators, first word capitalized) instead of echoing the raw identifier. Explicit name= passed by the user is preserved. - Add humanize_id() in utils.py (compiled regex, shared by Event and State) - Remove redundant name= from Event call sites in factory, events, and SCXML actions so auto-generation kicks in - Use event.name in diagram labels (_format_event_names) - Update docs and tests to reflect humanized names Closes #600 * docs: add event humanized name to 3.1.0 release notes
1 parent 67f2b0d commit ee3607a

22 files changed

+137
-80
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,6 @@ target/
7979
docs/auto_examples/sg_execution_times.*
8080
docs/auto_examples/*.pickle
8181
docs/sg_execution_times.rst
82+
83+
# Temporary files
84+
tmp/

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ Generate a diagram or get a text representation with f-strings:
8383
>>> print(f"{sm:md}")
8484
| State | Event | Guard | Target |
8585
| ------ | ----- | ----- | ------ |
86-
| Green | cycle | | Yellow |
87-
| Yellow | cycle | | Red |
88-
| Red | cycle | | Green |
86+
| Green | Cycle | | Yellow |
87+
| Yellow | Cycle | | Red |
88+
| Red | Cycle | | Green |
89+
<BLANKLINE>
8990

9091
```
9192

docs/diagram.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ stateDiagram-v2
126126
state "Yellow" as yellow
127127
state "Red" as red
128128
[*] --> green
129-
green --> yellow : cycle
130-
yellow --> red : cycle
131-
red --> green : cycle
129+
green --> yellow : Cycle
130+
yellow --> red : Cycle
131+
red --> green : Cycle
132132
<BLANKLINE>
133133
classDef active fill:#40E0D0,stroke:#333
134134
green:::active
@@ -137,9 +137,9 @@ stateDiagram-v2
137137
>>> print(f"{sm:md}")
138138
| State | Event | Guard | Target |
139139
| ------ | ----- | ----- | ------ |
140-
| Green | cycle | | Yellow |
141-
| Yellow | cycle | | Red |
142-
| Red | cycle | | Green |
140+
| Green | Cycle | | Yellow |
141+
| Yellow | Cycle | | Red |
142+
| Red | Cycle | | Green |
143143
<BLANKLINE>
144144

145145
```
@@ -154,9 +154,9 @@ stateDiagram-v2
154154
state "Yellow" as yellow
155155
state "Red" as red
156156
[*] --> green
157-
green --> yellow : cycle
158-
yellow --> red : cycle
159-
red --> green : cycle
157+
green --> yellow : Cycle
158+
yellow --> red : Cycle
159+
red --> green : Cycle
160160
<BLANKLINE>
161161

162162
```
@@ -191,9 +191,9 @@ stateDiagram-v2
191191
state "Yellow" as yellow
192192
state "Red" as red
193193
[*] --> green
194-
green --> yellow : cycle
195-
yellow --> red : cycle
196-
red --> green : cycle
194+
green --> yellow : Cycle
195+
yellow --> red : Cycle
196+
red --> green : Cycle
197197
<BLANKLINE>
198198

199199
>>> formatter.supported_formats()
@@ -294,9 +294,9 @@ A traffic light.
294294
<BLANKLINE>
295295
| State | Event | Guard | Target |
296296
| ------ | ----- | ----- | ------ |
297-
| Green | cycle | | Yellow |
298-
| Yellow | cycle | | Red |
299-
| Red | cycle | | Green |
297+
| Green | Cycle | | Yellow |
298+
| Yellow | Cycle | | Red |
299+
| Red | Cycle | | Green |
300300
<BLANKLINE>
301301
<BLANKLINE>
302302

docs/events.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,21 +80,31 @@ Every event has two string properties:
8080

8181
- **`id`** — the programmatic identifier, derived from the class attribute name.
8282
Use this in `send()`, guards, and comparisons.
83-
- **`name`** — a human-readable label for display purposes. Defaults to the `id`
84-
when not explicitly set.
83+
- **`name`** — a human-readable label for display purposes. Auto-generated from
84+
the `id` by replacing `_` and `.` with spaces and capitalizing the first word.
85+
You can override the automatic name by passing `name=` explicitly when
86+
declaring the event:
8587

8688
```py
8789
>>> TrafficLight.cycle.id
8890
'cycle'
8991

9092
>>> TrafficLight.cycle.name
91-
'cycle'
93+
'Cycle'
94+
95+
>>> class Example(StateChart):
96+
... on = State(initial=True)
97+
... off = State(final=True)
98+
... shut_down = Event(on.to(off), name="Shut the system down")
99+
100+
>>> Example.shut_down.name
101+
'Shut the system down'
92102

93103
```
94104

95105
```{tip}
96106
Always use `event.id` for programmatic checks. The `name` property is intended
97-
for UI display and may change format in future versions.
107+
for UI display and may differ from the `id`.
98108
```
99109

100110

609 Bytes
Loading

docs/releases/3.1.0.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,11 @@ machine instance concurrently. This is now documented in the
158158
interpreted as the event `id`, leaving the extra transitions eventless (auto-firing).
159159
[#588](https://github.com/fgmacedo/python-statemachine/pull/588).
160160

161+
- `Event.name` is now auto-humanized from the `id` (e.g., `cycle``Cycle`,
162+
`pick_up``Pick up`). Diagrams, Mermaid output, and text tables all display
163+
the human-readable name. Explicit `name=` values are preserved. The same
164+
`humanize_id()` helper is now shared by `Event` and `State`.
165+
[#601](https://github.com/fgmacedo/python-statemachine/pull/601),
166+
fixes [#600](https://github.com/fgmacedo/python-statemachine/issues/600).
167+
161168
## Misc in 3.1.0

docs/tutorial.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,9 @@ You can also get text representations of any state machine using Python's built-
375375
>>> print(f"{CoffeeOrder:md}")
376376
| State | Event | Guard | Target |
377377
| --------- | ------- | ----- | --------- |
378-
| Pending | start | | Preparing |
379-
| Preparing | finish | | Ready |
380-
| Ready | pick_up | | Picked up |
378+
| Pending | Start | | Preparing |
379+
| Preparing | Finish | | Ready |
380+
| Ready | Pick up | | Picked up |
381381

382382
```
383383

@@ -394,9 +394,9 @@ stateDiagram-v2
394394
state "Picked up" as picked_up
395395
[*] --> pending
396396
picked_up --> [*]
397-
pending --> preparing : start
398-
preparing --> ready : finish
399-
ready --> picked_up : pick_up
397+
pending --> preparing : Start
398+
preparing --> ready : Finish
399+
ready --> picked_up : Pick up
400400
<BLANKLINE>
401401

402402
```

statemachine/contrib/diagram/extract.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,17 @@ def _format_event_names(transition: "Transition") -> str:
116116

117117
all_ids = {str(e) for e in events}
118118

119+
seen_ids: Set[str] = set()
119120
display: List[str] = []
120121
for event in events:
121122
eid = str(event)
122123
# Skip dot-form aliases (e.g. "done.invoke.X") when the underscore
123124
# form ("done_invoke_X") is also registered on this transition.
124125
if "." in eid and eid.replace(".", "_") in all_ids:
125126
continue
126-
if eid not in display: # pragma: no branch
127-
display.append(eid)
127+
if eid not in seen_ids: # pragma: no branch
128+
seen_ids.add(eid)
129+
display.append(event.name if event.name else eid)
128130

129131
return " ".join(display)
130132

statemachine/event.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .exceptions import InvalidDefinition
1010
from .i18n import _
1111
from .transition_mixin import AddCallbacksMixin
12+
from .utils import humanize_id
1213

1314
if TYPE_CHECKING:
1415
from .statemachine import StateChart
@@ -107,7 +108,7 @@ def __new__(
107108
if name:
108109
instance.name = name
109110
elif _has_real_id:
110-
instance.name = str(id).replace("_", " ").capitalize()
111+
instance.name = humanize_id(id)
111112
else:
112113
instance.name = ""
113114
if transitions:

statemachine/events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def add(self, events):
3030
if isinstance(event, Event):
3131
self._items.append(event)
3232
else:
33-
self._items.append(Event(id=event, name=event))
33+
self._items.append(Event(id=event))
3434

3535
return self
3636

0 commit comments

Comments
 (0)