diff --git a/CHANGELOGS.rst b/CHANGELOGS.rst index f231c6b..08cc2f9 100644 --- a/CHANGELOGS.rst +++ b/CHANGELOGS.rst @@ -1,6 +1,11 @@ Change Logs =========== +0.4.2 ++++++ + +* :pr:`47`: use svg by default with gdot + 0.4.1 +++++ diff --git a/_doc/conf.py b/_doc/conf.py index d64cbb1..22c4aec 100644 --- a/_doc/conf.py +++ b/_doc/conf.py @@ -52,6 +52,8 @@ exclude_patterns = [] pygments_style = "sphinx" todo_include_todos = True +graphviz_output_format = "svg" +graphviz_dot_args = ["-Gbgcolor=transparent"] html_theme = "furo" html_theme_path = ["_static"] diff --git a/_doc/index.rst b/_doc/index.rst index 003082c..7724d9c 100644 --- a/_doc/index.rst +++ b/_doc/index.rst @@ -54,6 +54,7 @@ Sources available on Older versions ++++++++++++++ +* `0.4.2 <../v0.4.2/index.html>`_ * `0.4.1 <../v0.4.1/index.html>`_ * `0.4.0 <../v0.4.0/index.html>`_ * `0.3.0 <../v0.3.0/index.html>`_ diff --git a/_unittests/ut_gdot/test_gdot_extension.py b/_unittests/ut_gdot/test_gdot_extension.py index 39bee87..d1ee7a3 100644 --- a/_unittests/ut_gdot/test_gdot_extension.py +++ b/_unittests/ut_gdot/test_gdot_extension.py @@ -2,7 +2,12 @@ import logging import sys from sphinx_runpython.process_rst import rst2html -from sphinx_runpython.ext_test_case import ExtTestCase, ignore_warnings +from sphinx_runpython.ext_test_case import ( + ExtTestCase, + ignore_warnings, + skipif_ci_apple, + skipif_ci_windows, +) class TestGDotExtension(ExtTestCase): @@ -16,6 +21,7 @@ def test_gdot1(self): before .. gdot:: + :format: png digraph foo { "bar" -> "baz"; @@ -38,6 +44,7 @@ def test_gdot2(self): .. gdot:: :script: + :format: png print('''digraph foo { HbarH -> HbazH; }'''.replace("H", '"')) @@ -56,6 +63,7 @@ def test_gdot2_split(self): .. gdot:: :script: BEGIN + :format: png print('''...BEGINdigraph foo { HbarH -> HbazH; }'''.replace("H", '"')) @@ -65,10 +73,13 @@ def test_gdot2_split(self): content = rst2html( content, writer_name="rst", new_extensions=["sphinx_runpython.gdot"] ) - self.assertIn('digraph foo { "bar" -> "baz"; }', content) + self.assertNotIn("svg", content) self.assertNotIn("BEGIN", content) + self.assertIn("png", content) @ignore_warnings(PendingDeprecationWarning) + @skipif_ci_windows("crash") + @skipif_ci_apple("crash") def test_gdot3_svg(self): content = """ before @@ -86,8 +97,8 @@ def test_gdot3_svg(self): content = rst2html( content, writer_name="html", new_extensions=["sphinx_runpython.gdot"] ) - self.assertIn("document.getElementById('gdot-", content) - self.assertIn('foo {\\n \\"bar\\" -> \\"baz\\";\\n}");', content) + self.assertIn("svg", content) + self.assertNotIn("png", content) @ignore_warnings(PendingDeprecationWarning) def test_gdot3_svg_process(self): @@ -108,8 +119,7 @@ def test_gdot3_svg_process(self): content = rst2html( content, writer_name="html", new_extensions=["sphinx_runpython.gdot"] ) - self.assertIn("document.getElementById('gdot-", content) - self.assertIn('foo {\\n \\"bar\\" -> \\"baz\\";\\n}");', content) + self.assertIn("digraph foo {", content) @unittest.skipIf(sys.platform != "linux", reason="Missing dependency.") @ignore_warnings(PendingDeprecationWarning) diff --git a/sphinx_runpython/__init__.py b/sphinx_runpython/__init__.py index 12a3dfd..104ec78 100644 --- a/sphinx_runpython/__init__.py +++ b/sphinx_runpython/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.4.1" +__version__ = "0.4.2" __author__ = "Xavier Dupré" __github__ = "https://github.com/sdpython/sphinx-runpython" __url__ = "https://sdpython.github.io/doc/sphinx-runpython/dev/" diff --git a/sphinx_runpython/ext_test_case.py b/sphinx_runpython/ext_test_case.py index 762068c..3ad3bd3 100644 --- a/sphinx_runpython/ext_test_case.py +++ b/sphinx_runpython/ext_test_case.py @@ -108,6 +108,42 @@ def __exit__(self, exc_type, exc_value, traceback): sys.path = self.store +def is_windows() -> bool: + return sys.platform == "win32" + + +def is_apple() -> bool: + return sys.platform == "darwin" + + +def is_linux() -> bool: + return sys.platform == "linux" + + +def skipif_ci_windows(msg) -> Callable: + """Skips a unit test if it runs on :epkg:`azure pipeline` on :epkg:`Windows`.""" + if is_windows(): + msg = f"Test does not work on azure pipeline (Windows). {msg}" + return unittest.skip(msg) + return lambda x: x + + +def skipif_ci_linux(msg) -> Callable: + """Skips a unit test if it runs on :epkg:`azure pipeline` on :epkg:`Linux`.""" + if is_linux(): + msg = f"Takes too long (Linux). {msg}" + return unittest.skip(msg) + return lambda x: x + + +def skipif_ci_apple(msg) -> Callable: + """Skips a unit test if it runs on :epkg:`azure pipeline` on :epkg:`Windows`.""" + if is_apple(): + msg = f"Test does not work on azure pipeline (Apple). {msg}" + return unittest.skip(msg) + return lambda x: x + + class ExtTestCase(unittest.TestCase): _warns = [] diff --git a/sphinx_runpython/gdot/sphinx_gdot_extension.py b/sphinx_runpython/gdot/sphinx_gdot_extension.py index b9b7083..05b7eca 100644 --- a/sphinx_runpython/gdot/sphinx_gdot_extension.py +++ b/sphinx_runpython/gdot/sphinx_gdot_extension.py @@ -2,8 +2,16 @@ import logging from docutils import nodes from docutils.parsers.rst import directives, Directive +from typing import Any import sphinx -from sphinx.ext.graphviz import latex_visit_graphviz, text_visit_graphviz +from sphinx.ext.graphviz import ( + latex_visit_graphviz, + text_visit_graphviz, + render_dot, + GraphvizError, + ClickableMapDefinition, + __, +) from ..ext_helper import get_env_state_info from ..ext_io_helper import download_requirejs, get_url_content_timeout from ..runpython.sphinx_runpython_extension import run_python_script @@ -104,14 +112,12 @@ class GDotDirective(Directive): _default_url = "https://cdnjs.cloudflare.com/ajax/libs/viz.js/1.8.0/viz-lite.js" def run(self): - """ - Builds the collapse text. - """ + """Builds the collapse text.""" # retrieves the parameters if "format" in self.options: # noqa: SIM401 format = self.options["format"] else: - format = "png" + format = "svg" url = self.options.get("url", "local") bool_set_ = (True, 1, "True", "1", "true", "") process = "process" in self.options and self.options["process"] in bool_set_ @@ -182,15 +188,17 @@ def run(self): content = spl[-1] node = gdot_node( - format=format, code=content, url=url, options={"docname": docname} + format=format, + code=content, + url=url, + options={"docname": docname}, + use_sphinx_graphviz=True, ) return [node] def visit_gdot_node_rst(self, node): - """ - visit collapse_node - """ + """visit collapse_node""" self.new_state(0) self.add_text(".. gdot::" + self.nl) if node["format"] != "?": @@ -203,17 +211,81 @@ def visit_gdot_node_rst(self, node): def depart_gdot_node_rst(self, node): - """ - depart collapse_node - """ + """depart collapse_node""" self.end_state() self.end_state(wrap=False) +def render_dot_html( + self, + node: gdot_node, + code: str, + options: dict[str, Any], + prefix: str = "gdot", + imgcls: str | None = None, + alt: str | None = None, + filename: str | None = None, + format: str = "svg", +) -> tuple[str, str]: + if format not in {"png", "svg"}: + logger = logging.getLogger(__name__) + logger.warning(__("format must be either 'png' or 'svg', but is %r"), format) + try: + fname, outfn = render_dot(self, code, options, format, prefix, filename) + except GraphvizError as exc: + logger.warning(__("dot code %r: %s"), code, exc) + raise nodes.SkipNode from exc + + classes = [imgcls, "graphviz", *node.get("classes", [])] + imgcls = " ".join(filter(None, classes)) + + if fname is None: + self.body.append(self.encode(code)) + else: + src = fname.as_posix() + if alt is None: + alt = node.get("alt", self.encode(code).strip()) + if "align" in node: + align = node["align"] + self.body.append(f'