From f873d967211a07740ef2b1dc98ca6573b035b1fb Mon Sep 17 00:00:00 2001 From: Basil Chen <192173459+rastagan-git@users.noreply.github.com> Date: Sun, 13 Sep 2026 15:35:00 +0800 Subject: [PATCH] fix: resolve Sphinx diagram zoom links relative to the current page --- docs/images/readme_trafficlightmachine.png | Bin 12504 -> 14537 bytes statemachine/contrib/diagram/sphinx_ext.py | 4 +- tests/test_contrib_diagram.py | 51 ++++++++++++++++++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/docs/images/readme_trafficlightmachine.png b/docs/images/readme_trafficlightmachine.png index f5179c05a3378e453ae7557555ef988a88ca0404..2f65f4110932850e093a1b47865e7a640c1e1ea3 100644 GIT binary patch literal 14537 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 Mdn{Wn{WSRh0NuWx$^ZZW diff --git a/statemachine/contrib/diagram/sphinx_ext.py b/statemachine/contrib/diagram/sphinx_ext.py index 84ab50f3..8313b554 100644 --- a/statemachine/contrib/diagram/sphinx_ext.py +++ b/statemachine/contrib/diagram/sphinx_ext.py @@ -24,6 +24,7 @@ from docutils import nodes from docutils.parsers.rst import directives from sphinx.util.docutils import SphinxDirective +from sphinx.util.osutil import relative_uri if TYPE_CHECKING: from sphinx.application import Sphinx @@ -258,7 +259,8 @@ def _resolve_target(self, svg_text: str) -> str: with open(outpath, "w", encoding="utf-8") as f: f.write(svg_text) - return f"/_images/{filename}" + page_uri = self.env.app.builder.get_target_uri(self.env.docname) + return relative_uri(page_uri, f"_images/{filename}") def _build_wrapper_classes(self) -> list[str]: """Build CSS class list for the outer wrapper element.""" diff --git a/tests/test_contrib_diagram.py b/tests/test_contrib_diagram.py index 0aaa4685..ad9fceb3 100644 --- a/tests/test_contrib_diagram.py +++ b/tests/test_contrib_diagram.py @@ -1,10 +1,12 @@ import re from contextlib import contextmanager from unittest import mock +from urllib.parse import urljoin from xml.etree import ElementTree import pytest from docutils import nodes +from sphinx.testing.util import SphinxTestApp from statemachine.contrib.diagram import DotGraphMachine from statemachine.contrib.diagram import main from statemachine.contrib.diagram import quickchart_write_svg @@ -1146,6 +1148,8 @@ def _make_directive(self, options=None, tmp_path=None): if tmp_path is not None: directive.state = mock.MagicMock() directive.state.document.settings.env.app.outdir = str(tmp_path) + directive.env.docname = "index" + directive.env.app.builder.get_target_uri.return_value = "index.html" return directive def test_no_target_option(self): @@ -1161,7 +1165,7 @@ def test_empty_target_generates_file(self, tmp_path): svg_data = "" result = directive._resolve_target(svg_data) - assert result.startswith("/_images/statemachine-") + assert result.startswith("_images/statemachine-") assert result.endswith(".svg") # Verify the file was written @@ -1185,6 +1189,47 @@ def test_different_events_different_filename(self, tmp_path): assert d1._resolve_target("") != d2._resolve_target("") +@pytest.mark.parametrize("buildername", ["html", "dirhtml"]) +@pytest.mark.parametrize("docname", ["index", "guide/overview", "guide/index"]) +def test_sphinx_zoom_link_resolves_under_documentation_prefix( + tmp_path, request, buildername, docname +): + srcdir = tmp_path / "src" + srcdir.mkdir() + (srcdir / "conf.py").write_text( + 'extensions = ["statemachine.contrib.diagram.sphinx_ext"]\n', encoding="utf-8" + ) + document = srcdir / f"{docname}.rst" + document.parent.mkdir(parents=True, exist_ok=True) + document.write_text( + "Diagram\n=======\n\n" + ".. statemachine-diagram:: tests.examples.traffic_light_machine.TrafficLightMachine\n" + " :target:\n", + encoding="utf-8", + ) + if docname != "index": + (srcdir / "index.rst").write_text( + f"Index\n=====\n\n.. toctree::\n\n {docname}\n", encoding="utf-8" + ) + app = SphinxTestApp( + buildername=buildername, srcdir=srcdir, builddir=tmp_path / "build", freshenv=True + ) + request.addfinalizer(app.cleanup) + app.build() + assert app.statuscode == 0 + assert app.warning.getvalue() == "" + page = app.builder.get_outfilename(docname) + with open(page, encoding="utf-8") as f: + match = re.search(r'href="([^"]*statemachine-[^"]+\.svg)"', f.read()) + assert match is not None + href = match.group(1) + filename = href.rsplit("/", 1)[-1] + prefix = "https://example.com/project/en/stable/" + page_url = urljoin(prefix, app.builder.get_target_uri(docname)) + assert urljoin(page_url, href) == f"{prefix}_images/{filename}" + assert (app.outdir / "_images" / filename).is_file() + + class TestDirectiveRun: """Integration tests for StateMachineDiagram.run().""" @@ -1199,6 +1244,8 @@ def _make_directive(self, tmp_path, options=None): directive.state_machine = mock.MagicMock() directive.state = mock.MagicMock() directive.state.document.settings.env.app.outdir = str(tmp_path) + directive.env.docname = "index" + directive.env.app.builder.get_target_uri.return_value = "index.html" directive.content_offset = 0 return directive @@ -1273,7 +1320,7 @@ def test_render_with_empty_target(self, tmp_path): """Empty target auto-generates a zoom SVG file.""" _, result = self._run(tmp_path, options={"target": ""}) - assert 'href="/_images/statemachine-' in result[0].astext() + assert 'href="_images/statemachine-' in result[0].astext() images_dir = tmp_path / "_images" assert any(images_dir.glob("statemachine-*.svg"))