From 8c350993d2a2a90720e26a7ec8fb466d9664236e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= Date: Sun, 20 Jul 2025 23:11:59 +0200 Subject: [PATCH 01/10] Inline (#41) * try 3.13 on CI * changelogs --- CHANGELOGS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOGS.rst b/CHANGELOGS.rst index 6a8c824..3362b26 100644 --- a/CHANGELOGS.rst +++ b/CHANGELOGS.rst @@ -4,6 +4,7 @@ Change Logs 0.4.0 +++++ +* :pr:`40`: error message inline * :pr:`39`: add option ``:debug:`` to runpython to append rst code to a page 0.3.0 From d3bad5a6a4f3a416e8dc5bda5361a2f0b415eeb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= Date: Tue, 29 Jul 2025 10:43:43 +0200 Subject: [PATCH 02/10] Excludes function from pybind11 in docassert (#42) * exclude function from pybind11 in docassert * upgrade version " --- CHANGELOGS.rst | 5 +++++ _doc/index.rst | 3 +-- sphinx_runpython/__init__.py | 2 +- sphinx_runpython/docassert/sphinx_docassert_extension.py | 5 +++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOGS.rst b/CHANGELOGS.rst index 3362b26..090165f 100644 --- a/CHANGELOGS.rst +++ b/CHANGELOGS.rst @@ -1,6 +1,11 @@ Change Logs =========== +0.4.1 ++++++ + +* :pr:`42`: excludes failing functions from pybind11 in docassert + 0.4.0 +++++ diff --git a/_doc/index.rst b/_doc/index.rst index b3b0ad9..003082c 100644 --- a/_doc/index.rst +++ b/_doc/index.rst @@ -54,7 +54,6 @@ Sources available on Older versions ++++++++++++++ +* `0.4.1 <../v0.4.1/index.html>`_ * `0.4.0 <../v0.4.0/index.html>`_ * `0.3.0 <../v0.3.0/index.html>`_ -* `0.2.0 <../v0.2.0/index.html>`_ -* `0.1.0 <../v0.1.0/index.html>`_ diff --git a/sphinx_runpython/__init__.py b/sphinx_runpython/__init__.py index 5280383..12a3dfd 100644 --- a/sphinx_runpython/__init__.py +++ b/sphinx_runpython/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.4.0" +__version__ = "0.4.1" __author__ = "Xavier Dupré" __github__ = "https://github.com/sdpython/sphinx-runpython" __url__ = "https://sdpython.github.io/doc/sphinx-runpython/dev/" diff --git a/sphinx_runpython/docassert/sphinx_docassert_extension.py b/sphinx_runpython/docassert/sphinx_docassert_extension.py index 7a9fe62..0b81024 100644 --- a/sphinx_runpython/docassert/sphinx_docassert_extension.py +++ b/sphinx_runpython/docassert/sphinx_docassert_extension.py @@ -319,6 +319,11 @@ def override_transform(self, other_self, node): # Import object, get the list of parameters docs = fieldbody.parent.source.split("docstring of")[-1].strip() docs = docs.replace(".PyCapsule.", ".") + if ( + "pybind11_detail_function_record_v1_system_libstdcpp_gxx_abi_1xxx_use_cxx11_abi_1" + in docs + ): + continue myfunc = None funckind = None From fb4052e48f5329fcc572002e204c1ee12ad154d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= Date: Thu, 26 Feb 2026 18:22:46 +0100 Subject: [PATCH 03/10] style (#43) --- .../ut__main/test_documentation_examples.py | 2 +- .../ut_blocdefs/test_blocref_extension.py | 16 ++----- .../ut_blocdefs/test_exref_extension.py | 12 ++--- .../ut_blocdefs/test_faqref_extension.py | 12 ++--- .../ut_blocdefs/test_mathdef_extension.py | 16 ++----- .../ut_collapse/test_collapse_extension.py | 12 ++--- _unittests/ut_docassert/test_docassert.py | 12 ++--- _unittests/ut_epkg/test_epkg_extension.py | 29 +++--------- _unittests/ut_gdot/test_gdot_extension.py | 24 +++------- _unittests/ut_quote/test_quote_extension.py | 32 ++++---------- .../ut_runpython/test_run_cmd_timeout.py | 2 +- .../ut_runpython/test_runpython_code_block.py | 8 +--- .../ut_runpython/test_runpython_context.py | 4 +- .../ut_runpython/test_runpython_extension.py | 44 +++++-------------- .../test_runpython_extension_image.py | 4 +- .../test_runpython_extension_toggle.py | 4 +- .../test_runpython_store_in_file.py | 4 +- pyproject.toml | 3 +- .../collapse/sphinx_collapse_extension.py | 4 +- sphinx_runpython/ext_io_helper.py | 3 +- .../gdot/sphinx_gdot_extension.py | 8 +--- sphinx_runpython/runpython/run_cmd.py | 4 +- sphinx_runpython/tools/sphinx_api.py | 38 +++++----------- 23 files changed, 82 insertions(+), 215 deletions(-) diff --git a/_unittests/ut__main/test_documentation_examples.py b/_unittests/ut__main/test_documentation_examples.py index 1791f08..e273fee 100644 --- a/_unittests/ut__main/test_documentation_examples.py +++ b/_unittests/ut__main/test_documentation_examples.py @@ -40,7 +40,7 @@ def run_test(self, fold: str, name: str, verbose=0) -> int: cmds = [sys.executable, "-u", os.path.join(fold, name)] p = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE) res = p.communicate() - out, err = res + _out, err = res st = err.decode("ascii", errors="ignore") if len(st) > 0 and "Traceback" in st: if '"dot" not found in path.' in st: diff --git a/_unittests/ut_blocdefs/test_blocref_extension.py b/_unittests/ut_blocdefs/test_blocref_extension.py index 9ebbd7e..e98dd82 100644 --- a/_unittests/ut_blocdefs/test_blocref_extension.py +++ b/_unittests/ut_blocdefs/test_blocref_extension.py @@ -20,9 +20,7 @@ def test_blocref_rst(self): this code should appear___ after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -61,9 +59,7 @@ def test_blocref_html(self): this code should appear___ after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -102,9 +98,7 @@ def test_blocref2(self): this code should appear___ after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -147,9 +141,7 @@ def test_blocreflist(self): :contents: 1 after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="html") diff --git a/_unittests/ut_blocdefs/test_exref_extension.py b/_unittests/ut_blocdefs/test_exref_extension.py index 049b2a7..e724351 100644 --- a/_unittests/ut_blocdefs/test_exref_extension.py +++ b/_unittests/ut_blocdefs/test_exref_extension.py @@ -20,9 +20,7 @@ def test_exref(self): this code should appear___ after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -64,9 +62,7 @@ def test_exreflist(self): :sort: title after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="rst") @@ -105,9 +101,7 @@ def test_exreflist_rst(self): :sort: title after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') rst = rst2html(content, writer_name="rst") diff --git a/_unittests/ut_blocdefs/test_faqref_extension.py b/_unittests/ut_blocdefs/test_faqref_extension.py index eb247a1..3455c74 100644 --- a/_unittests/ut_blocdefs/test_faqref_extension.py +++ b/_unittests/ut_blocdefs/test_faqref_extension.py @@ -20,9 +20,7 @@ def test_faqref(self): this code should appear___ after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -64,9 +62,7 @@ def test_faqreflist(self): :sort: title after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="rst") @@ -105,9 +101,7 @@ def test_faqreflist_rst(self): :sort: title after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') rst = rst2html(content, writer_name="rst") diff --git a/_unittests/ut_blocdefs/test_mathdef_extension.py b/_unittests/ut_blocdefs/test_mathdef_extension.py index bcfc085..24cee62 100644 --- a/_unittests/ut_blocdefs/test_mathdef_extension.py +++ b/_unittests/ut_blocdefs/test_mathdef_extension.py @@ -20,9 +20,7 @@ def test_mathdef(self): this code should appear___ after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -62,9 +60,7 @@ def test_mathdeflist(self): :tag: definition after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="rst") @@ -102,9 +98,7 @@ def test_mathdeflist_contents(self): :contents: after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="rst") @@ -148,9 +142,7 @@ def test_mathdeflist_contents_body_sphinx(self): :contents: after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( diff --git a/_unittests/ut_collapse/test_collapse_extension.py b/_unittests/ut_collapse/test_collapse_extension.py index b7cb68a..2828af9 100644 --- a/_unittests/ut_collapse/test_collapse_extension.py +++ b/_unittests/ut_collapse/test_collapse_extension.py @@ -42,9 +42,7 @@ def test_collapse(self): this code should appear___ after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') # RST @@ -101,9 +99,7 @@ def test_collapse_legend(self): this code should appear___ after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') # RST @@ -136,9 +132,7 @@ def test_collapse_show(self): this code should appear___ after - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') # RST diff --git a/_unittests/ut_docassert/test_docassert.py b/_unittests/ut_docassert/test_docassert.py index e4af524..77fd152 100644 --- a/_unittests/ut_docassert/test_docassert.py +++ b/_unittests/ut_docassert/test_docassert.py @@ -13,7 +13,7 @@ def test_import_object(self): this = os.path.abspath(os.path.dirname(__file__)) data = os.path.join(this, "datadoc") with sys_path_append(data): - obj, name = import_object("exdocassert.onefunction", "function") + obj, _name = import_object("exdocassert.onefunction", "function") self.assertTrue(obj is not None) self.assertTrue(obj(4, 5), 9) @@ -45,7 +45,7 @@ def __len__(self): this = os.path.abspath(os.path.dirname(__file__)) data = os.path.join(this, "datadoc") with sys_path_append(data): - obj, name = import_object("exdocassert2.onefunction", "function") + _obj, _name = import_object("exdocassert2.onefunction", "function") newstring = ".. autofunction:: exdocassert2.onefunction" html, warn = rst2html(newstring, return_warnings=True) self.assertTrue(html is not None) @@ -88,7 +88,7 @@ def __len__(self): this = os.path.abspath(os.path.dirname(__file__)) data = os.path.join(this, "datadoc") with sys_path_append(data): - obj, name = import_object("exsig.clex.onemethod", "method") + _obj, _name = import_object("exsig.clex.onemethod", "method") newstring = ".. automethod:: exsig.clex.onemethod" html, warn = rst2html(newstring, return_warnings=True) self.assertTrue(html is not None) @@ -133,7 +133,7 @@ def __len__(self): this = os.path.abspath(os.path.dirname(__file__)) data = os.path.join(this, "datadoc") with sys_path_append(data): - obj, name = import_object("clsslk.Estimator", "class") + _obj, _name = import_object("clsslk.Estimator", "class") newstring = ".. autoclass:: clsslk.Estimator" html, warn = rst2html(newstring, return_warnings=True) self.assertTrue(html is not None) @@ -177,7 +177,7 @@ def __len__(self): this = os.path.abspath(os.path.dirname(__file__)) data = os.path.join(this, "datadoc") with sys_path_append(data): - obj, name = import_object("clsslk.Estimator2", "class") + _obj, _name = import_object("clsslk.Estimator2", "class") newstring = ".. autoclass:: clsslk.Estimator2" html, warn = rst2html(newstring, return_warnings=True) self.assertTrue(html is not None) @@ -221,7 +221,7 @@ def __len__(self): this = os.path.abspath(os.path.dirname(__file__)) data = os.path.join(this, "datadoc") with sys_path_append(data): - obj, name = import_object("clsslk.Estimator3", "class") + _obj, _name = import_object("clsslk.Estimator3", "class") newstring = ".. autoclass:: clsslk.Estimator3" html, warn = rst2html( newstring, return_warnings=True, new_extensions=["numpydoc"] diff --git a/_unittests/ut_epkg/test_epkg_extension.py b/_unittests/ut_epkg/test_epkg_extension.py index 31876a5..a741796 100644 --- a/_unittests/ut_epkg/test_epkg_extension.py +++ b/_unittests/ut_epkg/test_epkg_extension.py @@ -8,7 +8,6 @@ depart_epkg_node, ) - tives = [("epkg", epkg_role, epkg_node, visit_epkg_node, depart_epkg_node)] @@ -20,9 +19,7 @@ def test_epkg_module(self): ================ abeforea :epkg:`pandas` aaftera - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="rst") @@ -47,9 +44,7 @@ def test_epkg_module_twice(self): ================ abeforea :epkg:`pandas` aaftera - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="rst") @@ -62,9 +57,7 @@ def test_epkg_sub(self): ================ abeforea :epkg:`pandas:DataFrame.to_html` aaftera - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="html") @@ -90,9 +83,7 @@ def test_epkg_function(self): ================ abeforea :epkg:`pandas:DataFrame:to_html` aaftera - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') def pandas_link(input): @@ -129,9 +120,7 @@ def test_epkg_class(self): abeforea :epkg:`pandas:DataFrame:to_html` aaftera 7za :epkg:`Pandoc` 7zb - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') class pandas_link: @@ -153,9 +142,7 @@ def test_epkg_function_string(self): ================ abeforea :epkg:`pandas:DataFrame:to_html` aaftera - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="html") @@ -188,9 +175,7 @@ def test_epkg_function_long_link(self): `one link on two lines `_. - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="html") diff --git a/_unittests/ut_gdot/test_gdot_extension.py b/_unittests/ut_gdot/test_gdot_extension.py index 28afff4..39bee87 100644 --- a/_unittests/ut_gdot/test_gdot_extension.py +++ b/_unittests/ut_gdot/test_gdot_extension.py @@ -22,9 +22,7 @@ def test_gdot1(self): } after - """.replace( - " ", "" - ) + """.replace(" ", "") content = rst2html( content, writer_name="rst", new_extensions=["sphinx_runpython.gdot"] @@ -44,9 +42,7 @@ def test_gdot2(self): print('''digraph foo { HbarH -> HbazH; }'''.replace("H", '"')) after - """.replace( - " ", "" - ) + """.replace(" ", "") content = rst2html( content, writer_name="rst", new_extensions=["sphinx_runpython.gdot"] @@ -64,9 +60,7 @@ def test_gdot2_split(self): print('''...BEGINdigraph foo { HbarH -> HbazH; }'''.replace("H", '"')) after - """.replace( - " ", "" - ) + """.replace(" ", "") content = rst2html( content, writer_name="rst", new_extensions=["sphinx_runpython.gdot"] @@ -87,9 +81,7 @@ def test_gdot3_svg(self): } after - """.replace( - " ", "" - ) + """.replace(" ", "") content = rst2html( content, writer_name="html", new_extensions=["sphinx_runpython.gdot"] @@ -111,9 +103,7 @@ def test_gdot3_svg_process(self): } after - """.replace( - " ", "" - ) + """.replace(" ", "") content = rst2html( content, writer_name="html", new_extensions=["sphinx_runpython.gdot"] @@ -135,9 +125,7 @@ def test_gdot4_png(self): } after - """.replace( - " ", "" - ) + """.replace(" ", "") try: content = rst2html( diff --git a/_unittests/ut_quote/test_quote_extension.py b/_unittests/ut_quote/test_quote_extension.py index b49db17..d331172 100644 --- a/_unittests/ut_quote/test_quote_extension.py +++ b/_unittests/ut_quote/test_quote_extension.py @@ -16,9 +16,7 @@ def test_quote(self): this code should appear___ next - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -71,9 +69,7 @@ def test_quote_manga(self): this code should appear___ next - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -126,9 +122,7 @@ def test_quote_film(self): this code should appear___ next - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -182,9 +176,7 @@ def test_quote_show(self): this code should appear___ next - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -238,9 +230,7 @@ def test_quote_comic(self): this code should appear___ next - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -294,9 +284,7 @@ def test_quote_disc(self): this code should appear___ next - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -350,9 +338,7 @@ def test_quote_ado(self): this code should appear___ next - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( @@ -406,9 +392,7 @@ def test_quote_child(self): this code should appear___ next - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html( diff --git a/_unittests/ut_runpython/test_run_cmd_timeout.py b/_unittests/ut_runpython/test_run_cmd_timeout.py index 916806e..8a66bc0 100644 --- a/_unittests/ut_runpython/test_run_cmd_timeout.py +++ b/_unittests/ut_runpython/test_run_cmd_timeout.py @@ -7,7 +7,7 @@ class TestRunCmdTimeout(ExtTestCase): def test_run_cmd_timeout(self): if __name__ == "__main__": cmd = "more" - out, err = run_cmd( + _out, err = run_cmd( cmd, wait=True, tell_if_no_output=1, communicate=False, timeout=3 ) self.assertIn("Process killed", err) diff --git a/_unittests/ut_runpython/test_runpython_code_block.py b/_unittests/ut_runpython/test_runpython_code_block.py index cab5078..5260a5e 100644 --- a/_unittests/ut_runpython/test_runpython_code_block.py +++ b/_unittests/ut_runpython/test_runpython_code_block.py @@ -27,9 +27,7 @@ def test_code_block(self): ens = ["f", 0] for j in ens: print(j) - """.replace( - " ", "" - ) + """.replace(" ", "") rst = rst2html(content, writer_name="rst") self.assertIn("csharp", str(rst)) @@ -55,9 +53,7 @@ class runpythonthis_node(nodes.Structural, nodes.Element): { return x.ToInt(); } - """.replace( - " ", "" - ) + """.replace(" ", "") rst = rst2html(content, writer_name="rst") self.assertIn(".. code-block:: csharp", rst) diff --git a/_unittests/ut_runpython/test_runpython_context.py b/_unittests/ut_runpython/test_runpython_context.py index 3331e35..ae61d71 100644 --- a/_unittests/ut_runpython/test_runpython_context.py +++ b/_unittests/ut_runpython/test_runpython_context.py @@ -40,9 +40,7 @@ def depart_rp_node(self, node): print("failed") for k, v in sorted(locals().copy().items()): print(k, "=", [v]) - """.replace( - " ", "" - ) + """.replace(" ", "") rst = rst2html(content, writer_name="rst") self.assertIn("restored saved for later", rst) diff --git a/_unittests/ut_runpython/test_runpython_extension.py b/_unittests/ut_runpython/test_runpython_extension.py index cbaef02..99d9a77 100644 --- a/_unittests/ut_runpython/test_runpython_extension.py +++ b/_unittests/ut_runpython/test_runpython_extension.py @@ -60,9 +60,7 @@ def depart_rp_node(self, node): print(u"setsysvar: " + str( sys.__dict__.get( 'enable_disabled_documented_pieces_of_code', None))) - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="html") @@ -101,9 +99,7 @@ def test_runpython_numpy(self): import numpy print(numpy.array([1.123456789, 1.987654321])) - """.replace( - " ", "" - ) + """.replace(" ", "") html = rst2html(content, writer_name="rst") if "[1.12 1.99]" not in html: @@ -129,9 +125,7 @@ def test_runpython_numpy_linenos(self): import numpy print(numpy.array([1.123456789, 1.987654321])) - """.replace( - " ", "" - ) + """.replace(" ", "") html = rst2html(content, writer_name="rst") if "[1.12 1.99]" not in html: @@ -174,9 +168,7 @@ def depart_rp_node(self, node): import warnings warnings.warn("deprecated", DeprecationWarning) - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="rst") @@ -217,9 +209,7 @@ def depart_rp_node(self, node): import warnings warnings.warn("deprecated", DeprecationWarning) - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="rst") @@ -260,9 +250,7 @@ def depart_rp_node(self, node): import sys print(u"setsysvar: " + str(sys.__dict__.get( 'enable_disabled_documented_pieces_of_code', None))) - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="rst") @@ -319,9 +307,7 @@ def depart_rp_node(self, node): import sys print(u"setsysvar: " + str(sys.__dict__.get( 'enable_disabled_documented_pieces_of_code', None))) - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="html") @@ -372,9 +358,7 @@ def depart_rp_node(self, node): print(u"this code should" + u" appear") z = 1/0 print(u"this one should" + u" not") - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="html") @@ -417,9 +401,7 @@ def depart_rp_node(self, node): print(u"this code should" + u" appear") z = 0.5 + 0.6 print(u"this one should" + u" not") - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="html") @@ -440,9 +422,7 @@ def depart_rp_node(self, node): :assert: z == 1.2 z = 0.5 + 0.6 - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') try: @@ -485,9 +465,7 @@ def depart_rp_node(self, node): print(u"this code should" + u" appear") z = 1/0 print(u"this one should" + u" not") - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') html = rst2html(content, writer_name="html") diff --git a/_unittests/ut_runpython/test_runpython_extension_image.py b/_unittests/ut_runpython/test_runpython_extension_image.py index 6cb09d6..9b5609a 100644 --- a/_unittests/ut_runpython/test_runpython_extension_image.py +++ b/_unittests/ut_runpython/test_runpython_extension_image.py @@ -51,9 +51,7 @@ def depart_rp_node(self, node): text = ".. image:: oo.png\\n :width: 200px" print(text) - """.replace( - " ", "" - ).replace( + """.replace(" ", "").replace( "__FOLD__", temp.replace("\\", "/") ) content = content.replace('u"', '"') diff --git a/_unittests/ut_runpython/test_runpython_extension_toggle.py b/_unittests/ut_runpython/test_runpython_extension_toggle.py index 49ba8a5..b4351e9 100644 --- a/_unittests/ut_runpython/test_runpython_extension_toggle.py +++ b/_unittests/ut_runpython/test_runpython_extension_toggle.py @@ -54,9 +54,7 @@ def depart_rp_node(self, node): print(u"setsysvar: " + str( sys.__dict__.get( 'enable_disabled_documented_pieces_of_code', None))) - """.replace( - " ", "" - ) + """.replace(" ", "") content = content.replace('u"', '"') # HTML diff --git a/_unittests/ut_runpython/test_runpython_store_in_file.py b/_unittests/ut_runpython/test_runpython_store_in_file.py index e24574e..49cd411 100644 --- a/_unittests/ut_runpython/test_runpython_store_in_file.py +++ b/_unittests/ut_runpython/test_runpython_store_in_file.py @@ -39,9 +39,7 @@ def fctfct(): print("***********") print(code) print("***********") - """.replace( - " ", "" - ) + """.replace(" ", "") temp = os.path.abspath(os.path.dirname(__file__)) dest = os.path.join(temp, "exescript.py") diff --git a/pyproject.toml b/pyproject.toml index 7c9290c..34ddfac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,12 +40,13 @@ select = [ [tool.ruff.lint.extend-per-file-ignores] "**" = [ + "B905", "C401", "C408", "C413", "PIE790", "PYI041", "RUF012", "RUF100", "RUF010", "SIM108", "SIM102", "SIM114", "SIM103", "SIM910", - "UP006", "UP015", "UP027", "UP031", "UP034", "UP035", "UP032" + "UP006", "UP007", "UP015", "UP027", "UP031", "UP034", "UP035", "UP032", "UP045" ] "_doc/examples/plot_*.py" = ["E402", "B018", "PIE808", "SIM105", "SIM117"] "_doc/notebooks/plot_*.py" = ["E402", "B018", "PIE808", "SIM105", "SIM117"] diff --git a/sphinx_runpython/collapse/sphinx_collapse_extension.py b/sphinx_runpython/collapse/sphinx_collapse_extension.py index d55eafb..ff03380 100644 --- a/sphinx_runpython/collapse/sphinx_collapse_extension.py +++ b/sphinx_runpython/collapse/sphinx_collapse_extension.py @@ -126,9 +126,7 @@ def visit_collapse_node_html(self, node): x.style.display = "block"; b.innerText = '__HIDE__'; } else { x.style.display = "none"; b.innerText = '__UNHIDE__'; } - }""".replace( - " ", "" - ) + }""".replace(" ", "") script = script.replace("__ID__", nid) script = script.replace("__HIDE__", hide) script = script.replace("__UNHIDE__", unhide) diff --git a/sphinx_runpython/ext_io_helper.py b/sphinx_runpython/ext_io_helper.py index beabc7f..a287f3e 100644 --- a/sphinx_runpython/ext_io_helper.py +++ b/sphinx_runpython/ext_io_helper.py @@ -3,7 +3,6 @@ import os import re import shutil -import socket import time import warnings import http.client as http_client @@ -368,7 +367,7 @@ def _local_loop(ur): urllib_error.HTTPError, urllib_error.URLError, ConnectionRefusedError, - socket.timeout, + TimeoutError, ConnectionResetError, http_client.BadStatusLine, http_client.IncompleteRead, diff --git a/sphinx_runpython/gdot/sphinx_gdot_extension.py b/sphinx_runpython/gdot/sphinx_gdot_extension.py index 2692745..f527be1 100644 --- a/sphinx_runpython/gdot/sphinx_gdot_extension.py +++ b/sphinx_runpython/gdot/sphinx_gdot_extension.py @@ -217,17 +217,13 @@ def process(text): content = """
- """.format( - nid - ) + """.format(nid) script = ( """ require(['__URL__'], function() { var svgGraph = Viz("__DOT__"); document.getElementById('gdot-__ID__').innerHTML = svgGraph; }); - """.replace( - "__ID__", nid - ) + """.replace("__ID__", nid) .replace("__DOT__", process(node["code"])) .replace("__URL__", node["url"]) ) diff --git a/sphinx_runpython/runpython/run_cmd.py b/sphinx_runpython/runpython/run_cmd.py index bb0fc31..ca2323e 100644 --- a/sphinx_runpython/runpython/run_cmd.py +++ b/sphinx_runpython/runpython/run_cmd.py @@ -281,10 +281,10 @@ def run_cmd( begin = time.perf_counter() last_update = begin # with threads - (stdoutReader, stdoutQueue) = _AsyncLineReader.getForFd( + stdoutReader, stdoutQueue = _AsyncLineReader.getForFd( stdout, catch_exit=catch_exit ) - (stderrReader, stderrQueue) = _AsyncLineReader.getForFd( + stderrReader, stderrQueue = _AsyncLineReader.getForFd( stderr, catch_exit=catch_exit ) runloop = True diff --git a/sphinx_runpython/tools/sphinx_api.py b/sphinx_runpython/tools/sphinx_api.py index f5fb7fa..603890e 100644 --- a/sphinx_runpython/tools/sphinx_api.py +++ b/sphinx_runpython/tools/sphinx_api.py @@ -13,23 +13,19 @@ def _write_doc_folder( """ Creates all the file in a dictionary. """ - template = textwrap.dedent( - """ + template = textwrap.dedent(""" .. automodule:: :members: :no-undoc-members: - """ - ) + """) - index = textwrap.dedent( - """ + index = textwrap.dedent(""" - """ - ) + """) submodule = ".".join(os.path.splitext(folder)[0].replace("\\", "/").split("/")) fullsubmodule = f"{prefix}.{submodule}" if prefix else submodule @@ -39,16 +35,12 @@ def _write_doc_folder( .replace("", "=" * len(fullsubmodule)), ] if subfolders: - rows.append( - textwrap.dedent( - """ + rows.append(textwrap.dedent(""" .. toctree:: :maxdepth: 1 :caption: submodules - """ - ) - ) + """)) for sub in subfolders: rows.append(f" {sub}/index") res = {} @@ -75,29 +67,21 @@ def _write_doc_folder( res[key] = text if not has_module: has_module = True - rows.append( - textwrap.dedent( - """ + rows.append(textwrap.dedent(""" .. toctree:: :maxdepth: 1 :caption: modules - """ - ) - ) + """)) rows.append(f" {last}") - rows.append( - textwrap.dedent( - f""" + rows.append(textwrap.dedent(f""" .. automodule:: {submodule} :members: :no-undoc-members: - """ - ) - ) + """)) res["index.rst"] = "\n".join(rows) return res @@ -121,7 +105,7 @@ def sphinx_api( :return: list of written file """ folder = folder.rstrip("/\\") - root, package_name = os.path.split(folder) + root, _package_name = os.path.split(folder) files = [] if verbose: print(f"[sphinx_api] start creating API for {folder!r}") From 163e255b179bd62fa9c00b200ea90cce2a9812f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= Date: Thu, 26 Feb 2026 19:13:56 +0100 Subject: [PATCH 04/10] fix url (#44) * fix url * spell * fix --- CHANGELOGS.rst | 2 +- _unittests/ut_docassert/test_docassert.py | 18 ++-- .../gdot/sphinx_gdot_extension.py | 95 +++++-------------- .../runpython/sphinx_runpython_extension.py | 24 ++++- sphinx_runpython/sphinx_rst_builder.py | 1 + 5 files changed, 57 insertions(+), 83 deletions(-) diff --git a/CHANGELOGS.rst b/CHANGELOGS.rst index 090165f..f231c6b 100644 --- a/CHANGELOGS.rst +++ b/CHANGELOGS.rst @@ -4,7 +4,7 @@ Change Logs 0.4.1 +++++ -* :pr:`42`: excludes failing functions from pybind11 in docassert +* :pr:`42`: excludes failing functions from pybind11 in docassert 0.4.0 +++++ diff --git a/_unittests/ut_docassert/test_docassert.py b/_unittests/ut_docassert/test_docassert.py index 77fd152..e7ddb08 100644 --- a/_unittests/ut_docassert/test_docassert.py +++ b/_unittests/ut_docassert/test_docassert.py @@ -57,7 +57,7 @@ def __len__(self): for line in lines: if "'onefunction' has no parameter 'c'" in line: nb += 1 - if nb == 0 and "failed to import function" not in str(warn): + if nb == 0 and "failed to import " not in str(warn): raise AssertionError("not the right warning:\n" + "\n".join(lines)) @ignore_warnings(PendingDeprecationWarning) @@ -100,7 +100,7 @@ def __len__(self): for line in lines: if "'onemethod' has no parameter 'c'" in line: nb += 1 - if nb == 0 and "failed to import method" not in str(warn): + if nb == 0 and "failed to import " not in str(warn): raise AssertionError("not the right warning:\n" + "\n".join(lines)) for line in lines: if "'onemethod' has undocumented parameters 'b, self'" in line: @@ -147,8 +147,10 @@ def __len__(self): nb += 1 if "'Estimator' has undocumented parameters" in line: nb += 1 - if nb == 0 and "failed to import class" not in str(warn): - raise AssertionError("not the right warning:\n" + "\n".join(lines)) + if nb == 0 and "failed to import " not in str(warn): + raise AssertionError( + "not the right warning:\n" + "\n".join(lines) + "\n" + str(warn) + ) @ignore_warnings(PendingDeprecationWarning) def test_docassert_html_init2(self): @@ -191,7 +193,7 @@ def __len__(self): nb += 1 if "'Estimator2' has undocumented parameters" in line: nb += 1 - if nb == 0 and "failed to import class" not in str(warn): + if nb == 0 and "failed to import " not in str(warn): raise AssertionError("not the right warning:\n" + "\n".join(lines)) @ignore_warnings(PendingDeprecationWarning) @@ -223,9 +225,7 @@ def __len__(self): with sys_path_append(data): _obj, _name = import_object("clsslk.Estimator3", "class") newstring = ".. autoclass:: clsslk.Estimator3" - html, warn = rst2html( - newstring, return_warnings=True, new_extensions=["numpydoc"] - ) + html, warn = rst2html(newstring, return_warnings=True) self.assertTrue(html is not None) lines = log_capture_string.getvalue().split("\n") @@ -237,7 +237,7 @@ def __len__(self): nb += 1 if "'Estimator3' has undocumented parameters 'fit" in line: nb += 1 - if nb == 0 and "failed to import class" not in str(warn): + if nb == 0 and "failed to import " not in str(warn): raise AssertionError("not the right warning:\n" + "\n".join(lines)) def test_extract_signature(self): diff --git a/sphinx_runpython/gdot/sphinx_gdot_extension.py b/sphinx_runpython/gdot/sphinx_gdot_extension.py index f527be1..d3f1dcc 100644 --- a/sphinx_runpython/gdot/sphinx_gdot_extension.py +++ b/sphinx_runpython/gdot/sphinx_gdot_extension.py @@ -1,6 +1,5 @@ import os import logging -import shutil from docutils import nodes from docutils.parsers.rst import directives, Directive import sphinx @@ -102,10 +101,7 @@ class GDotDirective(Directive): "process": directives.unchanged, } - _default_url = ( - "https://github.com/sdpython/jyquickhelper/raw/master/src/" - "jyquickhelper/js/vizjs/viz.js" - ) + _default_url = "https://cdnjs.cloudflare.com/ajax/libs/viz.js/1.8.0/viz-lite.js" def run(self): """ @@ -120,19 +116,7 @@ def run(self): bool_set_ = (True, 1, "True", "1", "true", "") process = "process" in self.options and self.options["process"] in bool_set_ if url == "local": - try: - import jyquickhelper - - path = os.path.join( - os.path.dirname(jyquickhelper.__file__), "js", "vizjs", "viz.js" - ) - if not os.path.exists(path): - raise ImportError("jyquickelper needs to be updated to get viz.js.") - url = "local" - except ImportError: - url = GDotDirective._default_url - logger = logging.getLogger("gdot") - logger.warning("[gdot] use %r", url) + url = GDotDirective._default_url info = get_env_state_info(self) docname = info["docname"] @@ -165,11 +149,13 @@ def run(self): if script or script == "": stdout, stderr, _ = run_python_script(content, process=process) if stderr: + logger = logging.getLogger("gdot") logger.warning("[gdot] a dot graph cannot be draw due to %s", stderr) content = stdout if script: spl = content.split(script) if len(spl) > 2: + logger = logging.getLogger("gdot") logger.warning("[gdot] too many output lines %s", content) content = spl[-1] @@ -300,13 +286,6 @@ def depart_gdot_node_html(self, node): def copy_js_files(app): - try: - import jyquickhelper - - local = True - except ImportError: - local = False - logger = logging.getLogger("gdot") dest = app.config.html_static_path if isinstance(dest, list) and len(dest) > 0: @@ -333,63 +312,41 @@ def copy_js_files(app): # viz.js file_dest = os.path.join(destf, "viz.js") - if os.path.exists(file_dest): - logger.info("[gdot] %r already installed.", file_dest) - else: - if local: - path = os.path.join( - os.path.dirname(jyquickhelper.__file__), "js", "vizjs", "viz.js" + if not os.path.exists(file_dest): + logger.info("[gdot] viz.js, use %r", GDotDirective._default_url) + + try: + content = get_url_content_timeout( + GDotDirective._default_url, output=file_dest, raise_exception=False + ) + except Exception as e: + logger.warning("[gdot] download failed due to %r", e) + content = None + + if content is None: + logger.warning( + "[gdot] unable to download %r to %r", + GDotDirective._default_url, + file_dest, ) - if os.path.exists(path): - # We copy the file to static path. - try: - shutil.copy(path, file_dest) - logger.info("[gdot] copy %r to %r.", path, file_dest) - except PermissionError as e: - logger.warning( - "[gdot] permission error (%r), unable to use local viz.js", e - ) - else: - logger.warning("[gdot] unable to find %r", path) else: - logger.info("[gdot] viz.js, use %r", GDotDirective._default_url) - - file_dest = os.path.join(destf, "require.js") - try: - content = get_url_content_timeout( - GDotDirective._default_url, output=file_dest, raise_exception=False - ) - except Exception as e: - logger.warning("[gdot] download failed due to %r", e) - content = None - - if content is None: - logger.warning( - "[gdot] unable to download %r to %r", - GDotDirective._default_url, - file_dest, - ) - else: - logger.info( - "[gdot] download %r to %r", GDotDirective._default_url, file_dest - ) + logger.info( + "[gdot] download %r to %r", GDotDirective._default_url, file_dest + ) # require.js file_dest = os.path.join(destf, "require.js") - if os.path.exists(file_dest): - logger.info("[gdot] %r already installed.", file_dest) - else: + if not os.path.exists(file_dest): + logger.info("[gdot] download %r", file_dest) try: download_requirejs(destf) except Exception as e: logger.warning("[gdot] download_requirejs failed due to %r", e) - if os.path.exists(file_dest): + if not os.path.exists(file_dest): # It adds # at the bottom of the file. It needs to be at the beginning. # app.add_js_file("require.js", priority=200) - logger.info("[gdot] %r installed.", file_dest) - else: logger.warning("[gdot] %r not installed.", file_dest) diff --git a/sphinx_runpython/runpython/sphinx_runpython_extension.py b/sphinx_runpython/runpython/sphinx_runpython_extension.py index 1c16ec0..978e606 100644 --- a/sphinx_runpython/runpython/sphinx_runpython_extension.py +++ b/sphinx_runpython/runpython/sphinx_runpython_extension.py @@ -372,7 +372,7 @@ def interpret(s): try: out, err = run_cmd(cmd, script_arg, wait=True, change_path=chdir) - return out, err, None + return out, _filter_error(err), None except Exception as ee: if not exception: message = ( # noqa: UP030 @@ -383,7 +383,7 @@ def interpret(s): if exc_path: message += f"\n---EXC--\n{exc_path}" raise RunPythonExecutionError(message) from ee - return str(ee), str(ee), None + return str(ee), _filter_error(str(ee)), None else: if store_in_file: raise NotImplementedError( @@ -446,7 +446,7 @@ def interpret(s): "\n{5}\n--TRACEBACK--\n{6}" ).format(script, params, comment, gout, gerr, ee, excs) raise RunPythonExecutionError(message) from ee - return (gout + "\n" + gerr), (gerr + "\n" + excs), None + return (gout + "\n" + gerr), _filter_error(gerr + "\n" + excs), None if chdir is not None: os.chdir(current) @@ -462,7 +462,23 @@ def interpret(s): for k, v in globs.items() if k.startswith("__runpython__") and k not in avoid } - return gout, gerr, context + return gout, _filter_error(gerr), context + + +def _filter_error(err): + if not err: + return err + out = [ + "use_kernel_func_from_hub", + "is deprecated, use", + "was set in the config but", + ] + if isinstance(err, str): + res = "\n".join([_ for _ in err.split("\n") if all(o not in _ for o in out)]) + return res + bout = [_.encode("utf-8") for _ in out] + res = b"\n".join([_ for _ in err.split(b"\n") if all(o not in _ for o in bout)]) + return res class runpython_node(nodes.Structural, nodes.Element): diff --git a/sphinx_runpython/sphinx_rst_builder.py b/sphinx_runpython/sphinx_rst_builder.py index 627c359..5b0c02e 100644 --- a/sphinx_runpython/sphinx_rst_builder.py +++ b/sphinx_runpython/sphinx_rst_builder.py @@ -1421,6 +1421,7 @@ def get_outfilename(self, pagename): return f"{self.outdir}/{pagename}.rst".replace("\\", "/") def write_doc(self, docname, doctree): + """write documentation""" destination = StringOutput(encoding="utf-8") self.current_docname = docname self.writer.write(doctree, destination) From b8594e952e4ca2d472eeb35f80ce9c7008c1d088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?xavier=20dupr=C3=A9?= Date: Thu, 26 Feb 2026 19:17:43 +0100 Subject: [PATCH 05/10] Gdot (#45) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix url * spell * fix * last --------- Co-authored-by: Xavier Dupré --- .../gdot/sphinx_gdot_extension.py | 22 +++++++++++++++++++ .../runpython/sphinx_runpython_extension.py | 2 -- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/sphinx_runpython/gdot/sphinx_gdot_extension.py b/sphinx_runpython/gdot/sphinx_gdot_extension.py index d3f1dcc..b9b7083 100644 --- a/sphinx_runpython/gdot/sphinx_gdot_extension.py +++ b/sphinx_runpython/gdot/sphinx_gdot_extension.py @@ -148,6 +148,28 @@ def run(self): content = "\n".join(self.content) if script or script == "": stdout, stderr, _ = run_python_script(content, process=process) + + if stderr: + out = [ + "use_kernel_func_from_hub", + "is deprecated, use", + "was set in the config but", + "The axis name: batch will not be used", + ] + if isinstance(stderr, str): + stderr = "\n".join( + [_ for _ in stderr.split("\n") if all(o not in _ for o in out)] + ) + else: + bout = [_.encode("utf-8") for _ in out] + stderr = b"\n".join( + [ + _ + for _ in stderr.split(b"\n") + if all(o not in _ for o in bout) + ] + ) + if stderr: logger = logging.getLogger("gdot") logger.warning("[gdot] a dot graph cannot be draw due to %s", stderr) diff --git a/sphinx_runpython/runpython/sphinx_runpython_extension.py b/sphinx_runpython/runpython/sphinx_runpython_extension.py index 978e606..bf8b807 100644 --- a/sphinx_runpython/runpython/sphinx_runpython_extension.py +++ b/sphinx_runpython/runpython/sphinx_runpython_extension.py @@ -470,8 +470,6 @@ def _filter_error(err): return err out = [ "use_kernel_func_from_hub", - "is deprecated, use", - "was set in the config but", ] if isinstance(err, str): res = "\n".join([_ for _ in err.split("\n") if all(o not in _ for o in out)]) From 2820367281bb0d39f39c91623c8d9a1359baf4cb Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Mar 2026 15:22:56 +0100 Subject: [PATCH 06/10] gdot: default image format changed from PNG to SVG (#47) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial plan * Use svg by default instead of png in gdot directive Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> * update version * fix svg * fix svg * fix * fix --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> Co-authored-by: Xavier Dupré --- CHANGELOGS.rst | 5 + _doc/conf.py | 2 + _doc/index.rst | 1 + _unittests/ut_gdot/test_gdot_extension.py | 22 ++-- sphinx_runpython/__init__.py | 2 +- sphinx_runpython/ext_test_case.py | 36 +++++++ .../gdot/sphinx_gdot_extension.py | 102 +++++++++++++++--- 7 files changed, 148 insertions(+), 22 deletions(-) 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'
') + if format == "svg": + self.body.append('
') + self.body.append( + f'\n' + ) + self.body.append(f'

{alt}

') + self.body.append("
\n") + else: + assert outfn is not None + with open(f"{outfn}.map", encoding="utf-8") as mapfile: + map_content = mapfile.read() + imgmap = ClickableMapDefinition(f"{outfn}.map", map_content, dot=code) + if imgmap.clickable: + # has a map + self.body.append('
') + self.body.append( + f'{alt}' + ) + self.body.append("
\n") + self.body.append(imgmap.generate_clickable_map()) + else: + # nothing in image map + self.body.append('
') + self.body.append(f'{alt}') + self.body.append("
\n") + if "align" in node: + self.body.append("
\n") + + raise nodes.SkipNode + + def visit_gdot_node_html_svg(self, node): - """ - visit collapse_node - """ + """visit collapse_node""" + if node["use_sphinx_graphviz"]: + render_dot_html( + self, node, node["code"], node["options"], filename=node.get("filename") + ) + return def process(text): text = text.replace("\\", "\\\\") From 317aa8ee4fcb4fab6097f125f5b489b8f89ae893 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Mar 2026 15:42:35 +0100 Subject: [PATCH 07/10] Replace Azure Pipelines CI with GitHub Actions (#51) * Initial plan * Replace Azure Pipelines CI with GitHub Actions tests workflow Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> --- .github/workflows/black-ruff.yml | 4 +- .github/workflows/check-urls.yml | 2 +- .github/workflows/codeql.yml | 8 ++-- .github/workflows/documentation.yml | 4 +- .github/workflows/spell-check.yml | 2 +- .github/workflows/tests.yml | 64 +++++++++++++++++++++++++++++ .github/workflows/wheels-any.yml | 2 +- 7 files changed, 75 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/black-ruff.yml b/.github/workflows/black-ruff.yml index aaa98e3..fae3f72 100644 --- a/.github/workflows/black-ruff.yml +++ b/.github/workflows/black-ruff.yml @@ -4,7 +4,7 @@ jobs: black-format-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: psf/black@stable with: options: "--diff --check" @@ -12,5 +12,5 @@ jobs: ruff-format-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: chartboost/ruff-action@v1 diff --git a/.github/workflows/check-urls.yml b/.github/workflows/check-urls.yml index 3d177eb..c8ee37e 100644 --- a/.github/workflows/check-urls.yml +++ b/.github/workflows/check-urls.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: urls-checker-code uses: urlstechie/urlchecker-action@master diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index bea1259..2170757 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -32,11 +32,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java, ruby @@ -44,7 +44,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually (see below). - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -58,4 +58,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 3a67c85..e318806 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -17,7 +17,7 @@ jobs: os: [ubuntu-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: tlylt/install-graphviz@v1 @@ -52,7 +52,7 @@ jobs: export PYTHONPATH= - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/spell-check.yml b/.github/workflows/spell-check.yml index fe51c4d..ea0b764 100644 --- a/.github/workflows/spell-check.yml +++ b/.github/workflows/spell-check.yml @@ -13,7 +13,7 @@ jobs: steps: # Checkout the repository - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Install codespell - name: Install codespell diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..4fcb7ab --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,64 @@ +name: CI + +on: + push: + pull_request: + +jobs: + tests: + name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + permissions: + contents: read + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + python-version: '3.13' + - os: ubuntu-latest + python-version: '3.12' + - os: windows-latest + python-version: '3.12' + - os: macos-latest + python-version: '3.12' + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install system dependencies (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y pandoc graphviz + + - name: Install system dependencies (macOS) + if: runner.os == 'macOS' + run: brew install pandoc graphviz + + - name: Install pip tools + run: python -m pip install --upgrade pip setuptools wheel + + - name: Install requirements + run: pip install -r requirements.txt + + - name: Install requirements-dev + run: pip install -r requirements-dev.txt + + - name: Build wheel + run: python -m pip wheel . --wheel-dir dist + + - name: Install package + run: pip install . + + - name: Run tests + run: python -m pytest --durations=10 --ignore-glob=**LONG*.py --ignore-glob=**notebook*.py + + - uses: actions/upload-artifact@v4 + with: + name: wheel-${{ matrix.os }}-${{ matrix.python-version }} + path: dist/ diff --git a/.github/workflows/wheels-any.yml b/.github/workflows/wheels-any.yml index ab58575..e1b2cbc 100644 --- a/.github/workflows/wheels-any.yml +++ b/.github/workflows/wheels-any.yml @@ -15,7 +15,7 @@ jobs: os: [ubuntu-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: From ed8d015dd379c04c662007375be3a9f6da775869 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:33:27 +0100 Subject: [PATCH 08/10] Skip graphviz binary calls and emit format-specific dummy output when UNITTEST_GOING=1 (#53) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial plan * Disable gdot graphviz call when UNITTEST_GOING=1, replace with dummy SVG Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> * Differentiate dummy SVG vs PNG output when UNITTEST_GOING=1, both contain DISABLED FOR TESTS Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> * style * style --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> Co-authored-by: Xavier Dupré --- _unittests/ut_gdot/test_gdot_extension.py | 69 +++++++++++++++++++ .../gdot/sphinx_gdot_extension.py | 29 ++++++++ 2 files changed, 98 insertions(+) diff --git a/_unittests/ut_gdot/test_gdot_extension.py b/_unittests/ut_gdot/test_gdot_extension.py index d1ee7a3..dea0ff4 100644 --- a/_unittests/ut_gdot/test_gdot_extension.py +++ b/_unittests/ut_gdot/test_gdot_extension.py @@ -1,6 +1,8 @@ import unittest import logging +import os import sys +from contextlib import contextmanager from sphinx_runpython.process_rst import rst2html from sphinx_runpython.ext_test_case import ( ExtTestCase, @@ -10,6 +12,20 @@ ) +@contextmanager +def unittest_going(): + """Context manager that sets UNITTEST_GOING=1 for the duration of the block.""" + old = os.environ.get("UNITTEST_GOING", None) + os.environ["UNITTEST_GOING"] = "1" + try: + yield + finally: + if old is None: + os.environ.pop("UNITTEST_GOING", None) + else: + os.environ["UNITTEST_GOING"] = old + + class TestGDotExtension(ExtTestCase): def setUp(self): logger = logging.getLogger("gdot") @@ -146,6 +162,59 @@ def test_gdot4_png(self): return self.assertIn("png", content) + @ignore_warnings(PendingDeprecationWarning) + def test_gdot_unittest_going_svg(self): + """When UNITTEST_GOING=1, a dummy SVG containing 'DISABLED FOR TESTS' is rendered.""" + content = """ + before + + .. gdot:: + :format: svg + + digraph foo { + "bar" -> "baz"; + } + + after + """.replace(" ", "") + + with unittest_going(): + html = rst2html( + content, writer_name="html", new_extensions=["sphinx_runpython.gdot"] + ) + + self.assertIn("DISABLED FOR TESTS", html) + self.assertIn(" "baz"; + } + + after + """.replace(" ", "") + + with unittest_going(): + html = rst2html( + content, writer_name="html", new_extensions=["sphinx_runpython.gdot"] + ) + + self.assertIn("DISABLED FOR TESTS", html) + self.assertIn("' + 'DISABLED FOR TESTS' + "" +) + +_DUMMY_PNG_HTML = ( + 'DISABLED FOR TESTS' +) + + +def _emit_dummy_output(self, format: str = "svg"): + """Emit a placeholder graphic when ``UNITTEST_GOING=1`` is set.""" + self.body.append('
') + if format == "png": + self.body.append(_DUMMY_PNG_HTML) + else: + self.body.append(_DUMMY_SVG) + self.body.append("
\n") + raise nodes.SkipNode + + def render_dot_html( self, node: gdot_node, @@ -227,6 +251,9 @@ def render_dot_html( filename: str | None = None, format: str = "svg", ) -> tuple[str, str]: + if os.environ.get("UNITTEST_GOING", "0") == "1": + _emit_dummy_output(self, format=format) + if format not in {"png", "svg"}: logger = logging.getLogger(__name__) logger.warning(__("format must be either 'png' or 'svg', but is %r"), format) @@ -362,6 +389,8 @@ def visit_gdot_node_html(self, node): and the :epkg:`SVG` format. """ if node["format"].lower() == "png": + if os.environ.get("UNITTEST_GOING", "0") == "1": + _emit_dummy_output(self, format="png") from sphinx.ext.graphviz import html_visit_graphviz return html_visit_graphviz(self, node) From f3fadce0ee436893d12c00a1dc3a291d584f34d6 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:43:41 +0100 Subject: [PATCH 09/10] Cache gdot script execution results in Sphinx environment (#49) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial plan * speedup runpython execution for graphviz: cache script results in Sphinx env Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> * fix * fix * gdot: prefix cache key with last segment of docname Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> * change * changes --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> Co-authored-by: Xavier Dupré --- CHANGELOGS.rst | 3 ++ _unittests/ut_gdot/test_gdot_extension.py | 29 +++++++++++++++++++ .../gdot/sphinx_gdot_extension.py | 21 +++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/CHANGELOGS.rst b/CHANGELOGS.rst index 08cc2f9..5ab7192 100644 --- a/CHANGELOGS.rst +++ b/CHANGELOGS.rst @@ -4,6 +4,9 @@ Change Logs 0.4.2 +++++ +* :pr:`53`: Disable gdot call to graphviz if ``UNITTEST_GOING=1`` +* :pr:`51`: Uses GitHub actions for CI +* :pr:`49`: Cache gdot script execution results in Sphinx environment` * :pr:`47`: use svg by default with gdot 0.4.1 diff --git a/_unittests/ut_gdot/test_gdot_extension.py b/_unittests/ut_gdot/test_gdot_extension.py index dea0ff4..41f9393 100644 --- a/_unittests/ut_gdot/test_gdot_extension.py +++ b/_unittests/ut_gdot/test_gdot_extension.py @@ -163,6 +163,35 @@ def test_gdot4_png(self): self.assertIn("png", content) @ignore_warnings(PendingDeprecationWarning) + def test_gdot_script_cache(self): + """Test that identical scripts are cached and produce the same output.""" + script = "print('digraph foo { HbarH -> HbazH; }')".replace("H", '"') + content = f""" +before + +.. gdot:: + :script: + + {script} + +middle + +.. gdot:: + :script: + + {script} + +after +""" + content = rst2html( + content, writer_name="rst", new_extensions=["sphinx_runpython.gdot"] + ) + # Both gdot directives should produce the same DOT output + count = content.count('digraph foo { "bar" -> "baz"; }') + self.assertEqual( + count, 2, f"Expected the DOT code to appear twice, got {count}" + ) + def test_gdot_unittest_going_svg(self): """When UNITTEST_GOING=1, a dummy SVG containing 'DISABLED FOR TESTS' is rendered.""" content = """ diff --git a/sphinx_runpython/gdot/sphinx_gdot_extension.py b/sphinx_runpython/gdot/sphinx_gdot_extension.py index da1905b..285551c 100644 --- a/sphinx_runpython/gdot/sphinx_gdot_extension.py +++ b/sphinx_runpython/gdot/sphinx_gdot_extension.py @@ -1,3 +1,4 @@ +import hashlib import os import logging from docutils import nodes @@ -153,7 +154,25 @@ def run(self): # executes script if any content = "\n".join(self.content) if script or script == "": - stdout, stderr, _ = run_python_script(content, process=process) + env = info.get("env") + doc_prefix = docname.split("/")[-1] if docname else "" + cache_key = ( + f"{doc_prefix}:" + + hashlib.sha256(f"{content}:{process}".encode()).hexdigest() + ) + if env is not None: + if not hasattr(env, "gdot_script_cache"): + env.gdot_script_cache = {} + cached = env.gdot_script_cache.get(cache_key, None) + else: + cached = None + + if cached is not None: + stdout, stderr = cached + else: + stdout, stderr, _ = run_python_script(content, process=process) + if env is not None: + env.gdot_script_cache[cache_key] = (stdout, stderr) if stderr: out = [ From a8e6cdbc195e6c8dafcc088eeef0dc20500c3f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= Date: Fri, 6 Mar 2026 23:34:00 +0100 Subject: [PATCH 10/10] polish loggers (#54) --- sphinx_runpython/blocdefs/sphinx_blocref_extension.py | 4 ++-- sphinx_runpython/collapse/sphinx_collapse_extension.py | 3 ++- sphinx_runpython/docassert/sphinx_docassert_extension.py | 7 ++----- sphinx_runpython/gdot/sphinx_gdot_extension.py | 7 ++----- sphinx_runpython/runpython/sphinx_runpython_extension.py | 3 ++- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/sphinx_runpython/blocdefs/sphinx_blocref_extension.py b/sphinx_runpython/blocdefs/sphinx_blocref_extension.py index 90a6200..7afa9bb 100644 --- a/sphinx_runpython/blocdefs/sphinx_blocref_extension.py +++ b/sphinx_runpython/blocdefs/sphinx_blocref_extension.py @@ -19,6 +19,8 @@ from ..language import TITLES from ..ext_helper import info_blocref +logger = logging.getLogger("blocref") + class blocref_node(nodes.admonition): """ @@ -471,7 +473,6 @@ def process_blocref_nodes_generic( try: targ = blocref_info["target"] except KeyError as e: - logger = logging.getLogger("blocref") logger.warning( "Unable to find key 'target' in %r (e=%r)", blocref_info, e ) @@ -479,7 +480,6 @@ def process_blocref_nodes_generic( try: targ_refid = blocref_info["target"]["refid"] except KeyError as e: - logger = logging.getLogger("blocref") logger.warning("Unable to find key 'refid' in %r (e=%r)", targ, e) continue int_ids = [f"index{targ_refid}-{env.new_serialno(targ_refid)}"] diff --git a/sphinx_runpython/collapse/sphinx_collapse_extension.py b/sphinx_runpython/collapse/sphinx_collapse_extension.py index ff03380..e64256d 100644 --- a/sphinx_runpython/collapse/sphinx_collapse_extension.py +++ b/sphinx_runpython/collapse/sphinx_collapse_extension.py @@ -6,6 +6,8 @@ from sphinx.util.nodes import nested_parse_with_titles from ..language import TITLES, sphinx_lang +logger = logging.getLogger("sphinx") + class collapse_node(nodes.admonition): """ @@ -61,7 +63,6 @@ def run(self): if "legend" in self.options: legend = self.options["legend"] if "/" not in legend: - logger = logging.getLogger("sphinx") logger.warning( "[CollapseDirective] unable to interpret parameter legend %r.", legend, diff --git a/sphinx_runpython/docassert/sphinx_docassert_extension.py b/sphinx_runpython/docassert/sphinx_docassert_extension.py index 0b81024..af6e55a 100644 --- a/sphinx_runpython/docassert/sphinx_docassert_extension.py +++ b/sphinx_runpython/docassert/sphinx_docassert_extension.py @@ -6,6 +6,8 @@ from sphinx.util.docfields import DocFieldTransformer, _is_single_paragraph from ..import_object_helper import import_any_object, import_object +logger = logging.getLogger("docassert") + class Parameter: "Definition of a parameter." @@ -121,7 +123,6 @@ def kg(p): return p if isinstance(p, str) else p.name check_params = {kg(p): 0 for p in parameters} - logger = logging.getLogger("docassert") def check_item(fieldarg, content, logger): "local function" @@ -339,7 +340,6 @@ def override_transform(self, other_self, node): reasons = "\n".join(f" {e}" for e in excs) else: reasons = "unknown" - logger = logging.getLogger("docassert") logger.warning( "[docassert] unable to import object %r, reasons: %s", docs, reasons ) @@ -353,7 +353,6 @@ def override_transform(self, other_self, node): parameters = signature.parameters except (TypeError, ValueError): # built-in function - logger = logging.getLogger("docassert") if myfunc.__text_signature__: logger.warning( "[docassert] unable to get signature (1) of %r: %s", @@ -393,7 +392,6 @@ def override_transform(self, other_self, node): try: env = other_self.directive.state.document.settings.env except AttributeError as e: - logger = logging.getLogger("docassert") logger.warning("[docassert] %s", e) env = None @@ -401,7 +399,6 @@ def override_transform(self, other_self, node): for entry in entries: if isinstance(entry, nodes.field): - logger = logging.getLogger("docassert") logger.warning("[docassert] unable to check [nodes.field] %s", entry) else: fieldtype, content = entry diff --git a/sphinx_runpython/gdot/sphinx_gdot_extension.py b/sphinx_runpython/gdot/sphinx_gdot_extension.py index 285551c..540757c 100644 --- a/sphinx_runpython/gdot/sphinx_gdot_extension.py +++ b/sphinx_runpython/gdot/sphinx_gdot_extension.py @@ -17,6 +17,8 @@ from ..ext_io_helper import download_requirejs, get_url_content_timeout from ..runpython.sphinx_runpython_extension import run_python_script +logger = logging.getLogger("gdot") + class gdot_node(nodes.admonition): """ @@ -130,7 +132,6 @@ def run(self): if url == "local": if docname is None or "HERE" not in info: url = GDotDirective._default_url - logger = logging.getLogger("gdot") logger.warning("[gdot] docname is none, falling back to %r.", url) else: spl = docname.split("/") @@ -196,13 +197,11 @@ def run(self): ) if stderr: - logger = logging.getLogger("gdot") logger.warning("[gdot] a dot graph cannot be draw due to %s", stderr) content = stdout if script: spl = content.split(script) if len(spl) > 2: - logger = logging.getLogger("gdot") logger.warning("[gdot] too many output lines %s", content) content = spl[-1] @@ -274,7 +273,6 @@ def render_dot_html( _emit_dummy_output(self, format=format) 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) @@ -428,7 +426,6 @@ def depart_gdot_node_html(self, node): def copy_js_files(app): - logger = logging.getLogger("gdot") dest = app.config.html_static_path if isinstance(dest, list) and len(dest) > 0: dest = dest[0] diff --git a/sphinx_runpython/runpython/sphinx_runpython_extension.py b/sphinx_runpython/runpython/sphinx_runpython_extension.py index bf8b807..d5a15d6 100644 --- a/sphinx_runpython/runpython/sphinx_runpython_extension.py +++ b/sphinx_runpython/runpython/sphinx_runpython_extension.py @@ -14,6 +14,8 @@ from .run_cmd import run_cmd from ..collapse.sphinx_collapse_extension import collapse_node +logger = logging.getLogger("runpython") + def remove_extra_spaces_and_black( filename: str, apply_black=True, is_string=None @@ -757,7 +759,6 @@ def run(self): try: script_disp = remove_extra_spaces_and_black(script_disp, is_string=True) except Exception as e: - logger = logging.getLogger("runpython") if "." in docname: comment = f' File "{docname}", line {lineno}' else: