Sphinx 8.1.2__py3-none-any.whl → 8.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of Sphinx might be problematic. Click here for more details.
- sphinx/__init__.py +8 -4
- sphinx/__main__.py +2 -0
- sphinx/_cli/__init__.py +2 -5
- sphinx/_cli/util/colour.py +34 -11
- sphinx/_cli/util/errors.py +128 -61
- sphinx/addnodes.py +51 -35
- sphinx/application.py +362 -230
- sphinx/builders/__init__.py +87 -64
- sphinx/builders/_epub_base.py +65 -56
- sphinx/builders/changes.py +17 -23
- sphinx/builders/dirhtml.py +8 -13
- sphinx/builders/epub3.py +70 -38
- sphinx/builders/gettext.py +93 -73
- sphinx/builders/html/__init__.py +240 -186
- sphinx/builders/html/_assets.py +9 -2
- sphinx/builders/html/_build_info.py +3 -0
- sphinx/builders/latex/__init__.py +64 -54
- sphinx/builders/latex/constants.py +14 -11
- sphinx/builders/latex/nodes.py +2 -0
- sphinx/builders/latex/theming.py +8 -9
- sphinx/builders/latex/transforms.py +7 -5
- sphinx/builders/linkcheck.py +193 -149
- sphinx/builders/manpage.py +17 -17
- sphinx/builders/singlehtml.py +28 -16
- sphinx/builders/texinfo.py +28 -21
- sphinx/builders/text.py +10 -15
- sphinx/builders/xml.py +10 -19
- sphinx/cmd/build.py +49 -119
- sphinx/cmd/make_mode.py +35 -31
- sphinx/cmd/quickstart.py +78 -62
- sphinx/config.py +265 -163
- sphinx/directives/__init__.py +51 -54
- sphinx/directives/admonitions.py +107 -0
- sphinx/directives/code.py +24 -19
- sphinx/directives/other.py +21 -42
- sphinx/directives/patches.py +28 -16
- sphinx/domains/__init__.py +54 -31
- sphinx/domains/_domains_container.py +22 -17
- sphinx/domains/_index.py +5 -8
- sphinx/domains/c/__init__.py +366 -245
- sphinx/domains/c/_ast.py +378 -256
- sphinx/domains/c/_ids.py +89 -31
- sphinx/domains/c/_parser.py +283 -214
- sphinx/domains/c/_symbol.py +269 -198
- sphinx/domains/changeset.py +39 -24
- sphinx/domains/citation.py +54 -24
- sphinx/domains/cpp/__init__.py +517 -362
- sphinx/domains/cpp/_ast.py +999 -682
- sphinx/domains/cpp/_ids.py +133 -65
- sphinx/domains/cpp/_parser.py +746 -588
- sphinx/domains/cpp/_symbol.py +692 -489
- sphinx/domains/index.py +10 -8
- sphinx/domains/javascript.py +152 -74
- sphinx/domains/math.py +50 -40
- sphinx/domains/python/__init__.py +402 -211
- sphinx/domains/python/_annotations.py +134 -61
- sphinx/domains/python/_object.py +155 -68
- sphinx/domains/rst.py +94 -49
- sphinx/domains/std/__init__.py +510 -249
- sphinx/environment/__init__.py +345 -61
- sphinx/environment/adapters/asset.py +7 -1
- sphinx/environment/adapters/indexentries.py +15 -20
- sphinx/environment/adapters/toctree.py +19 -9
- sphinx/environment/collectors/__init__.py +3 -1
- sphinx/environment/collectors/asset.py +18 -15
- sphinx/environment/collectors/dependencies.py +8 -10
- sphinx/environment/collectors/metadata.py +6 -4
- sphinx/environment/collectors/title.py +3 -1
- sphinx/environment/collectors/toctree.py +4 -4
- sphinx/errors.py +1 -3
- sphinx/events.py +4 -4
- sphinx/ext/apidoc/__init__.py +66 -0
- sphinx/ext/apidoc/__main__.py +9 -0
- sphinx/ext/apidoc/_cli.py +356 -0
- sphinx/ext/apidoc/_extension.py +262 -0
- sphinx/ext/apidoc/_generate.py +356 -0
- sphinx/ext/apidoc/_shared.py +99 -0
- sphinx/ext/autodoc/__init__.py +837 -483
- sphinx/ext/autodoc/directive.py +57 -21
- sphinx/ext/autodoc/importer.py +184 -67
- sphinx/ext/autodoc/mock.py +25 -10
- sphinx/ext/autodoc/preserve_defaults.py +17 -9
- sphinx/ext/autodoc/type_comment.py +56 -29
- sphinx/ext/autodoc/typehints.py +49 -26
- sphinx/ext/autosectionlabel.py +28 -11
- sphinx/ext/autosummary/__init__.py +281 -142
- sphinx/ext/autosummary/generate.py +121 -51
- sphinx/ext/coverage.py +152 -91
- sphinx/ext/doctest.py +169 -101
- sphinx/ext/duration.py +12 -6
- sphinx/ext/extlinks.py +33 -21
- sphinx/ext/githubpages.py +8 -8
- sphinx/ext/graphviz.py +175 -109
- sphinx/ext/ifconfig.py +11 -6
- sphinx/ext/imgconverter.py +48 -25
- sphinx/ext/imgmath.py +127 -97
- sphinx/ext/inheritance_diagram.py +177 -103
- sphinx/ext/intersphinx/__init__.py +22 -13
- sphinx/ext/intersphinx/__main__.py +3 -1
- sphinx/ext/intersphinx/_cli.py +18 -14
- sphinx/ext/intersphinx/_load.py +91 -82
- sphinx/ext/intersphinx/_resolve.py +108 -74
- sphinx/ext/intersphinx/_shared.py +2 -2
- sphinx/ext/linkcode.py +28 -12
- sphinx/ext/mathjax.py +60 -29
- sphinx/ext/napoleon/__init__.py +19 -7
- sphinx/ext/napoleon/docstring.py +229 -231
- sphinx/ext/todo.py +44 -49
- sphinx/ext/viewcode.py +105 -57
- sphinx/extension.py +3 -1
- sphinx/highlighting.py +13 -7
- sphinx/io.py +9 -13
- sphinx/jinja2glue.py +29 -26
- sphinx/locale/__init__.py +8 -9
- sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ar/LC_MESSAGES/sphinx.po +2155 -2050
- sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bg/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bn/LC_MESSAGES/sphinx.po +2175 -2070
- sphinx/locale/ca/LC_MESSAGES/sphinx.js +3 -3
- sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca/LC_MESSAGES/sphinx.po +2690 -2585
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.js +63 -0
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.po +4216 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.po +2096 -1991
- sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cs/LC_MESSAGES/sphinx.po +2248 -2143
- sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cy/LC_MESSAGES/sphinx.po +2201 -2096
- sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/da/LC_MESSAGES/sphinx.po +2282 -2177
- sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de/LC_MESSAGES/sphinx.po +2261 -2156
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/el/LC_MESSAGES/sphinx.po +2604 -2499
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2631 -2526
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eo/LC_MESSAGES/sphinx.po +2078 -1973
- sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es/LC_MESSAGES/sphinx.po +2633 -2528
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/et/LC_MESSAGES/sphinx.po +2449 -2344
- sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eu/LC_MESSAGES/sphinx.po +2241 -2136
- sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fa/LC_MESSAGES/sphinx.po +504 -500
- sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fi/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr/LC_MESSAGES/sphinx.po +513 -509
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.po +2644 -2539
- sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi/LC_MESSAGES/sphinx.po +504 -500
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hr/LC_MESSAGES/sphinx.po +501 -497
- sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hu/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/id/LC_MESSAGES/sphinx.po +2609 -2504
- sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/is/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/it/LC_MESSAGES/sphinx.po +2265 -2160
- sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ja/LC_MESSAGES/sphinx.po +2621 -2516
- sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ka/LC_MESSAGES/sphinx.po +2567 -2462
- sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ko/LC_MESSAGES/sphinx.po +2631 -2526
- sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lt/LC_MESSAGES/sphinx.po +2214 -2109
- sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lv/LC_MESSAGES/sphinx.po +2218 -2113
- sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/mk/LC_MESSAGES/sphinx.po +2088 -1983
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2247 -2142
- sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ne/LC_MESSAGES/sphinx.po +2227 -2122
- sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nl/LC_MESSAGES/sphinx.po +2316 -2211
- sphinx/locale/pl/LC_MESSAGES/sphinx.js +2 -2
- sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pl/LC_MESSAGES/sphinx.po +2442 -2336
- sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2657 -2552
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2243 -2138
- sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ro/LC_MESSAGES/sphinx.po +2244 -2139
- sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
- sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ru/LC_MESSAGES/sphinx.po +2660 -2555
- sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/si/LC_MESSAGES/sphinx.po +2134 -2029
- sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sk/LC_MESSAGES/sphinx.po +2614 -2509
- sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sl/LC_MESSAGES/sphinx.po +2167 -2062
- sphinx/locale/sphinx.pot +2069 -1964
- sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sq/LC_MESSAGES/sphinx.po +2661 -2556
- sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr/LC_MESSAGES/sphinx.po +2213 -2108
- sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.po +2229 -2124
- sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/te/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/tr/LC_MESSAGES/sphinx.po +2608 -2503
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2167 -2062
- sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ur/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/vi/LC_MESSAGES/sphinx.po +2204 -2099
- sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/yue/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2659 -2554
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/parsers.py +8 -7
- sphinx/project.py +2 -2
- sphinx/pycode/__init__.py +31 -21
- sphinx/pycode/ast.py +6 -3
- sphinx/pycode/parser.py +14 -8
- sphinx/pygments_styles.py +4 -5
- sphinx/registry.py +192 -92
- sphinx/roles.py +58 -7
- sphinx/search/__init__.py +75 -54
- sphinx/search/en.py +11 -13
- sphinx/search/fi.py +1 -1
- sphinx/search/ja.py +8 -6
- sphinx/search/nl.py +1 -1
- sphinx/search/zh.py +19 -21
- sphinx/testing/fixtures.py +26 -29
- sphinx/testing/path.py +26 -62
- sphinx/testing/restructuredtext.py +14 -8
- sphinx/testing/util.py +21 -19
- sphinx/texinputs/make.bat.jinja +50 -50
- sphinx/texinputs/sphinx.sty +4 -3
- sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
- sphinx/texinputs/sphinxlatexobjects.sty +29 -10
- sphinx/themes/basic/static/searchtools.js +8 -5
- sphinx/theming.py +49 -61
- sphinx/transforms/__init__.py +17 -38
- sphinx/transforms/compact_bullet_list.py +5 -3
- sphinx/transforms/i18n.py +8 -21
- sphinx/transforms/post_transforms/__init__.py +142 -93
- sphinx/transforms/post_transforms/code.py +5 -5
- sphinx/transforms/post_transforms/images.py +28 -24
- sphinx/transforms/references.py +3 -1
- sphinx/util/__init__.py +109 -60
- sphinx/util/_files.py +39 -23
- sphinx/util/_importer.py +4 -1
- sphinx/util/_inventory_file_reader.py +76 -0
- sphinx/util/_io.py +2 -2
- sphinx/util/_lines.py +6 -3
- sphinx/util/_pathlib.py +40 -2
- sphinx/util/build_phase.py +2 -0
- sphinx/util/cfamily.py +19 -14
- sphinx/util/console.py +44 -179
- sphinx/util/display.py +9 -10
- sphinx/util/docfields.py +140 -122
- sphinx/util/docstrings.py +1 -1
- sphinx/util/docutils.py +118 -77
- sphinx/util/fileutil.py +25 -26
- sphinx/util/http_date.py +2 -0
- sphinx/util/i18n.py +77 -64
- sphinx/util/images.py +8 -6
- sphinx/util/inspect.py +147 -38
- sphinx/util/inventory.py +215 -116
- sphinx/util/logging.py +33 -33
- sphinx/util/matching.py +12 -4
- sphinx/util/nodes.py +18 -13
- sphinx/util/osutil.py +38 -39
- sphinx/util/parallel.py +22 -13
- sphinx/util/parsing.py +2 -1
- sphinx/util/png.py +6 -2
- sphinx/util/requests.py +33 -2
- sphinx/util/rst.py +3 -2
- sphinx/util/tags.py +1 -1
- sphinx/util/template.py +18 -10
- sphinx/util/texescape.py +8 -6
- sphinx/util/typing.py +148 -122
- sphinx/versioning.py +3 -3
- sphinx/writers/html.py +3 -1
- sphinx/writers/html5.py +63 -52
- sphinx/writers/latex.py +83 -67
- sphinx/writers/manpage.py +19 -38
- sphinx/writers/texinfo.py +47 -47
- sphinx/writers/text.py +50 -32
- sphinx/writers/xml.py +11 -8
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/LICENSE.rst +1 -1
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/METADATA +25 -15
- sphinx-8.2.0.dist-info/RECORD +606 -0
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/WHEEL +1 -1
- sphinx/builders/html/transforms.py +0 -90
- sphinx/ext/apidoc.py +0 -721
- sphinx/util/exceptions.py +0 -74
- sphinx-8.1.2.dist-info/RECORD +0 -598
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/entry_points.txt +0 -0
sphinx/ext/githubpages.py
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import contextlib
|
|
6
|
-
import os
|
|
7
5
|
import urllib.parse
|
|
8
6
|
from typing import TYPE_CHECKING
|
|
9
7
|
|
|
@@ -39,20 +37,22 @@ def create_nojekyll_and_cname(app: Sphinx, env: BuildEnvironment) -> None:
|
|
|
39
37
|
return
|
|
40
38
|
|
|
41
39
|
app.builder.outdir.joinpath('.nojekyll').touch()
|
|
42
|
-
cname_path =
|
|
40
|
+
cname_path = app.builder.outdir / 'CNAME'
|
|
43
41
|
|
|
44
42
|
domain = _get_domain_from_url(app.config.html_baseurl)
|
|
45
43
|
# Filter out GitHub Pages domains, as they do not require CNAME files.
|
|
46
|
-
if domain and not domain.endswith(
|
|
47
|
-
with open(cname_path, 'w', encoding=
|
|
44
|
+
if domain and not domain.endswith('.github.io'):
|
|
45
|
+
with open(cname_path, 'w', encoding='utf-8') as f:
|
|
48
46
|
# NOTE: don't write a trailing newline. The `CNAME` file that's
|
|
49
47
|
# auto-generated by the GitHub UI doesn't have one.
|
|
50
48
|
f.write(domain)
|
|
51
49
|
else:
|
|
52
|
-
|
|
53
|
-
os.unlink(cname_path)
|
|
50
|
+
cname_path.unlink(missing_ok=True)
|
|
54
51
|
|
|
55
52
|
|
|
56
53
|
def setup(app: Sphinx) -> ExtensionMetadata:
|
|
57
54
|
app.connect('env-updated', create_nojekyll_and_cname)
|
|
58
|
-
return {
|
|
55
|
+
return {
|
|
56
|
+
'version': sphinx.__display_version__,
|
|
57
|
+
'parallel_read_safe': True,
|
|
58
|
+
}
|
sphinx/ext/graphviz.py
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
"""Allow graphviz-formatted graphs to be included inline in generated documents.
|
|
2
|
-
"""
|
|
1
|
+
"""Allow graphviz-formatted graphs to be included inline in generated documents."""
|
|
3
2
|
|
|
4
3
|
from __future__ import annotations
|
|
5
4
|
|
|
6
|
-
import
|
|
5
|
+
import os.path
|
|
7
6
|
import re
|
|
8
7
|
import subprocess
|
|
9
8
|
import xml.etree.ElementTree as ET
|
|
10
9
|
from hashlib import sha1
|
|
11
10
|
from itertools import chain
|
|
12
|
-
from os import path
|
|
13
11
|
from subprocess import CalledProcessError
|
|
14
|
-
from typing import TYPE_CHECKING
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
15
13
|
from urllib.parse import urlsplit, urlunsplit
|
|
16
14
|
|
|
17
15
|
from docutils import nodes
|
|
@@ -21,12 +19,14 @@ import sphinx
|
|
|
21
19
|
from sphinx.errors import SphinxError
|
|
22
20
|
from sphinx.locale import _, __
|
|
23
21
|
from sphinx.util import logging
|
|
22
|
+
from sphinx.util._pathlib import _StrPath
|
|
24
23
|
from sphinx.util.docutils import SphinxDirective
|
|
25
24
|
from sphinx.util.i18n import search_image_for_language
|
|
26
25
|
from sphinx.util.nodes import set_source_info
|
|
27
|
-
from sphinx.util.osutil import ensuredir
|
|
28
26
|
|
|
29
27
|
if TYPE_CHECKING:
|
|
28
|
+
from typing import Any, ClassVar
|
|
29
|
+
|
|
30
30
|
from docutils.nodes import Node
|
|
31
31
|
|
|
32
32
|
from sphinx.application import Sphinx
|
|
@@ -62,14 +62,15 @@ class ClickableMapDefinition:
|
|
|
62
62
|
def parse(self, dot: str) -> None:
|
|
63
63
|
matched = self.maptag_re.match(self.content[0])
|
|
64
64
|
if not matched:
|
|
65
|
-
|
|
65
|
+
msg = f'Invalid clickable map file found: {self.filename}'
|
|
66
|
+
raise GraphvizError(msg)
|
|
66
67
|
|
|
67
68
|
self.id = matched.group(1)
|
|
68
69
|
if self.id == '%3':
|
|
69
70
|
# graphviz generates wrong ID if graph name not specified
|
|
70
71
|
# https://gitlab.com/graphviz/graphviz/issues/1327
|
|
71
72
|
hashed = sha1(dot.encode(), usedforsecurity=False).hexdigest()
|
|
72
|
-
self.id = 'grapviz
|
|
73
|
+
self.id = f'grapviz{hashed[-10:]}'
|
|
73
74
|
self.content[0] = self.content[0].replace('%3', self.id)
|
|
74
75
|
|
|
75
76
|
for line in self.content:
|
|
@@ -91,7 +92,9 @@ class graphviz(nodes.General, nodes.Inline, nodes.Element):
|
|
|
91
92
|
pass
|
|
92
93
|
|
|
93
94
|
|
|
94
|
-
def figure_wrapper(
|
|
95
|
+
def figure_wrapper(
|
|
96
|
+
directive: SphinxDirective, node: graphviz, caption: str
|
|
97
|
+
) -> nodes.figure:
|
|
95
98
|
figure_node = nodes.figure('', node)
|
|
96
99
|
if 'align' in node:
|
|
97
100
|
figure_node['align'] = node.attributes.pop('align')
|
|
@@ -109,9 +112,7 @@ def align_spec(argument: Any) -> str:
|
|
|
109
112
|
|
|
110
113
|
|
|
111
114
|
class Graphviz(SphinxDirective):
|
|
112
|
-
"""
|
|
113
|
-
Directive to insert arbitrary dot markup.
|
|
114
|
-
"""
|
|
115
|
+
"""Directive to insert arbitrary dot markup."""
|
|
115
116
|
|
|
116
117
|
has_content = True
|
|
117
118
|
required_arguments = 0
|
|
@@ -131,9 +132,15 @@ class Graphviz(SphinxDirective):
|
|
|
131
132
|
if self.arguments:
|
|
132
133
|
document = self.state.document
|
|
133
134
|
if self.content:
|
|
134
|
-
return [
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
return [
|
|
136
|
+
document.reporter.warning(
|
|
137
|
+
__(
|
|
138
|
+
'Graphviz directive cannot have both content and '
|
|
139
|
+
'a filename argument'
|
|
140
|
+
),
|
|
141
|
+
line=self.lineno,
|
|
142
|
+
)
|
|
143
|
+
]
|
|
137
144
|
argument = search_image_for_language(self.arguments[0], self.env)
|
|
138
145
|
rel_filename, filename = self.env.relfn2path(argument)
|
|
139
146
|
self.env.note_dependency(rel_filename)
|
|
@@ -141,16 +148,23 @@ class Graphviz(SphinxDirective):
|
|
|
141
148
|
with open(filename, encoding='utf-8') as fp:
|
|
142
149
|
dotcode = fp.read()
|
|
143
150
|
except OSError:
|
|
144
|
-
return [
|
|
145
|
-
|
|
146
|
-
|
|
151
|
+
return [
|
|
152
|
+
document.reporter.warning(
|
|
153
|
+
__('External Graphviz file %r not found or reading it failed')
|
|
154
|
+
% filename,
|
|
155
|
+
line=self.lineno,
|
|
156
|
+
)
|
|
157
|
+
]
|
|
147
158
|
else:
|
|
148
159
|
dotcode = '\n'.join(self.content)
|
|
149
160
|
rel_filename = None
|
|
150
161
|
if not dotcode.strip():
|
|
151
|
-
return [
|
|
152
|
-
|
|
153
|
-
|
|
162
|
+
return [
|
|
163
|
+
self.state_machine.reporter.warning(
|
|
164
|
+
__('Ignoring "graphviz" directive without content.'),
|
|
165
|
+
line=self.lineno,
|
|
166
|
+
)
|
|
167
|
+
]
|
|
154
168
|
node = graphviz()
|
|
155
169
|
node['code'] = dotcode
|
|
156
170
|
node['options'] = {'docname': self.env.docname}
|
|
@@ -178,9 +192,7 @@ class Graphviz(SphinxDirective):
|
|
|
178
192
|
|
|
179
193
|
|
|
180
194
|
class GraphvizSimple(SphinxDirective):
|
|
181
|
-
"""
|
|
182
|
-
Directive to insert arbitrary dot markup.
|
|
183
|
-
"""
|
|
195
|
+
"""Directive to insert arbitrary dot markup."""
|
|
184
196
|
|
|
185
197
|
has_content = True
|
|
186
198
|
required_arguments = 1
|
|
@@ -198,8 +210,8 @@ class GraphvizSimple(SphinxDirective):
|
|
|
198
210
|
|
|
199
211
|
def run(self) -> list[Node]:
|
|
200
212
|
node = graphviz()
|
|
201
|
-
|
|
202
|
-
|
|
213
|
+
dot_code = '\n'.join(self.content)
|
|
214
|
+
node['code'] = f'{self.name} {self.arguments[0]} {{\n{dot_code}\n}}\n'
|
|
203
215
|
node['options'] = {'docname': self.env.docname}
|
|
204
216
|
if 'graphviz_dot' in self.options:
|
|
205
217
|
node['options']['graphviz_dot'] = self.options['graphviz_dot']
|
|
@@ -221,9 +233,12 @@ class GraphvizSimple(SphinxDirective):
|
|
|
221
233
|
return [figure]
|
|
222
234
|
|
|
223
235
|
|
|
224
|
-
def fix_svg_relative_paths(
|
|
225
|
-
|
|
236
|
+
def fix_svg_relative_paths(
|
|
237
|
+
self: HTML5Translator | LaTeXTranslator | TexinfoTranslator,
|
|
238
|
+
filepath: str | os.PathLike[str],
|
|
239
|
+
) -> None:
|
|
226
240
|
"""Change relative links in generated svg files to be relative to imgpath."""
|
|
241
|
+
env = self.builder.env
|
|
227
242
|
tree = ET.parse(filepath) # NoQA: S314
|
|
228
243
|
root = tree.getroot()
|
|
229
244
|
ns = {'svg': 'http://www.w3.org/2000/svg', 'xlink': 'http://www.w3.org/1999/xlink'}
|
|
@@ -239,15 +254,15 @@ def fix_svg_relative_paths(self: HTML5Translator | LaTeXTranslator | TexinfoTran
|
|
|
239
254
|
# not a relative link
|
|
240
255
|
continue
|
|
241
256
|
|
|
242
|
-
docname =
|
|
257
|
+
docname = env.path2doc(self.document['source'])
|
|
243
258
|
if docname is None:
|
|
244
259
|
# This shouldn't happen!
|
|
245
260
|
continue
|
|
246
|
-
doc_dir = self.builder.
|
|
261
|
+
doc_dir = self.builder.outdir.joinpath(docname).resolve().parent
|
|
247
262
|
|
|
248
263
|
old_path = doc_dir / rel_uri
|
|
249
264
|
img_path = doc_dir / self.builder.imgpath
|
|
250
|
-
new_path = path.relpath(old_path, start=img_path)
|
|
265
|
+
new_path = os.path.relpath(old_path, start=img_path)
|
|
251
266
|
modified_url = urlunsplit((scheme, hostname, new_path, query, fragment))
|
|
252
267
|
|
|
253
268
|
element.set(href_name, modified_url)
|
|
@@ -257,61 +272,78 @@ def fix_svg_relative_paths(self: HTML5Translator | LaTeXTranslator | TexinfoTran
|
|
|
257
272
|
tree.write(filepath)
|
|
258
273
|
|
|
259
274
|
|
|
260
|
-
def render_dot(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
275
|
+
def render_dot(
|
|
276
|
+
self: HTML5Translator | LaTeXTranslator | TexinfoTranslator,
|
|
277
|
+
code: str,
|
|
278
|
+
options: dict[str, Any],
|
|
279
|
+
format: str,
|
|
280
|
+
prefix: str = 'graphviz',
|
|
281
|
+
filename: str | None = None,
|
|
282
|
+
) -> tuple[_StrPath | None, _StrPath | None]:
|
|
264
283
|
"""Render graphviz code into a PNG or PDF output file."""
|
|
265
284
|
graphviz_dot = options.get('graphviz_dot', self.builder.config.graphviz_dot)
|
|
266
285
|
if not graphviz_dot:
|
|
267
286
|
raise GraphvizError(
|
|
268
287
|
__('graphviz_dot executable path must be set! %r') % graphviz_dot,
|
|
269
288
|
)
|
|
270
|
-
hashkey = (
|
|
271
|
-
|
|
289
|
+
hashkey = ''.join((
|
|
290
|
+
code,
|
|
291
|
+
str(options),
|
|
292
|
+
str(graphviz_dot),
|
|
293
|
+
str(self.builder.config.graphviz_dot_args),
|
|
294
|
+
)).encode()
|
|
272
295
|
|
|
273
296
|
fname = f'{prefix}-{sha1(hashkey, usedforsecurity=False).hexdigest()}.{format}'
|
|
274
|
-
relfn =
|
|
275
|
-
outfn =
|
|
297
|
+
relfn = _StrPath(self.builder.imgpath, fname)
|
|
298
|
+
outfn = self.builder.outdir / self.builder.imagedir / fname
|
|
276
299
|
|
|
277
|
-
if
|
|
300
|
+
if outfn.is_file():
|
|
278
301
|
return relfn, outfn
|
|
279
302
|
|
|
280
|
-
if (
|
|
281
|
-
self.builder._graphviz_warned_dot.get(graphviz_dot)):
|
|
303
|
+
if getattr(self.builder, '_graphviz_warned_dot', {}).get(graphviz_dot):
|
|
282
304
|
return None, None
|
|
283
305
|
|
|
284
|
-
|
|
306
|
+
outfn.parent.mkdir(parents=True, exist_ok=True)
|
|
285
307
|
|
|
286
308
|
dot_args = [graphviz_dot]
|
|
287
309
|
dot_args.extend(self.builder.config.graphviz_dot_args)
|
|
288
|
-
dot_args.extend(['-T'
|
|
310
|
+
dot_args.extend([f'-T{format}', f'-o{outfn}'])
|
|
289
311
|
|
|
290
312
|
docname = options.get('docname', 'index')
|
|
291
313
|
if filename:
|
|
292
|
-
cwd =
|
|
314
|
+
cwd = (self.builder.srcdir / filename).parent
|
|
293
315
|
else:
|
|
294
|
-
cwd =
|
|
316
|
+
cwd = (self.builder.srcdir / docname).parent
|
|
295
317
|
|
|
296
318
|
if format == 'png':
|
|
297
|
-
dot_args.extend(['-Tcmapx', '-o
|
|
319
|
+
dot_args.extend(['-Tcmapx', f'-o{outfn}.map'])
|
|
298
320
|
|
|
299
321
|
try:
|
|
300
|
-
ret = subprocess.run(
|
|
301
|
-
|
|
322
|
+
ret = subprocess.run(
|
|
323
|
+
dot_args, input=code.encode(), capture_output=True, cwd=cwd, check=True
|
|
324
|
+
)
|
|
302
325
|
except OSError:
|
|
303
|
-
logger.warning(
|
|
304
|
-
|
|
326
|
+
logger.warning(
|
|
327
|
+
__(
|
|
328
|
+
'dot command %r cannot be run (needed for graphviz '
|
|
329
|
+
'output), check the graphviz_dot setting'
|
|
330
|
+
),
|
|
331
|
+
graphviz_dot,
|
|
332
|
+
)
|
|
305
333
|
if not hasattr(self.builder, '_graphviz_warned_dot'):
|
|
306
334
|
self.builder._graphviz_warned_dot = {} # type: ignore[union-attr]
|
|
307
|
-
self.builder._graphviz_warned_dot[graphviz_dot] = True
|
|
335
|
+
self.builder._graphviz_warned_dot[graphviz_dot] = True # type: ignore[union-attr]
|
|
308
336
|
return None, None
|
|
309
337
|
except CalledProcessError as exc:
|
|
310
|
-
raise GraphvizError(
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
338
|
+
raise GraphvizError(
|
|
339
|
+
__('dot exited with error:\n[stderr]\n%r\n[stdout]\n%r')
|
|
340
|
+
% (exc.stderr, exc.stdout)
|
|
341
|
+
) from exc
|
|
342
|
+
if not outfn.is_file():
|
|
343
|
+
raise GraphvizError(
|
|
344
|
+
__('dot did not produce an output file:\n[stderr]\n%r\n[stdout]\n%r')
|
|
345
|
+
% (ret.stderr, ret.stdout)
|
|
346
|
+
)
|
|
315
347
|
|
|
316
348
|
if format == 'svg':
|
|
317
349
|
fix_svg_relative_paths(self, outfn)
|
|
@@ -319,15 +351,23 @@ def render_dot(self: HTML5Translator | LaTeXTranslator | TexinfoTranslator,
|
|
|
319
351
|
return relfn, outfn
|
|
320
352
|
|
|
321
353
|
|
|
322
|
-
def render_dot_html(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
354
|
+
def render_dot_html(
|
|
355
|
+
self: HTML5Translator,
|
|
356
|
+
node: graphviz,
|
|
357
|
+
code: str,
|
|
358
|
+
options: dict[str, Any],
|
|
359
|
+
prefix: str = 'graphviz',
|
|
360
|
+
imgcls: str | None = None,
|
|
361
|
+
alt: str | None = None,
|
|
362
|
+
filename: str | None = None,
|
|
363
|
+
) -> tuple[str, str]:
|
|
326
364
|
format = self.builder.config.graphviz_output_format
|
|
365
|
+
if format not in {'png', 'svg'}:
|
|
366
|
+
logger.warning(
|
|
367
|
+
__("graphviz_output_format must be either 'png' or 'svg', but is %r"),
|
|
368
|
+
format,
|
|
369
|
+
)
|
|
327
370
|
try:
|
|
328
|
-
if format not in ('png', 'svg'):
|
|
329
|
-
raise GraphvizError(__("graphviz_output_format must be one of 'png', "
|
|
330
|
-
"'svg', but is %r") % format)
|
|
331
371
|
fname, outfn = render_dot(self, code, options, format, prefix, filename)
|
|
332
372
|
except GraphvizError as exc:
|
|
333
373
|
logger.warning(__('dot code %r: %s'), code, exc)
|
|
@@ -339,34 +379,37 @@ def render_dot_html(self: HTML5Translator, node: graphviz, code: str, options: d
|
|
|
339
379
|
if fname is None:
|
|
340
380
|
self.body.append(self.encode(code))
|
|
341
381
|
else:
|
|
382
|
+
src = fname.as_posix()
|
|
342
383
|
if alt is None:
|
|
343
384
|
alt = node.get('alt', self.encode(code).strip())
|
|
344
385
|
if 'align' in node:
|
|
345
|
-
|
|
346
|
-
|
|
386
|
+
align = node['align']
|
|
387
|
+
self.body.append(f'<div align="{align}" class="align-{align}">')
|
|
347
388
|
if format == 'svg':
|
|
348
389
|
self.body.append('<div class="graphviz">')
|
|
349
|
-
self.body.append(
|
|
350
|
-
|
|
351
|
-
|
|
390
|
+
self.body.append(
|
|
391
|
+
f'<object data="{src}" type="image/svg+xml" class="{imgcls}">\n'
|
|
392
|
+
)
|
|
393
|
+
self.body.append(f'<p class="warning">{alt}</p>')
|
|
352
394
|
self.body.append('</object></div>\n')
|
|
353
395
|
else:
|
|
354
396
|
assert outfn is not None
|
|
355
|
-
with open(outfn
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
397
|
+
with open(f'{outfn}.map', encoding='utf-8') as mapfile:
|
|
398
|
+
map_content = mapfile.read()
|
|
399
|
+
imgmap = ClickableMapDefinition(f'{outfn}.map', map_content, dot=code)
|
|
400
|
+
if imgmap.clickable:
|
|
401
|
+
# has a map
|
|
402
|
+
self.body.append('<div class="graphviz">')
|
|
403
|
+
self.body.append(
|
|
404
|
+
f'<img src="{src}" alt="{alt}" usemap="#{imgmap.id}" class="{imgcls}" />'
|
|
405
|
+
)
|
|
406
|
+
self.body.append('</div>\n')
|
|
407
|
+
self.body.append(imgmap.generate_clickable_map())
|
|
408
|
+
else:
|
|
409
|
+
# nothing in image map
|
|
410
|
+
self.body.append('<div class="graphviz">')
|
|
411
|
+
self.body.append(f'<img src="{src}" alt="{alt}" class="{imgcls}" />')
|
|
412
|
+
self.body.append('</div>\n')
|
|
370
413
|
if 'align' in node:
|
|
371
414
|
self.body.append('</div>\n')
|
|
372
415
|
|
|
@@ -374,12 +417,19 @@ def render_dot_html(self: HTML5Translator, node: graphviz, code: str, options: d
|
|
|
374
417
|
|
|
375
418
|
|
|
376
419
|
def html_visit_graphviz(self: HTML5Translator, node: graphviz) -> None:
|
|
377
|
-
render_dot_html(
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
420
|
+
render_dot_html(
|
|
421
|
+
self, node, node['code'], node['options'], filename=node.get('filename')
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def render_dot_latex(
|
|
426
|
+
self: LaTeXTranslator,
|
|
427
|
+
node: graphviz,
|
|
428
|
+
code: str,
|
|
429
|
+
options: dict[str, Any],
|
|
430
|
+
prefix: str = 'graphviz',
|
|
431
|
+
filename: str | None = None,
|
|
432
|
+
) -> None:
|
|
383
433
|
try:
|
|
384
434
|
fname, outfn = render_dot(self, code, options, 'pdf', prefix, filename)
|
|
385
435
|
except GraphvizError as exc:
|
|
@@ -401,22 +451,29 @@ def render_dot_latex(self: LaTeXTranslator, node: graphviz, code: str,
|
|
|
401
451
|
elif node['align'] == 'center':
|
|
402
452
|
pre = r'{\hfill'
|
|
403
453
|
post = r'\hspace*{\fill}}'
|
|
404
|
-
self.body.append('\n
|
|
454
|
+
self.body.append(f'\n{pre}')
|
|
405
455
|
|
|
406
456
|
self.body.append(r'\sphinxincludegraphics[]{%s}' % fname)
|
|
407
457
|
|
|
408
458
|
if not is_inline:
|
|
409
|
-
self.body.append('
|
|
459
|
+
self.body.append(f'{post}\n')
|
|
410
460
|
|
|
411
461
|
raise nodes.SkipNode
|
|
412
462
|
|
|
413
463
|
|
|
414
464
|
def latex_visit_graphviz(self: LaTeXTranslator, node: graphviz) -> None:
|
|
415
|
-
render_dot_latex(
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
465
|
+
render_dot_latex(
|
|
466
|
+
self, node, node['code'], node['options'], filename=node.get('filename')
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def render_dot_texinfo(
|
|
471
|
+
self: TexinfoTranslator,
|
|
472
|
+
node: graphviz,
|
|
473
|
+
code: str,
|
|
474
|
+
options: dict[str, Any],
|
|
475
|
+
prefix: str = 'graphviz',
|
|
476
|
+
) -> None:
|
|
420
477
|
try:
|
|
421
478
|
fname, outfn = render_dot(self, code, options, 'png', prefix)
|
|
422
479
|
except GraphvizError as exc:
|
|
@@ -448,23 +505,32 @@ def man_visit_graphviz(self: ManualPageTranslator, node: graphviz) -> None:
|
|
|
448
505
|
|
|
449
506
|
|
|
450
507
|
def on_config_inited(_app: Sphinx, config: Config) -> None:
|
|
451
|
-
css_path =
|
|
452
|
-
config.html_static_path.append(css_path)
|
|
508
|
+
css_path = sphinx.package_dir.joinpath('templates', 'graphviz', 'graphviz.css')
|
|
509
|
+
config.html_static_path.append(str(css_path))
|
|
453
510
|
|
|
454
511
|
|
|
455
512
|
def setup(app: Sphinx) -> ExtensionMetadata:
|
|
456
|
-
app.add_node(
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
513
|
+
app.add_node(
|
|
514
|
+
graphviz,
|
|
515
|
+
html=(html_visit_graphviz, None),
|
|
516
|
+
latex=(latex_visit_graphviz, None),
|
|
517
|
+
texinfo=(texinfo_visit_graphviz, None),
|
|
518
|
+
text=(text_visit_graphviz, None),
|
|
519
|
+
man=(man_visit_graphviz, None),
|
|
520
|
+
)
|
|
462
521
|
app.add_directive('graphviz', Graphviz)
|
|
463
522
|
app.add_directive('graph', GraphvizSimple)
|
|
464
523
|
app.add_directive('digraph', GraphvizSimple)
|
|
465
|
-
app.add_config_value('graphviz_dot', 'dot', 'html')
|
|
466
|
-
app.add_config_value(
|
|
467
|
-
|
|
524
|
+
app.add_config_value('graphviz_dot', 'dot', 'html', types=frozenset({str}))
|
|
525
|
+
app.add_config_value(
|
|
526
|
+
'graphviz_dot_args', (), 'html', types=frozenset({list, tuple})
|
|
527
|
+
)
|
|
528
|
+
app.add_config_value(
|
|
529
|
+
'graphviz_output_format', 'png', 'html', types=frozenset({str})
|
|
530
|
+
)
|
|
468
531
|
app.add_css_file('graphviz.css')
|
|
469
532
|
app.connect('config-inited', on_config_inited)
|
|
470
|
-
return {
|
|
533
|
+
return {
|
|
534
|
+
'version': sphinx.__display_version__,
|
|
535
|
+
'parallel_read_safe': True,
|
|
536
|
+
}
|
sphinx/ext/ifconfig.py
CHANGED
|
@@ -16,7 +16,7 @@ namespace of the project configuration (that is, all variables from
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
from typing import TYPE_CHECKING
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
20
|
|
|
21
21
|
from docutils import nodes
|
|
22
22
|
|
|
@@ -24,6 +24,8 @@ import sphinx
|
|
|
24
24
|
from sphinx.util.docutils import SphinxDirective
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
+
from typing import ClassVar
|
|
28
|
+
|
|
27
29
|
from docutils.nodes import Node
|
|
28
30
|
|
|
29
31
|
from sphinx.application import Sphinx
|
|
@@ -35,7 +37,6 @@ class ifconfig(nodes.Element):
|
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
class IfConfig(SphinxDirective):
|
|
38
|
-
|
|
39
40
|
has_content = True
|
|
40
41
|
required_arguments = 1
|
|
41
42
|
optional_arguments = 0
|
|
@@ -61,10 +62,11 @@ def process_ifconfig_nodes(app: Sphinx, doctree: nodes.document, docname: str) -
|
|
|
61
62
|
except Exception as err:
|
|
62
63
|
# handle exceptions in a clean fashion
|
|
63
64
|
from traceback import format_exception_only
|
|
65
|
+
|
|
64
66
|
msg = ''.join(format_exception_only(err.__class__, err))
|
|
65
|
-
newnode = doctree.reporter.error(
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
newnode = doctree.reporter.error(
|
|
68
|
+
f'Exception occurred in ifconfig expression: \n{msg}', base_node=node
|
|
69
|
+
)
|
|
68
70
|
node.replace_self(newnode)
|
|
69
71
|
else:
|
|
70
72
|
if not res:
|
|
@@ -77,4 +79,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
|
|
|
77
79
|
app.add_node(ifconfig)
|
|
78
80
|
app.add_directive('ifconfig', IfConfig)
|
|
79
81
|
app.connect('doctree-resolved', process_ifconfig_nodes)
|
|
80
|
-
return {
|
|
82
|
+
return {
|
|
83
|
+
'version': sphinx.__display_version__,
|
|
84
|
+
'parallel_read_safe': True,
|
|
85
|
+
}
|