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.

Files changed (328) hide show
  1. sphinx/__init__.py +8 -4
  2. sphinx/__main__.py +2 -0
  3. sphinx/_cli/__init__.py +2 -5
  4. sphinx/_cli/util/colour.py +34 -11
  5. sphinx/_cli/util/errors.py +128 -61
  6. sphinx/addnodes.py +51 -35
  7. sphinx/application.py +362 -230
  8. sphinx/builders/__init__.py +87 -64
  9. sphinx/builders/_epub_base.py +65 -56
  10. sphinx/builders/changes.py +17 -23
  11. sphinx/builders/dirhtml.py +8 -13
  12. sphinx/builders/epub3.py +70 -38
  13. sphinx/builders/gettext.py +93 -73
  14. sphinx/builders/html/__init__.py +240 -186
  15. sphinx/builders/html/_assets.py +9 -2
  16. sphinx/builders/html/_build_info.py +3 -0
  17. sphinx/builders/latex/__init__.py +64 -54
  18. sphinx/builders/latex/constants.py +14 -11
  19. sphinx/builders/latex/nodes.py +2 -0
  20. sphinx/builders/latex/theming.py +8 -9
  21. sphinx/builders/latex/transforms.py +7 -5
  22. sphinx/builders/linkcheck.py +193 -149
  23. sphinx/builders/manpage.py +17 -17
  24. sphinx/builders/singlehtml.py +28 -16
  25. sphinx/builders/texinfo.py +28 -21
  26. sphinx/builders/text.py +10 -15
  27. sphinx/builders/xml.py +10 -19
  28. sphinx/cmd/build.py +49 -119
  29. sphinx/cmd/make_mode.py +35 -31
  30. sphinx/cmd/quickstart.py +78 -62
  31. sphinx/config.py +265 -163
  32. sphinx/directives/__init__.py +51 -54
  33. sphinx/directives/admonitions.py +107 -0
  34. sphinx/directives/code.py +24 -19
  35. sphinx/directives/other.py +21 -42
  36. sphinx/directives/patches.py +28 -16
  37. sphinx/domains/__init__.py +54 -31
  38. sphinx/domains/_domains_container.py +22 -17
  39. sphinx/domains/_index.py +5 -8
  40. sphinx/domains/c/__init__.py +366 -245
  41. sphinx/domains/c/_ast.py +378 -256
  42. sphinx/domains/c/_ids.py +89 -31
  43. sphinx/domains/c/_parser.py +283 -214
  44. sphinx/domains/c/_symbol.py +269 -198
  45. sphinx/domains/changeset.py +39 -24
  46. sphinx/domains/citation.py +54 -24
  47. sphinx/domains/cpp/__init__.py +517 -362
  48. sphinx/domains/cpp/_ast.py +999 -682
  49. sphinx/domains/cpp/_ids.py +133 -65
  50. sphinx/domains/cpp/_parser.py +746 -588
  51. sphinx/domains/cpp/_symbol.py +692 -489
  52. sphinx/domains/index.py +10 -8
  53. sphinx/domains/javascript.py +152 -74
  54. sphinx/domains/math.py +50 -40
  55. sphinx/domains/python/__init__.py +402 -211
  56. sphinx/domains/python/_annotations.py +134 -61
  57. sphinx/domains/python/_object.py +155 -68
  58. sphinx/domains/rst.py +94 -49
  59. sphinx/domains/std/__init__.py +510 -249
  60. sphinx/environment/__init__.py +345 -61
  61. sphinx/environment/adapters/asset.py +7 -1
  62. sphinx/environment/adapters/indexentries.py +15 -20
  63. sphinx/environment/adapters/toctree.py +19 -9
  64. sphinx/environment/collectors/__init__.py +3 -1
  65. sphinx/environment/collectors/asset.py +18 -15
  66. sphinx/environment/collectors/dependencies.py +8 -10
  67. sphinx/environment/collectors/metadata.py +6 -4
  68. sphinx/environment/collectors/title.py +3 -1
  69. sphinx/environment/collectors/toctree.py +4 -4
  70. sphinx/errors.py +1 -3
  71. sphinx/events.py +4 -4
  72. sphinx/ext/apidoc/__init__.py +66 -0
  73. sphinx/ext/apidoc/__main__.py +9 -0
  74. sphinx/ext/apidoc/_cli.py +356 -0
  75. sphinx/ext/apidoc/_extension.py +262 -0
  76. sphinx/ext/apidoc/_generate.py +356 -0
  77. sphinx/ext/apidoc/_shared.py +99 -0
  78. sphinx/ext/autodoc/__init__.py +837 -483
  79. sphinx/ext/autodoc/directive.py +57 -21
  80. sphinx/ext/autodoc/importer.py +184 -67
  81. sphinx/ext/autodoc/mock.py +25 -10
  82. sphinx/ext/autodoc/preserve_defaults.py +17 -9
  83. sphinx/ext/autodoc/type_comment.py +56 -29
  84. sphinx/ext/autodoc/typehints.py +49 -26
  85. sphinx/ext/autosectionlabel.py +28 -11
  86. sphinx/ext/autosummary/__init__.py +281 -142
  87. sphinx/ext/autosummary/generate.py +121 -51
  88. sphinx/ext/coverage.py +152 -91
  89. sphinx/ext/doctest.py +169 -101
  90. sphinx/ext/duration.py +12 -6
  91. sphinx/ext/extlinks.py +33 -21
  92. sphinx/ext/githubpages.py +8 -8
  93. sphinx/ext/graphviz.py +175 -109
  94. sphinx/ext/ifconfig.py +11 -6
  95. sphinx/ext/imgconverter.py +48 -25
  96. sphinx/ext/imgmath.py +127 -97
  97. sphinx/ext/inheritance_diagram.py +177 -103
  98. sphinx/ext/intersphinx/__init__.py +22 -13
  99. sphinx/ext/intersphinx/__main__.py +3 -1
  100. sphinx/ext/intersphinx/_cli.py +18 -14
  101. sphinx/ext/intersphinx/_load.py +91 -82
  102. sphinx/ext/intersphinx/_resolve.py +108 -74
  103. sphinx/ext/intersphinx/_shared.py +2 -2
  104. sphinx/ext/linkcode.py +28 -12
  105. sphinx/ext/mathjax.py +60 -29
  106. sphinx/ext/napoleon/__init__.py +19 -7
  107. sphinx/ext/napoleon/docstring.py +229 -231
  108. sphinx/ext/todo.py +44 -49
  109. sphinx/ext/viewcode.py +105 -57
  110. sphinx/extension.py +3 -1
  111. sphinx/highlighting.py +13 -7
  112. sphinx/io.py +9 -13
  113. sphinx/jinja2glue.py +29 -26
  114. sphinx/locale/__init__.py +8 -9
  115. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  116. sphinx/locale/ar/LC_MESSAGES/sphinx.po +2155 -2050
  117. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  118. sphinx/locale/bg/LC_MESSAGES/sphinx.po +2045 -1940
  119. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  120. sphinx/locale/bn/LC_MESSAGES/sphinx.po +2175 -2070
  121. sphinx/locale/ca/LC_MESSAGES/sphinx.js +3 -3
  122. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/ca/LC_MESSAGES/sphinx.po +2690 -2585
  124. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.js +63 -0
  125. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.mo +0 -0
  126. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.po +4216 -0
  127. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  128. sphinx/locale/cak/LC_MESSAGES/sphinx.po +2096 -1991
  129. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  130. sphinx/locale/cs/LC_MESSAGES/sphinx.po +2248 -2143
  131. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  132. sphinx/locale/cy/LC_MESSAGES/sphinx.po +2201 -2096
  133. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  134. sphinx/locale/da/LC_MESSAGES/sphinx.po +2282 -2177
  135. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  136. sphinx/locale/de/LC_MESSAGES/sphinx.po +2261 -2156
  137. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  138. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2045 -1940
  139. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  140. sphinx/locale/el/LC_MESSAGES/sphinx.po +2604 -2499
  141. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  142. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2045 -1940
  143. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2045 -1940
  145. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2631 -2526
  147. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  148. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2045 -1940
  149. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  150. sphinx/locale/eo/LC_MESSAGES/sphinx.po +2078 -1973
  151. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/es/LC_MESSAGES/sphinx.po +2633 -2528
  153. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2045 -1940
  155. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/et/LC_MESSAGES/sphinx.po +2449 -2344
  157. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/eu/LC_MESSAGES/sphinx.po +2241 -2136
  159. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/fa/LC_MESSAGES/sphinx.po +504 -500
  161. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/fi/LC_MESSAGES/sphinx.po +499 -495
  163. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/fr/LC_MESSAGES/sphinx.po +513 -509
  165. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +499 -495
  167. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/gl/LC_MESSAGES/sphinx.po +2644 -2539
  169. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/he/LC_MESSAGES/sphinx.po +499 -495
  171. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/hi/LC_MESSAGES/sphinx.po +504 -500
  173. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +499 -495
  175. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/hr/LC_MESSAGES/sphinx.po +501 -497
  177. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/hu/LC_MESSAGES/sphinx.po +499 -495
  179. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  180. sphinx/locale/id/LC_MESSAGES/sphinx.po +2609 -2504
  181. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  182. sphinx/locale/is/LC_MESSAGES/sphinx.po +499 -495
  183. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  184. sphinx/locale/it/LC_MESSAGES/sphinx.po +2265 -2160
  185. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  186. sphinx/locale/ja/LC_MESSAGES/sphinx.po +2621 -2516
  187. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  188. sphinx/locale/ka/LC_MESSAGES/sphinx.po +2567 -2462
  189. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  190. sphinx/locale/ko/LC_MESSAGES/sphinx.po +2631 -2526
  191. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  192. sphinx/locale/lt/LC_MESSAGES/sphinx.po +2214 -2109
  193. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  194. sphinx/locale/lv/LC_MESSAGES/sphinx.po +2218 -2113
  195. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  196. sphinx/locale/mk/LC_MESSAGES/sphinx.po +2088 -1983
  197. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  198. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2247 -2142
  199. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  200. sphinx/locale/ne/LC_MESSAGES/sphinx.po +2227 -2122
  201. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  202. sphinx/locale/nl/LC_MESSAGES/sphinx.po +2316 -2211
  203. sphinx/locale/pl/LC_MESSAGES/sphinx.js +2 -2
  204. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  205. sphinx/locale/pl/LC_MESSAGES/sphinx.po +2442 -2336
  206. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/pt/LC_MESSAGES/sphinx.po +2045 -1940
  208. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2657 -2552
  210. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2243 -2138
  212. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/ro/LC_MESSAGES/sphinx.po +2244 -2139
  214. sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
  215. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  216. sphinx/locale/ru/LC_MESSAGES/sphinx.po +2660 -2555
  217. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  218. sphinx/locale/si/LC_MESSAGES/sphinx.po +2134 -2029
  219. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  220. sphinx/locale/sk/LC_MESSAGES/sphinx.po +2614 -2509
  221. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  222. sphinx/locale/sl/LC_MESSAGES/sphinx.po +2167 -2062
  223. sphinx/locale/sphinx.pot +2069 -1964
  224. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  225. sphinx/locale/sq/LC_MESSAGES/sphinx.po +2661 -2556
  226. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  227. sphinx/locale/sr/LC_MESSAGES/sphinx.po +2213 -2108
  228. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  229. sphinx/locale/sv/LC_MESSAGES/sphinx.po +2229 -2124
  230. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  231. sphinx/locale/te/LC_MESSAGES/sphinx.po +2045 -1940
  232. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  233. sphinx/locale/tr/LC_MESSAGES/sphinx.po +2608 -2503
  234. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  235. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2167 -2062
  236. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  237. sphinx/locale/ur/LC_MESSAGES/sphinx.po +2045 -1940
  238. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  239. sphinx/locale/vi/LC_MESSAGES/sphinx.po +2204 -2099
  240. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  241. sphinx/locale/yue/LC_MESSAGES/sphinx.po +2045 -1940
  242. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  243. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2045 -1940
  244. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  245. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2659 -2554
  246. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  247. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2045 -1940
  248. sphinx/parsers.py +8 -7
  249. sphinx/project.py +2 -2
  250. sphinx/pycode/__init__.py +31 -21
  251. sphinx/pycode/ast.py +6 -3
  252. sphinx/pycode/parser.py +14 -8
  253. sphinx/pygments_styles.py +4 -5
  254. sphinx/registry.py +192 -92
  255. sphinx/roles.py +58 -7
  256. sphinx/search/__init__.py +75 -54
  257. sphinx/search/en.py +11 -13
  258. sphinx/search/fi.py +1 -1
  259. sphinx/search/ja.py +8 -6
  260. sphinx/search/nl.py +1 -1
  261. sphinx/search/zh.py +19 -21
  262. sphinx/testing/fixtures.py +26 -29
  263. sphinx/testing/path.py +26 -62
  264. sphinx/testing/restructuredtext.py +14 -8
  265. sphinx/testing/util.py +21 -19
  266. sphinx/texinputs/make.bat.jinja +50 -50
  267. sphinx/texinputs/sphinx.sty +4 -3
  268. sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
  269. sphinx/texinputs/sphinxlatexobjects.sty +29 -10
  270. sphinx/themes/basic/static/searchtools.js +8 -5
  271. sphinx/theming.py +49 -61
  272. sphinx/transforms/__init__.py +17 -38
  273. sphinx/transforms/compact_bullet_list.py +5 -3
  274. sphinx/transforms/i18n.py +8 -21
  275. sphinx/transforms/post_transforms/__init__.py +142 -93
  276. sphinx/transforms/post_transforms/code.py +5 -5
  277. sphinx/transforms/post_transforms/images.py +28 -24
  278. sphinx/transforms/references.py +3 -1
  279. sphinx/util/__init__.py +109 -60
  280. sphinx/util/_files.py +39 -23
  281. sphinx/util/_importer.py +4 -1
  282. sphinx/util/_inventory_file_reader.py +76 -0
  283. sphinx/util/_io.py +2 -2
  284. sphinx/util/_lines.py +6 -3
  285. sphinx/util/_pathlib.py +40 -2
  286. sphinx/util/build_phase.py +2 -0
  287. sphinx/util/cfamily.py +19 -14
  288. sphinx/util/console.py +44 -179
  289. sphinx/util/display.py +9 -10
  290. sphinx/util/docfields.py +140 -122
  291. sphinx/util/docstrings.py +1 -1
  292. sphinx/util/docutils.py +118 -77
  293. sphinx/util/fileutil.py +25 -26
  294. sphinx/util/http_date.py +2 -0
  295. sphinx/util/i18n.py +77 -64
  296. sphinx/util/images.py +8 -6
  297. sphinx/util/inspect.py +147 -38
  298. sphinx/util/inventory.py +215 -116
  299. sphinx/util/logging.py +33 -33
  300. sphinx/util/matching.py +12 -4
  301. sphinx/util/nodes.py +18 -13
  302. sphinx/util/osutil.py +38 -39
  303. sphinx/util/parallel.py +22 -13
  304. sphinx/util/parsing.py +2 -1
  305. sphinx/util/png.py +6 -2
  306. sphinx/util/requests.py +33 -2
  307. sphinx/util/rst.py +3 -2
  308. sphinx/util/tags.py +1 -1
  309. sphinx/util/template.py +18 -10
  310. sphinx/util/texescape.py +8 -6
  311. sphinx/util/typing.py +148 -122
  312. sphinx/versioning.py +3 -3
  313. sphinx/writers/html.py +3 -1
  314. sphinx/writers/html5.py +63 -52
  315. sphinx/writers/latex.py +83 -67
  316. sphinx/writers/manpage.py +19 -38
  317. sphinx/writers/texinfo.py +47 -47
  318. sphinx/writers/text.py +50 -32
  319. sphinx/writers/xml.py +11 -8
  320. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/LICENSE.rst +1 -1
  321. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/METADATA +25 -15
  322. sphinx-8.2.0.dist-info/RECORD +606 -0
  323. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/WHEEL +1 -1
  324. sphinx/builders/html/transforms.py +0 -90
  325. sphinx/ext/apidoc.py +0 -721
  326. sphinx/util/exceptions.py +0 -74
  327. sphinx-8.1.2.dist-info/RECORD +0 -598
  328. {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 = os.path.join(app.builder.outdir, 'CNAME')
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(".github.io"):
47
- with open(cname_path, 'w', encoding="utf-8") as f:
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
- with contextlib.suppress(FileNotFoundError):
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 {'version': sphinx.__display_version__, 'parallel_read_safe': True}
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 posixpath
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, Any, ClassVar
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
- raise GraphvizError('Invalid clickable map file found: %s' % self.filename)
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%s' % hashed[-10:]
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(directive: SphinxDirective, node: graphviz, caption: str) -> nodes.figure:
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 [document.reporter.warning(
135
- __('Graphviz directive cannot have both content and '
136
- 'a filename argument'), line=self.lineno)]
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 [document.reporter.warning(
145
- __('External Graphviz file %r not found or reading '
146
- 'it failed') % filename, line=self.lineno)]
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 [self.state_machine.reporter.warning(
152
- __('Ignoring "graphviz" directive without content.'),
153
- line=self.lineno)]
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
- node['code'] = '%s %s {\n%s\n}\n' % \
202
- (self.name, self.arguments[0], '\n'.join(self.content))
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(self: HTML5Translator | LaTeXTranslator | TexinfoTranslator,
225
- filepath: str) -> None:
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 = self.builder.env.path2doc(self.document["source"])
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.app.outdir.joinpath(docname).resolve().parent
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(self: HTML5Translator | LaTeXTranslator | TexinfoTranslator,
261
- code: str, options: dict, format: str,
262
- prefix: str = 'graphviz', filename: str | None = None,
263
- ) -> tuple[str | None, str | None]:
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 = (code + str(options) + str(graphviz_dot) +
271
- str(self.builder.config.graphviz_dot_args)).encode()
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 = posixpath.join(self.builder.imgpath, fname)
275
- outfn = path.join(self.builder.outdir, self.builder.imagedir, fname)
297
+ relfn = _StrPath(self.builder.imgpath, fname)
298
+ outfn = self.builder.outdir / self.builder.imagedir / fname
276
299
 
277
- if path.isfile(outfn):
300
+ if outfn.is_file():
278
301
  return relfn, outfn
279
302
 
280
- if (hasattr(self.builder, '_graphviz_warned_dot') and
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
- ensuredir(path.dirname(outfn))
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' + format, '-o' + outfn])
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 = path.dirname(path.join(self.builder.srcdir, filename))
314
+ cwd = (self.builder.srcdir / filename).parent
293
315
  else:
294
- cwd = path.dirname(path.join(self.builder.srcdir, docname))
316
+ cwd = (self.builder.srcdir / docname).parent
295
317
 
296
318
  if format == 'png':
297
- dot_args.extend(['-Tcmapx', '-o%s.map' % outfn])
319
+ dot_args.extend(['-Tcmapx', f'-o{outfn}.map'])
298
320
 
299
321
  try:
300
- ret = subprocess.run(dot_args, input=code.encode(), capture_output=True,
301
- cwd=cwd, check=True)
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(__('dot command %r cannot be run (needed for graphviz '
304
- 'output), check the graphviz_dot setting'), graphviz_dot)
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(__('dot exited with error:\n[stderr]\n%r\n'
311
- '[stdout]\n%r') % (exc.stderr, exc.stdout)) from exc
312
- if not path.isfile(outfn):
313
- raise GraphvizError(__('dot did not produce an output file:\n[stderr]\n%r\n'
314
- '[stdout]\n%r') % (ret.stderr, ret.stdout))
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(self: HTML5Translator, node: graphviz, code: str, options: dict,
323
- prefix: str = 'graphviz', imgcls: str | None = None,
324
- alt: str | None = None, filename: str | None = None,
325
- ) -> tuple[str, str]:
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
- self.body.append('<div align="%s" class="align-%s">' %
346
- (node['align'], node['align']))
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('<object data="%s" type="image/svg+xml" class="%s">\n' %
350
- (fname, imgcls))
351
- self.body.append('<p class="warning">%s</p>' % alt)
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 + '.map', encoding='utf-8') as mapfile:
356
- imgmap = ClickableMapDefinition(outfn + '.map', mapfile.read(), dot=code)
357
- if imgmap.clickable:
358
- # has a map
359
- self.body.append('<div class="graphviz">')
360
- self.body.append('<img src="%s" alt="%s" usemap="#%s" class="%s" />' %
361
- (fname, alt, imgmap.id, imgcls))
362
- self.body.append('</div>\n')
363
- self.body.append(imgmap.generate_clickable_map())
364
- else:
365
- # nothing in image map
366
- self.body.append('<div class="graphviz">')
367
- self.body.append('<img src="%s" alt="%s" class="%s" />' %
368
- (fname, alt, imgcls))
369
- self.body.append('</div>\n')
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(self, node, node['code'], node['options'], filename=node.get('filename'))
378
-
379
-
380
- def render_dot_latex(self: LaTeXTranslator, node: graphviz, code: str,
381
- options: dict, prefix: str = 'graphviz', filename: str | None = None,
382
- ) -> None:
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%s' % pre)
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('%s\n' % post)
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(self, node, node['code'], node['options'], filename=node.get('filename'))
416
-
417
-
418
- def render_dot_texinfo(self: TexinfoTranslator, node: graphviz, code: str,
419
- options: dict, prefix: str = 'graphviz') -> None:
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 = path.join(sphinx.package_dir, 'templates', 'graphviz', 'graphviz.css')
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(graphviz,
457
- html=(html_visit_graphviz, None),
458
- latex=(latex_visit_graphviz, None),
459
- texinfo=(texinfo_visit_graphviz, None),
460
- text=(text_visit_graphviz, None),
461
- man=(man_visit_graphviz, None))
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('graphviz_dot_args', [], 'html')
467
- app.add_config_value('graphviz_output_format', 'png', 'html')
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 {'version': sphinx.__display_version__, 'parallel_read_safe': True}
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, ClassVar
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('Exception occurred in '
66
- 'ifconfig expression: \n%s' %
67
- msg, base_node=node)
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 {'version': sphinx.__display_version__, 'parallel_read_safe': True}
82
+ return {
83
+ 'version': sphinx.__display_version__,
84
+ 'parallel_read_safe': True,
85
+ }