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
5 changes: 2 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,10 @@ repos:
uv run python -m statemachine.contrib.diagram
tests.examples.traffic_light_machine.TrafficLightMachine
docs/images/readme_trafficlightmachine.png
--events cycle cycle cycle
language: system
pass_filenames: false
files: >-
(statemachine/contrib/diagram/
|tests/examples/traffic_light_machine\.py)
files: (statemachine/contrib/diagram/|tests/examples/traffic_light_machine\.py)
- id: pytest
name: Pytest
entry: uv run pytest -n auto --cov-fail-under=100
Expand Down
7 changes: 7 additions & 0 deletions docs/diagram.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ The output format is inferred from the file extension:
python -m statemachine.contrib.diagram tests.examples.traffic_light_machine.TrafficLightMachine diagram.png
```

To highlight the current state, use `--events` to instantiate the machine and
send events before rendering:

```bash
python -m statemachine.contrib.diagram tests.examples.traffic_light_machine.TrafficLightMachine diagram.png --events cycle cycle cycle
```


## Sphinx directive

Expand Down
Binary file modified docs/images/readme_trafficlightmachine.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions docs/releases/3.1.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ machine instance concurrently. This is now documented in the
[#592](https://github.com/fgmacedo/python-statemachine/pull/592).


### Diagram CLI `--events` option

The `python -m statemachine.contrib.diagram` command now accepts `--events` to
instantiate the machine and send events before rendering, highlighting the
current active state — matching the Sphinx directive's `:events:` option.
See {ref}`diagram:Command line` for details.

### Bugfixes in 3.1.0

- Fixes silent misuse of `Event()` with multiple positional arguments. Passing more than one
Expand Down
21 changes: 18 additions & 3 deletions statemachine/contrib/diagram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,28 @@ def import_sm(qualname):
return smclass


def write_image(qualname, out):
def write_image(qualname, out, events=None):
"""
Given a `qualname`, that is the fully qualified dotted path to a StateMachine
classes, imports the class and generates a dot graph using the `pydot` lib.
Writes the graph representation to the filename 'out' that will
open/create and truncate such file and write on it a representation of
the graph defined by the statemachine, in the format specified by
the extension contained in the out path (out.ext).

If `events` is provided, the machine is instantiated and each event is sent
before rendering, so the diagram highlights the current active state.
"""
smclass = import_sm(qualname)

graph = DotGraphMachine(smclass).get_graph()
if events:
machine = smclass()
for event_name in events:
machine.send(event_name)
else:
machine = smclass

graph = DotGraphMachine(machine).get_graph()
out_extension = out.rsplit(".", 1)[1]
graph.write(out, format=out_extension)

Expand All @@ -165,6 +175,11 @@ def main(argv=None):
"out",
help="File to generate the image using extension as the output format.",
)
parser.add_argument(
"--events",
nargs="+",
help="Instantiate the machine and send these events before rendering.",
)

args = parser.parse_args(argv)
write_image(qualname=args.class_path, out=args.out)
write_image(qualname=args.class_path, out=args.out, events=args.events)
18 changes: 18 additions & 0 deletions tests/test_contrib_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,24 @@ def test_generate_complain_about_bad_sm_path(self, capsys, tmp_path):
]
)

def test_generate_image_with_events(self, tmp_path):
"""CLI --events instantiates the machine and sends events before rendering."""
out = tmp_path / "sm.png"

main(
[
"tests.examples.traffic_light_machine.TrafficLightMachine",
str(out),
"--events",
"cycle",
"cycle",
"cycle",
]
)

assert out.exists()
assert out.stat().st_size > 0

def test_generate_complain_about_module_without_sm(self, tmp_path):
out = tmp_path / "sm.svg"

Expand Down
Loading