From 9f571ce2ac44f41ae1764fa5fdfe0b999f55b099 Mon Sep 17 00:00:00 2001 From: Tim Paine <3105306+timkpaine@users.noreply.github.com> Date: Thu, 12 Sep 2024 11:18:50 -0400 Subject: [PATCH 1/2] Add linting --- .github/workflows/test.yml | 6 +++++- pyproject.toml | 20 ++++++++++++++++++++ setup.py | 6 +++--- 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 pyproject.toml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e706fee..62a50bc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,9 +26,13 @@ jobs: - name: Install dependencies run: | - python -m pip install -U pytest python -m pip install -e .[test] + - name: Lint + run: | + ruff format --check sphinxcontrib + ruff check sphinxcontrib + - name: Test run: | pytest diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..cc7eecb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,20 @@ +[tool.ruff] +line-length = 150 + +[tool.ruff.lint] +extend-select = ["I"] + +[tool.ruff.lint.isort] +combine-as-imports = true +default-section = "third-party" +known-first-party = ["sphinxcontrib.mermaid"] +section-order = [ + "future", + "standard-library", + "third-party", + "first-party", + "local-folder", +] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401", "F403"] diff --git a/setup.py b/setup.py index 64fbee1..21e1ce7 100644 --- a/setup.py +++ b/setup.py @@ -58,9 +58,9 @@ def remove_block(text, token, margin=0): "Topic :: Utilities", ], platforms="any", - packages=find_namespace_packages(where='./', include=['sphinxcontrib.mermaid']), - package_dir={'': './'}, + packages=find_namespace_packages(where="./", include=["sphinxcontrib.mermaid"]), + package_dir={"": "./"}, include_package_data=True, install_requires=["sphinx", "pyyaml"], - extras_require={'test': ['myst-parser', 'defusedxml', 'sphinx', 'pytest']}, + extras_require={"test": ["defusedxml", "myst-parser", "pytest", "ruff", "sphinx"]}, ) From c12a83d59ed5cbfb5a56f43b581cc9528f604703 Mon Sep 17 00:00:00 2001 From: Tim Paine <3105306+timkpaine@users.noreply.github.com> Date: Thu, 12 Sep 2024 11:27:04 -0400 Subject: [PATCH 2/2] Fix lint --- sphinxcontrib/mermaid/__init__.py | 137 +++++++++---------------- sphinxcontrib/mermaid/autoclassdiag.py | 9 +- 2 files changed, 50 insertions(+), 96 deletions(-) diff --git a/sphinxcontrib/mermaid/__init__.py b/sphinxcontrib/mermaid/__init__.py index 62dde4b..39e2841 100644 --- a/sphinxcontrib/mermaid/__init__.py +++ b/sphinxcontrib/mermaid/__init__.py @@ -1,13 +1,14 @@ """ - sphinx-mermaid - ~~~~~~~~~~~~~~~ +sphinx-mermaid +~~~~~~~~~~~~~~~ - Allow mermaid diagrams to be included in Sphinx-generated - documents inline. +Allow mermaid diagrams to be included in Sphinx-generated +documents inline. - :copyright: Copyright 2016-2023 by Martín Gaitán and others - :license: BSD, see LICENSE for details. +:copyright: Copyright 2016-2023 by Martín Gaitán and others +:license: BSD, see LICENSE for details. """ + from __future__ import annotations import codecs @@ -80,6 +81,7 @@ window.addEventListener("load", load); """ + class mermaid(nodes.General, nodes.Inline, nodes.Element): pass @@ -90,9 +92,7 @@ def figure_wrapper(directive, node, caption): figure_node["align"] = node.attributes.pop("align") parsed = nodes.Element() - directive.state.nested_parse( - ViewList([caption], source=""), directive.content_offset, parsed - ) + directive.state.nested_parse(ViewList([caption], source=""), directive.content_offset, parsed) caption_node = nodes.caption(parsed[0].rawsource, "", *parsed[0].children) caption_node.source = parsed[0].source caption_node.line = parsed[0].line @@ -132,8 +132,7 @@ def get_mm_code(self): if self.content: return [ document.reporter.warning( - "Mermaid directive cannot have both content and " - "a filename argument", + "Mermaid directive cannot have both content and " "a filename argument", line=self.lineno, ) ] @@ -147,8 +146,7 @@ def get_mm_code(self): except OSError: return [ document.reporter.warning( - "External Mermaid file %r not found or reading " - "it failed" % filename, + "External Mermaid file %r not found or reading " "it failed" % filename, line=self.lineno, ) ] @@ -192,7 +190,7 @@ def run(self, **kwargs): mm_config = "---" if "config" in self.options: mm_config += "\n" - mm_config += dump({"config": loads(self.options['config'])}) + mm_config += dump({"config": loads(self.options["config"])}) if "title" in self.options: mm_config += "\n" mm_config += f"title: {self.options['title']}" @@ -209,7 +207,6 @@ def run(self, **kwargs): class MermaidClassDiagram(Mermaid): - has_content = False required_arguments = 1 optional_arguments = 100 @@ -239,9 +236,7 @@ def render_mm(self, code, options, _fmt, prefix="mermaid"): mermaid_cmd = self.builder.config.mermaid_cmd mermaid_cmd_shell = self.builder.config.mermaid_cmd_shell in {True, "True", "true"} - hashkey = ( - code + str(options) + str(self.builder.config.mermaid_sequence_config) - ).encode("utf-8") + hashkey = (code + str(options) + str(self.builder.config.mermaid_sequence_config)).encode("utf-8") basename = f"{prefix}-{sha1(hashkey).hexdigest()}" fname = f"{basename}.{_fmt}" @@ -249,7 +244,7 @@ def render_mm(self, code, options, _fmt, prefix="mermaid"): outdir = os.path.join(self.builder.outdir, self.builder.imagedir) outfn = os.path.join(outdir, fname) with TemporaryDirectory() as tempDir: - tmpfn = os.path.join(tempDir, basename) + tmpfn = os.path.join(tempDir, basename) if os.path.isfile(outfn): return relfn, outfn @@ -265,19 +260,14 @@ def render_mm(self, code, options, _fmt, prefix="mermaid"): mm_args = list(mermaid_cmd) mm_args.extend(self.builder.config.mermaid_params) - mm_args += ['-i', tmpfn, '-o', outfn] + mm_args += ["-i", tmpfn, "-o", outfn] if self.builder.config.mermaid_sequence_config: mm_args.extend(["--configFile", self.builder.config.mermaid_sequence_config]) try: - p = Popen( - mm_args, shell=mermaid_cmd_shell, stdout=PIPE, stdin=PIPE, stderr=PIPE - ) + p = Popen(mm_args, shell=mermaid_cmd_shell, stdout=PIPE, stdin=PIPE, stderr=PIPE) except FileNotFoundError: - logger.warning( - "command %r cannot be run (needed for mermaid " - "output), check the mermaid_cmd setting" % mermaid_cmd - ) + logger.warning("command %r cannot be run (needed for mermaid " "output), check the mermaid_cmd setting" % mermaid_cmd) return None, None stdout, stderr = p.communicate(str.encode(code)) @@ -285,62 +275,42 @@ def render_mm(self, code, options, _fmt, prefix="mermaid"): logger.info(stdout) if p.returncode != 0: - raise MermaidError( - "Mermaid exited with error:\n[stderr]\n%s\n" - "[stdout]\n%s" % (stderr, stdout) - ) + raise MermaidError("Mermaid exited with error:\n[stderr]\n%s\n" "[stdout]\n%s" % (stderr, stdout)) if not os.path.isfile(outfn): - raise MermaidError( - "Mermaid did not produce an output file:\n[stderr]\n%s\n" - "[stdout]\n%s" % (stderr, stdout) - ) + raise MermaidError("Mermaid did not produce an output file:\n[stderr]\n%s\n" "[stdout]\n%s" % (stderr, stdout)) return relfn, outfn -def _render_mm_html_raw( - self, node, code, options, prefix="mermaid", imgcls=None, alt=None -): +def _render_mm_html_raw(self, node, code, options, prefix="mermaid", imgcls=None, alt=None): classes = ["mermaid"] attrs = {} - + if "align" in node: classes.append(f"align-{node['align']}") attrs["align"] = node["align"] if "zoom_id" in node: attrs["data-zoom-id"] = node["zoom_id"] - + if "ids" in node and len(node["ids"]) == 1: attrs["id"] = node["ids"][0] - + tag_template = """
         {code}
     
""" - attr_defs = ["{}=\"{}\"".format(k, v) for k, v in attrs.items()] - self.body.append( - tag_template.format( - attr_defs=" ".join(attr_defs), - classes=" ".join(classes), - code=self.encode(code) - ) - ) + attr_defs = ['{}="{}"'.format(k, v) for k, v in attrs.items()] + self.body.append(tag_template.format(attr_defs=" ".join(attr_defs), classes=" ".join(classes), code=self.encode(code))) raise nodes.SkipNode def render_mm_html(self, node, code, options, prefix="mermaid", imgcls=None, alt=None): - _fmt = self.builder.config.mermaid_output_format if _fmt == "raw": - return _render_mm_html_raw( - self, node, code, options, prefix="mermaid", imgcls=None, alt=None - ) + return _render_mm_html_raw(self, node, code, options, prefix="mermaid", imgcls=None, alt=None) try: if _fmt not in ("png", "svg"): - raise MermaidError( - "mermaid_output_format must be one of 'raw', 'png', " - "'svg', but is %r" % _fmt - ) + raise MermaidError("mermaid_output_format must be one of 'raw', 'png', " "'svg', but is %r" % _fmt) fname, outfn = render_mm(self, code, options, _fmt, prefix) except MermaidError as exc: @@ -360,9 +330,7 @@ def render_mm_html(self, node, code, options, prefix="mermaid", imgcls=None, alt self.body.append(svgtag) else: if "align" in node: - self.body.append( - '
' % (node["align"], node["align"])
-                )
+                self.body.append('
' % (node["align"], node["align"]))
 
             self.body.append(f'{alt}\n')
             if "align" in node:
@@ -387,11 +355,9 @@ def render_mm_latex(self, node, code, options, prefix="mermaid"):
         try:
             p = Popen(mm_args, stdout=PIPE, stdin=PIPE, stderr=PIPE)
         except OSError as err:
-            if err.errno != errno.ENOENT:   # No such file or directory
+            if err.errno != errno.ENOENT:  # No such file or directory
                 raise
-            logger.warning(
-                f"command {self.builder.config.mermaid_pdfcrop!r} cannot be run (needed to crop pdf), check the mermaid_cmd setting"
-            )
+            logger.warning(f"command {self.builder.config.mermaid_pdfcrop!r} cannot be run (needed to crop pdf), check the mermaid_cmd setting")
             return None, None
 
         stdout, stderr = p.communicate()
@@ -399,19 +365,11 @@ def render_mm_latex(self, node, code, options, prefix="mermaid"):
             logger.info(stdout)
 
         if p.returncode != 0:
-            raise MermaidError(
-                "PdfCrop exited with error:\n[stderr]\n%s\n"
-                "[stdout]\n%s" % (stderr, stdout)
-            )
+            raise MermaidError("PdfCrop exited with error:\n[stderr]\n%s\n" "[stdout]\n%s" % (stderr, stdout))
         if not os.path.isfile(outfn):
-            raise MermaidError(
-                "PdfCrop did not produce an output file:\n[stderr]\n%s\n"
-                "[stdout]\n%s" % (stderr, stdout)
-            )
+            raise MermaidError("PdfCrop did not produce an output file:\n[stderr]\n%s\n" "[stdout]\n%s" % (stderr, stdout))
 
-        fname = "{filename[0]}-crop{filename[1]}".format(
-            filename=os.path.splitext(fname)
-        )
+        fname = "{filename[0]}-crop{filename[1]}".format(filename=os.path.splitext(fname))
 
     is_inline = self.is_inline(node)
     if is_inline:
@@ -428,9 +386,7 @@ def render_mm_latex(self, node, code, options, prefix="mermaid"):
             elif node["align"] == "right":
                 self.body.append("{\\hspace*{\\fill}")
                 post = "}"
-        self.body.append(
-            "%s\\sphinxincludegraphics{%s}%s" % (para_separator, fname, para_separator)
-        )
+        self.body.append("%s\\sphinxincludegraphics{%s}%s" % (para_separator, fname, para_separator))
         if post:
             self.body.append(post)
 
@@ -492,7 +448,7 @@ def install_js(
         _mermaid_js_url = f"https://cdn.jsdelivr.net/npm/mermaid@{app.config.mermaid_version}/dist/mermaid.esm.min.mjs"
     elif app.config.mermaid_version:
         raise MermaidError("Requires mermaid js version 10.3.0 or later")
-    
+
     app.add_js_file(_mermaid_js_url, priority=app.config.mermaid_js_priority, type="module")
 
     if app.config.mermaid_elk_use_local:
@@ -500,7 +456,9 @@ def install_js(
     elif app.config.mermaid_include_elk == "latest":
         _mermaid_elk_js_url = "https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk/dist/mermaid-layout-elk.esm.min.mjs"
     elif app.config.mermaid_include_elk:
-        _mermaid_elk_js_url = f"https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@{app.config.mermaid_include_elk}/dist/mermaid-layout-elk.esm.min.mjs"
+        _mermaid_elk_js_url = (
+            f"https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@{app.config.mermaid_include_elk}/dist/mermaid-layout-elk.esm.min.mjs"
+        )
     else:
         _mermaid_elk_js_url = None
     if _mermaid_elk_js_url:
@@ -510,17 +468,15 @@ def install_js(
         # Update if esm is used and no custom init-js is provided
         if _mermaid_elk_js_url:
             # Add registration of ELK layouts
-            app.config.mermaid_init_js = f'import mermaid from "{_mermaid_js_url}";import elkLayouts from "{_mermaid_elk_js_url}";mermaid.registerLayoutLoaders(elkLayouts);{app.config.mermaid_init_js}';
+            app.config.mermaid_init_js = f'import mermaid from "{_mermaid_js_url}";import elkLayouts from "{_mermaid_elk_js_url}";mermaid.registerLayoutLoaders(elkLayouts);{app.config.mermaid_init_js}'
         else:
-            app.config.mermaid_init_js = f'import mermaid from "{_mermaid_js_url}";{app.config.mermaid_init_js}';
+            app.config.mermaid_init_js = f'import mermaid from "{_mermaid_js_url}";{app.config.mermaid_init_js}'
 
     if app.config.mermaid_init_js:
         # If mermaid is local the init-call must be placed after `html_js_files` which has a priority of 800.
-        priority = (
-            app.config.mermaid_init_js_priority if _mermaid_js_url is not None else 801
-        )
+        priority = app.config.mermaid_init_js_priority if _mermaid_js_url is not None else 801
         app.add_js_file(None, body=app.config.mermaid_init_js, priority=priority, type="module")
-            
+
     _wrote_mermaid_run = False
     if app.config.mermaid_output_format == "raw":
         if app.config.d3_use_local:
@@ -553,7 +509,10 @@ def install_js(
                 _wrote_mermaid_run = True
 
     if not _wrote_mermaid_run and _mermaid_js_url:
-        app.add_js_file(None, body=_MERMAID_RUN_NO_D3_ZOOM.format(mermaid_js_url=_mermaid_js_url), priority=app.config.mermaid_js_priority, type="module")
+        app.add_js_file(
+            None, body=_MERMAID_RUN_NO_D3_ZOOM.format(mermaid_js_url=_mermaid_js_url), priority=app.config.mermaid_js_priority, type="module"
+        )
+
 
 def setup(app):
     app.add_node(
@@ -574,7 +533,7 @@ def setup(app):
     app.add_config_value("mermaid_params", list(), "html")
     app.add_config_value("mermaid_verbose", False, "html")
     app.add_config_value("mermaid_sequence_config", False, "html")
-    
+
     app.add_config_value("mermaid_use_local", "", "html")
     app.add_config_value("mermaid_version", "11.2.0", "html")
     app.add_config_value("mermaid_elk_use_local", "", "html")
@@ -587,4 +546,4 @@ def setup(app):
     app.add_config_value("mermaid_d3_zoom", False, "html")
     app.connect("html-page-context", install_js)
 
-    return {"version": sphinx.__display_version__, "parallel_read_safe": True}
\ No newline at end of file
+    return {"version": sphinx.__display_version__, "parallel_read_safe": True}
diff --git a/sphinxcontrib/mermaid/autoclassdiag.py b/sphinxcontrib/mermaid/autoclassdiag.py
index f1bf836..5f4f99d 100644
--- a/sphinxcontrib/mermaid/autoclassdiag.py
+++ b/sphinxcontrib/mermaid/autoclassdiag.py
@@ -23,9 +23,7 @@ def get_classes(*cls_or_modules, strict=False):
 
         elif inspect.ismodule(obj):
             for obj_ in obj.__dict__.values():
-                if inspect.isclass(obj_) and (
-                    not strict or obj_.__module__.startswith(obj.__name__)
-                ):
+                if inspect.isclass(obj_) and (not strict or obj_.__module__.startswith(obj.__name__)):
                     yield obj_
         else:
             raise MermaidError(f"{cls_or_module} is not a class nor a module")
@@ -36,7 +34,6 @@ def class_diagram(*cls_or_modules, full=False, strict=False, namespace=None):
 
     def get_tree(cls):
         for base in cls.__bases__:
-
             if base.__name__ == "object":
                 continue
             if namespace and not base.__module__.startswith(namespace):
@@ -51,9 +48,7 @@ def get_tree(cls):
     if not inheritances:
         return ""
 
-    return "classDiagram\n" + "\n".join(
-        f"  {a} <|-- {b}" for a, b in sorted(inheritances)
-    )
+    return "classDiagram\n" + "\n".join(f"  {a} <|-- {b}" for a, b in sorted(inheritances))
 
 
 if __name__ == "__main__":