Sphinx 7.1.1__py3-none-any.whl → 7.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 (313) hide show
  1. sphinx/__init__.py +6 -6
  2. sphinx/__main__.py +3 -1
  3. sphinx/addnodes.py +35 -22
  4. sphinx/application.py +40 -38
  5. sphinx/builders/__init__.py +16 -12
  6. sphinx/builders/_epub_base.py +15 -11
  7. sphinx/builders/changes.py +6 -4
  8. sphinx/builders/dirhtml.py +4 -2
  9. sphinx/builders/dummy.py +6 -4
  10. sphinx/builders/epub3.py +16 -8
  11. sphinx/builders/gettext.py +40 -43
  12. sphinx/builders/html/__init__.py +166 -196
  13. sphinx/builders/html/_assets.py +116 -0
  14. sphinx/builders/html/transforms.py +4 -2
  15. sphinx/builders/latex/__init__.py +12 -7
  16. sphinx/builders/latex/theming.py +5 -2
  17. sphinx/builders/latex/transforms.py +6 -3
  18. sphinx/builders/linkcheck.py +21 -13
  19. sphinx/builders/manpage.py +6 -4
  20. sphinx/builders/singlehtml.py +16 -9
  21. sphinx/builders/texinfo.py +11 -6
  22. sphinx/builders/text.py +8 -3
  23. sphinx/builders/xml.py +9 -4
  24. sphinx/cmd/build.py +27 -14
  25. sphinx/cmd/make_mode.py +13 -4
  26. sphinx/cmd/quickstart.py +13 -4
  27. sphinx/config.py +17 -14
  28. sphinx/deprecation.py +4 -2
  29. sphinx/directives/__init__.py +44 -12
  30. sphinx/directives/code.py +5 -4
  31. sphinx/directives/other.py +92 -44
  32. sphinx/directives/patches.py +1 -1
  33. sphinx/domains/__init__.py +11 -8
  34. sphinx/domains/c.py +67 -57
  35. sphinx/domains/changeset.py +3 -2
  36. sphinx/domains/citation.py +2 -1
  37. sphinx/domains/cpp.py +136 -93
  38. sphinx/domains/index.py +9 -5
  39. sphinx/domains/javascript.py +32 -19
  40. sphinx/domains/math.py +5 -3
  41. sphinx/domains/python.py +69 -57
  42. sphinx/domains/rst.py +20 -11
  43. sphinx/domains/std.py +21 -15
  44. sphinx/environment/__init__.py +97 -65
  45. sphinx/environment/adapters/indexentries.py +13 -10
  46. sphinx/environment/adapters/toctree.py +485 -308
  47. sphinx/environment/collectors/__init__.py +3 -4
  48. sphinx/environment/collectors/asset.py +10 -4
  49. sphinx/environment/collectors/dependencies.py +7 -4
  50. sphinx/environment/collectors/metadata.py +7 -5
  51. sphinx/environment/collectors/title.py +5 -3
  52. sphinx/environment/collectors/toctree.py +13 -8
  53. sphinx/errors.py +1 -1
  54. sphinx/events.py +5 -5
  55. sphinx/ext/apidoc.py +49 -27
  56. sphinx/ext/autodoc/__init__.py +179 -161
  57. sphinx/ext/autodoc/directive.py +10 -6
  58. sphinx/ext/autodoc/importer.py +22 -13
  59. sphinx/ext/autodoc/mock.py +4 -1
  60. sphinx/ext/autodoc/preserve_defaults.py +80 -12
  61. sphinx/ext/autodoc/type_comment.py +14 -10
  62. sphinx/ext/autodoc/typehints.py +7 -3
  63. sphinx/ext/autosectionlabel.py +6 -3
  64. sphinx/ext/autosummary/__init__.py +21 -15
  65. sphinx/ext/autosummary/generate.py +176 -126
  66. sphinx/ext/coverage.py +93 -8
  67. sphinx/ext/doctest.py +28 -17
  68. sphinx/ext/duration.py +19 -17
  69. sphinx/ext/extlinks.py +11 -6
  70. sphinx/ext/githubpages.py +8 -7
  71. sphinx/ext/graphviz.py +61 -17
  72. sphinx/ext/ifconfig.py +7 -4
  73. sphinx/ext/imgconverter.py +4 -2
  74. sphinx/ext/imgmath.py +29 -23
  75. sphinx/ext/inheritance_diagram.py +41 -27
  76. sphinx/ext/intersphinx.py +45 -38
  77. sphinx/ext/linkcode.py +8 -5
  78. sphinx/ext/mathjax.py +13 -9
  79. sphinx/ext/napoleon/__init__.py +3 -3
  80. sphinx/ext/napoleon/docstring.py +40 -31
  81. sphinx/ext/todo.py +10 -7
  82. sphinx/ext/viewcode.py +46 -25
  83. sphinx/extension.py +1 -1
  84. sphinx/highlighting.py +20 -12
  85. sphinx/io.py +5 -4
  86. sphinx/jinja2glue.py +24 -19
  87. sphinx/locale/__init__.py +8 -2
  88. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  89. sphinx/locale/ar/LC_MESSAGES/sphinx.po +756 -740
  90. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  91. sphinx/locale/bg/LC_MESSAGES/sphinx.po +754 -738
  92. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  93. sphinx/locale/bn/LC_MESSAGES/sphinx.po +755 -739
  94. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  95. sphinx/locale/ca/LC_MESSAGES/sphinx.po +768 -752
  96. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  97. sphinx/locale/cak/LC_MESSAGES/sphinx.po +754 -738
  98. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  99. sphinx/locale/cs/LC_MESSAGES/sphinx.po +758 -742
  100. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  101. sphinx/locale/cy/LC_MESSAGES/sphinx.po +759 -743
  102. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  103. sphinx/locale/da/LC_MESSAGES/sphinx.po +760 -744
  104. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  105. sphinx/locale/de/LC_MESSAGES/sphinx.po +759 -743
  106. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  107. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +754 -738
  108. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  109. sphinx/locale/el/LC_MESSAGES/sphinx.po +763 -747
  110. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  111. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +754 -738
  112. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  113. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +754 -738
  114. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  115. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +768 -752
  116. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  117. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +754 -738
  118. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  119. sphinx/locale/eo/LC_MESSAGES/sphinx.po +754 -738
  120. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  121. sphinx/locale/es/LC_MESSAGES/sphinx.po +767 -751
  122. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +754 -738
  124. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  125. sphinx/locale/et/LC_MESSAGES/sphinx.po +762 -746
  126. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  127. sphinx/locale/eu/LC_MESSAGES/sphinx.po +755 -739
  128. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  129. sphinx/locale/fa/LC_MESSAGES/sphinx.po +766 -750
  130. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  131. sphinx/locale/fi/LC_MESSAGES/sphinx.po +754 -738
  132. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  133. sphinx/locale/fr/LC_MESSAGES/sphinx.po +768 -752
  134. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  135. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +754 -738
  136. sphinx/locale/gl/LC_MESSAGES/sphinx.js +60 -0
  137. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  138. sphinx/locale/gl/LC_MESSAGES/sphinx.po +3695 -0
  139. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  140. sphinx/locale/he/LC_MESSAGES/sphinx.po +755 -739
  141. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  142. sphinx/locale/hi/LC_MESSAGES/sphinx.po +763 -747
  143. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +754 -738
  145. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/hr/LC_MESSAGES/sphinx.po +760 -744
  147. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  148. sphinx/locale/hu/LC_MESSAGES/sphinx.po +759 -743
  149. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  150. sphinx/locale/id/LC_MESSAGES/sphinx.po +765 -749
  151. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/is/LC_MESSAGES/sphinx.po +760 -744
  153. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/it/LC_MESSAGES/sphinx.po +760 -744
  155. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/ja/LC_MESSAGES/sphinx.po +767 -751
  157. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/ka/LC_MESSAGES/sphinx.po +759 -743
  159. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/ko/LC_MESSAGES/sphinx.po +767 -751
  161. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/lt/LC_MESSAGES/sphinx.po +755 -739
  163. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/lv/LC_MESSAGES/sphinx.po +755 -739
  165. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/mk/LC_MESSAGES/sphinx.po +754 -738
  167. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +755 -739
  169. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/ne/LC_MESSAGES/sphinx.po +755 -739
  171. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/nl/LC_MESSAGES/sphinx.po +760 -744
  173. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/pl/LC_MESSAGES/sphinx.po +762 -745
  175. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/pt/LC_MESSAGES/sphinx.po +754 -738
  177. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +768 -752
  179. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  180. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +755 -739
  181. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  182. sphinx/locale/ro/LC_MESSAGES/sphinx.po +759 -743
  183. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  184. sphinx/locale/ru/LC_MESSAGES/sphinx.po +760 -744
  185. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  186. sphinx/locale/si/LC_MESSAGES/sphinx.po +754 -738
  187. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  188. sphinx/locale/sk/LC_MESSAGES/sphinx.po +765 -749
  189. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  190. sphinx/locale/sl/LC_MESSAGES/sphinx.po +755 -739
  191. sphinx/locale/sphinx.pot +748 -740
  192. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  193. sphinx/locale/sq/LC_MESSAGES/sphinx.po +768 -752
  194. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  195. sphinx/locale/sr/LC_MESSAGES/sphinx.po +754 -738
  196. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
  197. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po +754 -738
  198. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
  199. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po +754 -738
  200. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  201. sphinx/locale/sv/LC_MESSAGES/sphinx.po +755 -739
  202. sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
  203. sphinx/locale/ta/LC_MESSAGES/sphinx.po +754 -738
  204. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  205. sphinx/locale/te/LC_MESSAGES/sphinx.po +754 -738
  206. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/tr/LC_MESSAGES/sphinx.po +763 -747
  208. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +760 -749
  210. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/ur/LC_MESSAGES/sphinx.po +759 -748
  212. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/vi/LC_MESSAGES/sphinx.po +754 -738
  214. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  215. sphinx/locale/yue/LC_MESSAGES/sphinx.po +754 -738
  216. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo +0 -0
  217. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +768 -752
  218. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  219. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +754 -738
  220. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  221. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +767 -751
  222. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  223. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +754 -738
  224. sphinx/parsers.py +5 -4
  225. sphinx/project.py +52 -34
  226. sphinx/pycode/__init__.py +2 -1
  227. sphinx/pycode/ast.py +7 -13
  228. sphinx/pycode/parser.py +42 -38
  229. sphinx/registry.py +35 -29
  230. sphinx/roles.py +9 -4
  231. sphinx/search/__init__.py +5 -17
  232. sphinx/search/da.py +1 -1
  233. sphinx/search/de.py +1 -1
  234. sphinx/search/en.py +1 -1
  235. sphinx/search/es.py +1 -1
  236. sphinx/search/fi.py +1 -1
  237. sphinx/search/fr.py +1 -1
  238. sphinx/search/hu.py +1 -1
  239. sphinx/search/it.py +1 -1
  240. sphinx/search/ja.py +1 -1
  241. sphinx/search/nl.py +1 -1
  242. sphinx/search/no.py +1 -1
  243. sphinx/search/pt.py +1 -1
  244. sphinx/search/ro.py +1 -1
  245. sphinx/search/ru.py +1 -1
  246. sphinx/search/sv.py +1 -1
  247. sphinx/search/tr.py +1 -1
  248. sphinx/search/zh.py +1 -1
  249. sphinx/testing/fixtures.py +23 -30
  250. sphinx/testing/path.py +9 -0
  251. sphinx/testing/restructuredtext.py +13 -5
  252. sphinx/testing/util.py +20 -63
  253. sphinx/texinputs/sphinxlatexobjects.sty +15 -15
  254. sphinx/themes/agogo/static/agogo.css_t +10 -4
  255. sphinx/themes/basic/layout.html +1 -1
  256. sphinx/themes/basic/static/basic.css_t +4 -0
  257. sphinx/themes/basic/static/documentation_options.js_t +1 -2
  258. sphinx/themes/basic/static/searchtools.js +17 -9
  259. sphinx/themes/basic/static/sphinx_highlight.js +13 -3
  260. sphinx/themes/bizstyle/static/bizstyle.css_t +4 -0
  261. sphinx/themes/classic/theme.conf +1 -1
  262. sphinx/themes/epub/static/epub.css_t +6 -1
  263. sphinx/themes/haiku/theme.conf +1 -1
  264. sphinx/themes/nature/static/nature.css_t +4 -0
  265. sphinx/themes/nonav/static/nonav.css_t +6 -1
  266. sphinx/themes/pyramid/static/pyramid.css_t +4 -0
  267. sphinx/themes/scrolls/static/scrolls.css_t +4 -0
  268. sphinx/themes/scrolls/theme.conf +1 -1
  269. sphinx/themes/sphinxdoc/static/sphinxdoc.css_t +4 -0
  270. sphinx/theming.py +9 -7
  271. sphinx/transforms/__init__.py +79 -3
  272. sphinx/transforms/compact_bullet_list.py +6 -3
  273. sphinx/transforms/i18n.py +26 -10
  274. sphinx/transforms/post_transforms/__init__.py +21 -8
  275. sphinx/transforms/post_transforms/code.py +6 -3
  276. sphinx/transforms/post_transforms/images.py +13 -9
  277. sphinx/util/__init__.py +21 -92
  278. sphinx/util/cfamily.py +7 -4
  279. sphinx/util/display.py +3 -2
  280. sphinx/util/docfields.py +7 -6
  281. sphinx/util/docstrings.py +1 -1
  282. sphinx/util/docutils.py +41 -31
  283. sphinx/util/fileutil.py +9 -6
  284. sphinx/util/i18n.py +21 -18
  285. sphinx/util/images.py +2 -1
  286. sphinx/util/index_entries.py +27 -0
  287. sphinx/util/inspect.py +83 -67
  288. sphinx/util/inventory.py +4 -2
  289. sphinx/util/logging.py +9 -6
  290. sphinx/util/matching.py +5 -2
  291. sphinx/util/math.py +6 -3
  292. sphinx/util/nodes.py +70 -31
  293. sphinx/util/osutil.py +22 -40
  294. sphinx/util/parallel.py +4 -1
  295. sphinx/util/rst.py +7 -3
  296. sphinx/util/tags.py +11 -4
  297. sphinx/util/template.py +17 -14
  298. sphinx/util/typing.py +61 -20
  299. sphinx/versioning.py +6 -4
  300. sphinx/writers/html.py +1 -1
  301. sphinx/writers/html5.py +32 -24
  302. sphinx/writers/latex.py +67 -53
  303. sphinx/writers/manpage.py +9 -5
  304. sphinx/writers/texinfo.py +11 -9
  305. sphinx/writers/text.py +14 -9
  306. sphinx/writers/xml.py +3 -2
  307. {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/METADATA +7 -5
  308. sphinx-7.2.0.dist-info/RECORD +568 -0
  309. sphinx/testing/comparer.py +0 -97
  310. sphinx-7.1.1.dist-info/RECORD +0 -564
  311. {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/LICENSE +0 -0
  312. {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/WHEEL +0 -0
  313. {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/entry_points.txt +0 -0
sphinx/parsers.py CHANGED
@@ -9,15 +9,16 @@ import docutils.parsers.rst
9
9
  from docutils import nodes
10
10
  from docutils.parsers.rst import states
11
11
  from docutils.statemachine import StringList
12
- from docutils.transforms import Transform
13
12
  from docutils.transforms.universal import SmartQuotes
14
13
 
15
- from sphinx.config import Config
16
- from sphinx.environment import BuildEnvironment
17
14
  from sphinx.util.rst import append_epilog, prepend_prolog
18
15
 
19
16
  if TYPE_CHECKING:
17
+ from docutils.transforms import Transform
18
+
20
19
  from sphinx.application import Sphinx
20
+ from sphinx.config import Config
21
+ from sphinx.environment import BuildEnvironment
21
22
 
22
23
 
23
24
  class Parser(docutils.parsers.Parser):
@@ -60,7 +61,7 @@ class RSTParser(docutils.parsers.rst.Parser, Parser):
60
61
 
61
62
  def parse(self, inputstring: str | StringList, document: nodes.document) -> None:
62
63
  """Parse text and generate a document tree."""
63
- self.setup_parse(inputstring, document) # type: ignore
64
+ self.setup_parse(inputstring, document) # type: ignore[arg-type]
64
65
  self.statemachine = states.RSTStateMachine(
65
66
  state_classes=self.state_classes,
66
67
  initial_state=self.initial_state,
sphinx/project.py CHANGED
@@ -2,14 +2,18 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import contextlib
5
6
  import os
6
7
  from glob import glob
7
- from typing import Iterable
8
+ from typing import TYPE_CHECKING
8
9
 
9
10
  from sphinx.locale import __
10
11
  from sphinx.util import logging
11
12
  from sphinx.util.matching import get_matching_files
12
- from sphinx.util.osutil import SEP, path_stabilize, relpath
13
+ from sphinx.util.osutil import path_stabilize, relpath
14
+
15
+ if TYPE_CHECKING:
16
+ from collections.abc import Iterable
13
17
 
14
18
  logger = logging.getLogger(__name__)
15
19
  EXCLUDE_PATHS = ['**/_sources', '.#*', '**/.#*', '*.lproj/**']
@@ -18,77 +22,91 @@ EXCLUDE_PATHS = ['**/_sources', '.#*', '**/.#*', '*.lproj/**']
18
22
  class Project:
19
23
  """A project is the source code set of the Sphinx document(s)."""
20
24
 
21
- def __init__(self, srcdir: str, source_suffix: dict[str, str]) -> None:
25
+ def __init__(self, srcdir: str | os.PathLike[str], source_suffix: Iterable[str]) -> None:
22
26
  #: Source directory.
23
27
  self.srcdir = srcdir
24
28
 
25
29
  #: source_suffix. Same as :confval:`source_suffix`.
26
- self.source_suffix = source_suffix
30
+ self.source_suffix = tuple(source_suffix)
31
+ self._first_source_suffix = next(iter(self.source_suffix), "")
27
32
 
28
- #: The name of documents belongs to this project.
33
+ #: The name of documents belonging to this project.
29
34
  self.docnames: set[str] = set()
30
35
 
36
+ # Bijective mapping between docnames and (srcdir relative) paths.
37
+ self._path_to_docname: dict[str, str] = {}
38
+ self._docname_to_path: dict[str, str] = {}
39
+
31
40
  def restore(self, other: Project) -> None:
32
41
  """Take over a result of last build."""
33
42
  self.docnames = other.docnames
43
+ self._path_to_docname = other._path_to_docname
44
+ self._docname_to_path = other._docname_to_path
34
45
 
35
46
  def discover(self, exclude_paths: Iterable[str] = (),
36
47
  include_paths: Iterable[str] = ("**",)) -> set[str]:
37
48
  """Find all document files in the source directory and put them in
38
49
  :attr:`docnames`.
39
50
  """
40
- self.docnames = set()
51
+
52
+ self.docnames.clear()
53
+ self._path_to_docname.clear()
54
+ self._docname_to_path.clear()
55
+
41
56
  for filename in get_matching_files(
42
57
  self.srcdir,
43
58
  include_paths,
44
59
  [*exclude_paths] + EXCLUDE_PATHS,
45
60
  ):
46
- docname = self.path2doc(filename)
47
- if docname:
61
+ if docname := self.path2doc(filename):
48
62
  if docname in self.docnames:
49
63
  pattern = os.path.join(self.srcdir, docname) + '.*'
50
64
  files = [relpath(f, self.srcdir) for f in glob(pattern)]
51
65
  logger.warning(__('multiple files found for the document "%s": %r\n'
52
66
  'Use %r for the build.'),
53
- docname, files, self.doc2path(docname), once=True)
67
+ docname, files, self.doc2path(docname, absolute=True),
68
+ once=True)
54
69
  elif os.access(os.path.join(self.srcdir, filename), os.R_OK):
55
70
  self.docnames.add(docname)
71
+ self._path_to_docname[filename] = docname
72
+ self._docname_to_path[docname] = filename
56
73
  else:
57
- logger.warning(__("document not readable. Ignored."), location=docname)
74
+ logger.warning(__("Ignored unreadable document %r."),
75
+ filename, location=docname)
58
76
 
59
77
  return self.docnames
60
78
 
61
- def path2doc(self, filename: str) -> str | None:
79
+ def path2doc(self, filename: str | os.PathLike[str]) -> str | None:
62
80
  """Return the docname for the filename if the file is a document.
63
81
 
64
82
  *filename* should be absolute or relative to the source directory.
65
83
  """
66
- if filename.startswith(self.srcdir):
67
- filename = relpath(filename, self.srcdir)
68
- for suffix in self.source_suffix:
69
- if filename.endswith(suffix):
70
- filename = path_stabilize(filename)
71
- return filename[:-len(suffix)]
84
+ try:
85
+ return self._path_to_docname[filename] # type: ignore[index]
86
+ except KeyError:
87
+ if os.path.isabs(filename):
88
+ with contextlib.suppress(ValueError):
89
+ filename = os.path.relpath(filename, self.srcdir)
90
+
91
+ for suffix in self.source_suffix:
92
+ if os.path.basename(filename).endswith(suffix):
93
+ return path_stabilize(filename).removesuffix(suffix)
72
94
 
73
- # the file does not have docname
74
- return None
95
+ # the file does not have a docname
96
+ return None
75
97
 
76
- def doc2path(self, docname: str, basedir: bool = True) -> str:
98
+ def doc2path(self, docname: str, absolute: bool) -> str:
77
99
  """Return the filename for the document name.
78
100
 
79
- If *basedir* is True, return as an absolute path.
101
+ If *absolute* is True, return as an absolute path.
80
102
  Else, return as a relative path to the source directory.
81
103
  """
82
- docname = docname.replace(SEP, os.path.sep)
83
- basename = os.path.join(self.srcdir, docname)
84
- for suffix in self.source_suffix:
85
- if os.path.isfile(basename + suffix):
86
- break
87
- else:
88
- # document does not exist
89
- suffix = list(self.source_suffix)[0]
90
-
91
- if basedir:
92
- return basename + suffix
93
- else:
94
- return docname + suffix
104
+ try:
105
+ filename = self._docname_to_path[docname]
106
+ except KeyError:
107
+ # Backwards compatibility: the document does not exist
108
+ filename = docname + self._first_source_suffix
109
+
110
+ if absolute:
111
+ return os.path.join(self.srcdir, filename)
112
+ return filename
sphinx/pycode/__init__.py CHANGED
@@ -138,7 +138,8 @@ class ModuleAnalyzer:
138
138
  self.tagorder = parser.deforders
139
139
  self._analyzed = True
140
140
  except Exception as exc:
141
- raise PycodeError(f'parsing {self.srcname!r} failed: {exc!r}') from exc
141
+ msg = f'parsing {self.srcname!r} failed: {exc!r}'
142
+ raise PycodeError(msg) from exc
142
143
 
143
144
  def find_attr_docs(self) -> dict[tuple[str, str], list[str]]:
144
145
  """Find class and module-level attributes and their documentation."""
sphinx/pycode/ast.py CHANGED
@@ -143,9 +143,6 @@ class _UnparseVisitor(ast.NodeVisitor):
143
143
  items = (k + ": " + v for k, v in zip(keys, values))
144
144
  return "{" + ", ".join(items) + "}"
145
145
 
146
- def visit_Index(self, node: ast.Index) -> str:
147
- return self.visit(node.value)
148
-
149
146
  def visit_Lambda(self, node: ast.Lambda) -> str:
150
147
  return "lambda %s: ..." % self.visit(node.args)
151
148
 
@@ -159,21 +156,18 @@ class _UnparseVisitor(ast.NodeVisitor):
159
156
  return "{" + ", ".join(self.visit(e) for e in node.elts) + "}"
160
157
 
161
158
  def visit_Subscript(self, node: ast.Subscript) -> str:
162
- def is_simple_tuple(value: ast.AST) -> bool:
159
+ def is_simple_tuple(value: ast.expr) -> bool:
163
160
  return (
164
- isinstance(value, ast.Tuple) and
165
- bool(value.elts) and
166
- not any(isinstance(elt, ast.Starred) for elt in value.elts)
161
+ isinstance(value, ast.Tuple)
162
+ and bool(value.elts)
163
+ and not any(isinstance(elt, ast.Starred) for elt in value.elts)
167
164
  )
168
165
 
169
166
  if is_simple_tuple(node.slice):
170
- elts = ", ".join(self.visit(e) for e in node.slice.elts) # type: ignore
171
- return f"{self.visit(node.value)}[{elts}]"
172
- elif isinstance(node.slice, ast.Index) and is_simple_tuple(node.slice.value):
173
- elts = ", ".join(self.visit(e) for e in node.slice.value.elts) # type: ignore
167
+ elts = ", ".join(self.visit(e)
168
+ for e in node.slice.elts) # type: ignore[attr-defined]
174
169
  return f"{self.visit(node.value)}[{elts}]"
175
- else:
176
- return f"{self.visit(node.value)}[{self.visit(node.slice)}]"
170
+ return f"{self.visit(node.value)}[{self.visit(node.slice)}]"
177
171
 
178
172
  def visit_UnaryOp(self, node: ast.UnaryOp) -> str:
179
173
  # UnaryOp is one of {UAdd, USub, Invert, Not}, which refer to ``+x``,
sphinx/pycode/parser.py CHANGED
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import ast
6
+ import contextlib
6
7
  import inspect
7
8
  import itertools
8
9
  import re
@@ -28,7 +29,7 @@ def get_assign_targets(node: ast.AST) -> list[ast.expr]:
28
29
  if isinstance(node, ast.Assign):
29
30
  return node.targets
30
31
  else:
31
- return [node.target] # type: ignore
32
+ return [node.target] # type: ignore[attr-defined]
32
33
 
33
34
 
34
35
  def get_lvar_names(node: ast.AST, self: ast.arg | None = None) -> list[str]:
@@ -44,20 +45,19 @@ def get_lvar_names(node: ast.AST, self: ast.arg | None = None) -> list[str]:
44
45
  self_id = self.arg
45
46
 
46
47
  node_name = node.__class__.__name__
47
- if node_name in ('Index', 'Num', 'Slice', 'Str', 'Subscript'):
48
+ if node_name in ('Constant', 'Index', 'Slice', 'Subscript'):
48
49
  raise TypeError('%r does not create new variable' % node)
49
50
  if node_name == 'Name':
50
- if self is None or node.id == self_id: # type: ignore
51
- return [node.id] # type: ignore
51
+ if self is None or node.id == self_id: # type: ignore[attr-defined]
52
+ return [node.id] # type: ignore[attr-defined]
52
53
  else:
53
54
  raise TypeError('The assignment %r is not instance variable' % node)
54
55
  elif node_name in ('Tuple', 'List'):
55
56
  members = []
56
- for elt in node.elts: # type: ignore
57
- try:
57
+ for elt in node.elts: # type: ignore[attr-defined]
58
+ with contextlib.suppress(TypeError):
58
59
  members.extend(get_lvar_names(elt, self))
59
- except TypeError:
60
- pass
60
+
61
61
  return members
62
62
  elif node_name == 'Attribute':
63
63
  if (
@@ -65,13 +65,13 @@ def get_lvar_names(node: ast.AST, self: ast.arg | None = None) -> list[str]:
65
65
  self and node.value.id == self_id # type: ignore[attr-defined]
66
66
  ):
67
67
  # instance variable
68
- return ["%s" % get_lvar_names(node.attr, self)[0]] # type: ignore
68
+ return ["%s" % get_lvar_names(node.attr, self)[0]] # type: ignore[attr-defined]
69
69
  else:
70
70
  raise TypeError('The assignment %r is not instance variable' % node)
71
71
  elif node_name == 'str':
72
- return [node] # type: ignore
72
+ return [node] # type: ignore[list-item]
73
73
  elif node_name == 'Starred':
74
- return get_lvar_names(node.value, self) # type: ignore
74
+ return get_lvar_names(node.value, self) # type: ignore[attr-defined]
75
75
  else:
76
76
  raise NotImplementedError('Unexpected node name %r' % node_name)
77
77
 
@@ -145,21 +145,21 @@ class TokenProcessor:
145
145
 
146
146
  return self.current
147
147
 
148
- def fetch_until(self, condition: Any) -> list[Token | None]:
148
+ def fetch_until(self, condition: Any) -> list[Token]:
149
149
  """Fetch tokens until specified token appeared.
150
150
 
151
151
  .. note:: This also handles parenthesis well.
152
152
  """
153
153
  tokens = []
154
- while self.fetch_token():
155
- tokens.append(self.current)
156
- if self.current == condition:
154
+ while current := self.fetch_token():
155
+ tokens.append(current)
156
+ if current == condition:
157
157
  break
158
- if self.current == [OP, '(']:
158
+ if current == [OP, '(']:
159
159
  tokens += self.fetch_until([OP, ')'])
160
- elif self.current == [OP, '{']:
160
+ elif current == [OP, '{']:
161
161
  tokens += self.fetch_until([OP, '}'])
162
- elif self.current == [OP, '[']:
162
+ elif current == [OP, '[']:
163
163
  tokens += self.fetch_until([OP, ']'])
164
164
 
165
165
  return tokens
@@ -176,22 +176,22 @@ class AfterCommentParser(TokenProcessor):
176
176
  super().__init__(lines)
177
177
  self.comment: str | None = None
178
178
 
179
- def fetch_rvalue(self) -> list[Token | None]:
179
+ def fetch_rvalue(self) -> list[Token]:
180
180
  """Fetch right-hand value of assignment."""
181
181
  tokens = []
182
- while self.fetch_token():
183
- tokens.append(self.current)
184
- if self.current == [OP, '(']:
182
+ while current := self.fetch_token():
183
+ tokens.append(current)
184
+ if current == [OP, '(']:
185
185
  tokens += self.fetch_until([OP, ')'])
186
- elif self.current == [OP, '{']:
186
+ elif current == [OP, '{']:
187
187
  tokens += self.fetch_until([OP, '}'])
188
- elif self.current == [OP, '[']:
188
+ elif current == [OP, '[']:
189
189
  tokens += self.fetch_until([OP, ']'])
190
- elif self.current == INDENT:
190
+ elif current == INDENT:
191
191
  tokens += self.fetch_until(DEDENT)
192
- elif self.current == [OP, ';']: # NoQA: SIM114
192
+ elif current == [OP, ';']: # NoQA: SIM114
193
193
  break
194
- elif self.current and self.current.kind not in {OP, NAME, NUMBER, STRING}:
194
+ elif current and current.kind not in {OP, NAME, NUMBER, STRING}:
195
195
  break
196
196
 
197
197
  return tokens
@@ -200,14 +200,17 @@ class AfterCommentParser(TokenProcessor):
200
200
  """Parse the code and obtain comment after assignment."""
201
201
  # skip lvalue (or whole of AnnAssign)
202
202
  while (tok := self.fetch_token()) and not tok.match([OP, '='], NEWLINE, COMMENT):
203
- assert self.current
203
+ assert tok
204
+ assert tok is not None
204
205
 
205
206
  # skip rvalue (if exists)
206
- if self.current == [OP, '=']:
207
+ if tok == [OP, '=']:
207
208
  self.fetch_rvalue()
209
+ tok = self.current
210
+ assert tok is not None
208
211
 
209
- if self.current == COMMENT:
210
- self.comment = self.current.value # type: ignore[union-attr]
212
+ if tok == COMMENT:
213
+ self.comment = tok.value
211
214
 
212
215
 
213
216
  class VariableCommentPicker(ast.NodeVisitor):
@@ -360,7 +363,8 @@ class VariableCommentPicker(ast.NodeVisitor):
360
363
  self.add_variable_annotation(varname, node.annotation)
361
364
  elif hasattr(node, 'type_comment') and node.type_comment:
362
365
  for varname in varnames:
363
- self.add_variable_annotation(varname, node.type_comment) # type: ignore
366
+ self.add_variable_annotation(
367
+ varname, node.type_comment) # type: ignore[arg-type]
364
368
 
365
369
  # check comments after assignment
366
370
  parser = AfterCommentParser([current_line[node.col_offset:]] +
@@ -395,20 +399,20 @@ class VariableCommentPicker(ast.NodeVisitor):
395
399
 
396
400
  def visit_AnnAssign(self, node: ast.AnnAssign) -> None:
397
401
  """Handles AnnAssign node and pick up a variable comment."""
398
- self.visit_Assign(node) # type: ignore
402
+ self.visit_Assign(node) # type: ignore[arg-type]
399
403
 
400
404
  def visit_Expr(self, node: ast.Expr) -> None:
401
405
  """Handles Expr node and pick up a comment if string."""
402
406
  if (isinstance(self.previous, (ast.Assign, ast.AnnAssign)) and
403
- isinstance(node.value, ast.Str)):
407
+ isinstance(node.value, ast.Constant) and isinstance(node.value.value, str)):
404
408
  try:
405
409
  targets = get_assign_targets(self.previous)
406
410
  varnames = get_lvar_names(targets[0], self.get_self())
407
411
  for varname in varnames:
408
- if isinstance(node.value.s, str):
409
- docstring = node.value.s
412
+ if isinstance(node.value.value, str):
413
+ docstring = node.value.value
410
414
  else:
411
- docstring = node.value.s.decode(self.encoding or 'utf-8')
415
+ docstring = node.value.value.decode(self.encoding or 'utf-8')
412
416
 
413
417
  self.add_variable_comment(varname, dedent_docstring(docstring))
414
418
  self.add_entry(varname)
@@ -455,7 +459,7 @@ class VariableCommentPicker(ast.NodeVisitor):
455
459
 
456
460
  def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
457
461
  """Handles AsyncFunctionDef node and set context."""
458
- self.visit_FunctionDef(node) # type: ignore
462
+ self.visit_FunctionDef(node) # type: ignore[arg-type]
459
463
 
460
464
 
461
465
  class DefinitionFinder(TokenProcessor):
sphinx/registry.py CHANGED
@@ -6,25 +6,15 @@ import sys
6
6
  import traceback
7
7
  from importlib import import_module
8
8
  from types import MethodType
9
- from typing import TYPE_CHECKING, Any, Callable, Iterator
10
-
11
- from docutils import nodes
12
- from docutils.core import Publisher
13
- from docutils.nodes import Element, Node, TextElement
14
- from docutils.parsers import Parser
15
- from docutils.parsers.rst import Directive
16
- from docutils.transforms import Transform
9
+ from typing import TYPE_CHECKING, Any, Callable
17
10
 
18
11
  if sys.version_info >= (3, 10):
19
12
  from importlib.metadata import entry_points
20
13
  else:
21
14
  from importlib_metadata import entry_points
22
15
 
23
- from sphinx.builders import Builder
24
- from sphinx.config import Config
25
16
  from sphinx.domains import Domain, Index, ObjType
26
17
  from sphinx.domains.std import GenericObject, Target
27
- from sphinx.environment import BuildEnvironment
28
18
  from sphinx.errors import ExtensionError, SphinxError, VersionRequirementError
29
19
  from sphinx.extension import Extension
30
20
  from sphinx.io import create_publisher
@@ -33,11 +23,23 @@ from sphinx.parsers import Parser as SphinxParser
33
23
  from sphinx.roles import XRefRole
34
24
  from sphinx.util import logging
35
25
  from sphinx.util.logging import prefixed_warnings
36
- from sphinx.util.typing import RoleFunction, TitleGetter
37
26
 
38
27
  if TYPE_CHECKING:
28
+ from collections.abc import Iterator, Sequence
29
+
30
+ from docutils import nodes
31
+ from docutils.core import Publisher
32
+ from docutils.nodes import Element, Node, TextElement
33
+ from docutils.parsers import Parser
34
+ from docutils.parsers.rst import Directive
35
+ from docutils.transforms import Transform
36
+
39
37
  from sphinx.application import Sphinx
38
+ from sphinx.builders import Builder
39
+ from sphinx.config import Config
40
+ from sphinx.environment import BuildEnvironment
40
41
  from sphinx.ext.autodoc import Documenter
42
+ from sphinx.util.typing import RoleFunction, TitleGetter
41
43
 
42
44
  logger = logging.getLogger(__name__)
43
45
 
@@ -83,12 +85,14 @@ class SphinxComponentRegistry:
83
85
 
84
86
  #: additional enumerable nodes
85
87
  #: a dict of node class -> tuple of figtype and title_getter function
86
- self.enumerable_nodes: dict[type[Node], tuple[str, TitleGetter]] = {}
88
+ self.enumerable_nodes: dict[type[Node], tuple[str, TitleGetter | None]] = {}
87
89
 
88
90
  #: HTML inline and block math renderers
89
91
  #: a dict of name -> tuple of visit function and depart function
90
- self.html_inline_math_renderers: dict[str, tuple[Callable, Callable]] = {}
91
- self.html_block_math_renderers: dict[str, tuple[Callable, Callable]] = {}
92
+ self.html_inline_math_renderers: dict[str,
93
+ tuple[Callable, Callable | None]] = {}
94
+ self.html_block_math_renderers: dict[str,
95
+ tuple[Callable, Callable | None]] = {}
92
96
 
93
97
  #: HTML assets
94
98
  self.html_assets_policy: str = 'per_page'
@@ -97,12 +101,12 @@ class SphinxComponentRegistry:
97
101
  self.html_themes: dict[str, str] = {}
98
102
 
99
103
  #: js_files; list of JS paths or URLs
100
- self.js_files: list[tuple[str, dict[str, Any]]] = []
104
+ self.js_files: list[tuple[str | None, dict[str, Any]]] = []
101
105
 
102
106
  #: LaTeX packages; list of package names and its options
103
- self.latex_packages: list[tuple[str, str]] = []
107
+ self.latex_packages: list[tuple[str, str | None]] = []
104
108
 
105
- self.latex_packages_after_hyperref: list[tuple[str, str]] = []
109
+ self.latex_packages_after_hyperref: list[tuple[str, str | None]] = []
106
110
 
107
111
  #: post transforms; list of transforms
108
112
  self.post_transforms: list[type[Transform]] = []
@@ -118,7 +122,7 @@ class SphinxComponentRegistry:
118
122
 
119
123
  #: custom handlers for translators
120
124
  #: a dict of builder name -> dict of node name -> visitor and departure functions
121
- self.translation_handlers: dict[str, dict[str, tuple[Callable, Callable]]] = {}
125
+ self.translation_handlers: dict[str, dict[str, tuple[Callable, Callable | None]]] = {}
122
126
 
123
127
  #: additional transforms; list of transforms
124
128
  self.transforms: list[type[Transform]] = []
@@ -149,8 +153,7 @@ class SphinxComponentRegistry:
149
153
 
150
154
  self.load_extension(app, entry_point.module)
151
155
 
152
- def create_builder(self, app: Sphinx, name: str,
153
- env: BuildEnvironment | None = None) -> Builder:
156
+ def create_builder(self, app: Sphinx, name: str, env: BuildEnvironment) -> Builder:
154
157
  if name not in self.builders:
155
158
  raise SphinxError(__('Builder name %s not registered') % name)
156
159
 
@@ -221,7 +224,7 @@ class SphinxComponentRegistry:
221
224
  parse_node: Callable | None = None,
222
225
  ref_nodeclass: type[TextElement] | None = None,
223
226
  objname: str = '',
224
- doc_field_types: list = [],
227
+ doc_field_types: Sequence = (),
225
228
  override: bool = False,
226
229
  ) -> None:
227
230
  logger.debug('[app] adding object type: %r',
@@ -232,7 +235,7 @@ class SphinxComponentRegistry:
232
235
  directive = type(directivename,
233
236
  (GenericObject, object),
234
237
  {'indextemplate': indextemplate,
235
- 'parse_node': staticmethod(parse_node),
238
+ 'parse_node': parse_node and staticmethod(parse_node),
236
239
  'doc_field_types': doc_field_types})
237
240
 
238
241
  self.add_directive_to_domain('std', directivename, directive)
@@ -312,7 +315,7 @@ class SphinxComponentRegistry:
312
315
  def add_translation_handlers(
313
316
  self,
314
317
  node: type[Element],
315
- **kwargs: tuple[Callable | None, Callable | None],
318
+ **kwargs: tuple[Callable, Callable | None],
316
319
  ) -> None:
317
320
  logger.debug('[app] adding translation_handlers: %r, %r', node, kwargs)
318
321
  for builder_name, handlers in kwargs.items():
@@ -333,7 +336,8 @@ class SphinxComponentRegistry:
333
336
  try:
334
337
  return builder.default_translator_class
335
338
  except AttributeError as err:
336
- raise AttributeError(f'translator not found for {builder.name}') from err
339
+ msg = f'translator not found for {builder.name}'
340
+ raise AttributeError(msg) from err
337
341
 
338
342
  def create_translator(self, builder: Builder, *args: Any) -> nodes.NodeVisitor:
339
343
  translator_class = self.get_translator_class(builder)
@@ -410,16 +414,18 @@ class SphinxComponentRegistry:
410
414
  def add_html_math_renderer(
411
415
  self,
412
416
  name: str,
413
- inline_renderers: tuple[Callable | None, Callable | None] | None,
414
- block_renderers: tuple[Callable | None, Callable | None] | None,
417
+ inline_renderers: tuple[Callable, Callable | None] | None,
418
+ block_renderers: tuple[Callable, Callable | None] | None,
415
419
  ) -> None:
416
420
  logger.debug('[app] adding html_math_renderer: %s, %r, %r',
417
421
  name, inline_renderers, block_renderers)
418
422
  if name in self.html_inline_math_renderers:
419
423
  raise ExtensionError(__('math renderer %s is already registered') % name)
420
424
 
421
- self.html_inline_math_renderers[name] = inline_renderers
422
- self.html_block_math_renderers[name] = block_renderers
425
+ if inline_renderers is not None:
426
+ self.html_inline_math_renderers[name] = inline_renderers
427
+ if block_renderers is not None:
428
+ self.html_block_math_renderers[name] = block_renderers
423
429
 
424
430
  def add_html_theme(self, name: str, theme_path: str) -> None:
425
431
  self.html_themes[name] = theme_path
sphinx/roles.py CHANGED
@@ -9,17 +9,20 @@ import docutils.parsers.rst.directives
9
9
  import docutils.parsers.rst.roles
10
10
  import docutils.parsers.rst.states
11
11
  from docutils import nodes, utils
12
- from docutils.nodes import Element, Node, TextElement, system_message
13
12
 
14
13
  from sphinx import addnodes
15
14
  from sphinx.locale import _, __
16
15
  from sphinx.util import ws_re
17
16
  from sphinx.util.docutils import ReferenceRole, SphinxRole
18
- from sphinx.util.typing import RoleFunction
19
17
 
20
18
  if TYPE_CHECKING:
19
+ from collections.abc import Sequence
20
+
21
+ from docutils.nodes import Element, Node, TextElement, system_message
22
+
21
23
  from sphinx.application import Sphinx
22
24
  from sphinx.environment import BuildEnvironment
25
+ from sphinx.util.typing import RoleFunction
23
26
 
24
27
 
25
28
  generic_docroles = {
@@ -365,8 +368,10 @@ class Abbreviation(SphinxRole):
365
368
  # TODO: Change to use `SphinxRole` once SphinxRole is fixed to support options.
366
369
  def code_role(name: str, rawtext: str, text: str, lineno: int,
367
370
  inliner: docutils.parsers.rst.states.Inliner,
368
- options: dict = {}, content: list[str] = [],
371
+ options: dict | None = None, content: Sequence[str] = (),
369
372
  ) -> tuple[list[Node], list[system_message]]:
373
+ if options is None:
374
+ options = {}
370
375
  options = options.copy()
371
376
  docutils.parsers.rst.roles.set_classes(options)
372
377
  language = options.get('language', '')
@@ -384,7 +389,7 @@ def code_role(name: str, rawtext: str, text: str, lineno: int,
384
389
  return [node], []
385
390
 
386
391
 
387
- code_role.options = { # type: ignore
392
+ code_role.options = { # type: ignore[attr-defined]
388
393
  'class': docutils.parsers.rst.directives.class_option,
389
394
  'language': docutils.parsers.rst.directives.unchanged,
390
395
  }
sphinx/search/__init__.py CHANGED
@@ -9,29 +9,17 @@ import pickle
9
9
  import re
10
10
  from importlib import import_module
11
11
  from os import path
12
- from typing import (
13
- IO,
14
- Any,
15
- Callable,
16
- Dict,
17
- Generator,
18
- Iterable,
19
- Iterator,
20
- List,
21
- Optional,
22
- Sequence,
23
- Set,
24
- Tuple,
25
- Type,
26
- Union,
27
- )
12
+ from typing import IO, TYPE_CHECKING, Any
28
13
 
29
14
  from docutils import nodes
30
15
  from docutils.nodes import Element, Node
31
16
 
32
17
  from sphinx import addnodes, package_dir
33
18
  from sphinx.environment import BuildEnvironment
34
- from sphinx.util import split_index_msg
19
+ from sphinx.util.index_entries import split_index_msg
20
+
21
+ if TYPE_CHECKING:
22
+ from collections.abc import Iterable
35
23
 
36
24
 
37
25
  class SearchLanguage:
sphinx/search/da.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Dict
5
+ from typing import TYPE_CHECKING, Dict
6
6
 
7
7
  import snowballstemmer
8
8