Sphinx 7.1.2__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 +18 -11
  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.2.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.2.dist-info/RECORD +0 -564
  311. {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/LICENSE +0 -0
  312. {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/WHEEL +0 -0
  313. {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/entry_points.txt +0 -0
sphinx/domains/std.py CHANGED
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import re
6
6
  from copy import copy
7
- from typing import TYPE_CHECKING, Any, Callable, Final, Iterable, Iterator, cast
7
+ from typing import TYPE_CHECKING, Any, Callable, Final, cast
8
8
 
9
9
  from docutils import nodes
10
10
  from docutils.nodes import Element, Node, system_message
@@ -20,12 +20,14 @@ from sphinx.roles import EmphasizedLiteral, XRefRole
20
20
  from sphinx.util import docname_join, logging, ws_re
21
21
  from sphinx.util.docutils import SphinxDirective
22
22
  from sphinx.util.nodes import clean_astext, make_id, make_refnode
23
- from sphinx.util.typing import OptionSpec, RoleFunction
24
23
 
25
24
  if TYPE_CHECKING:
25
+ from collections.abc import Iterable, Iterator
26
+
26
27
  from sphinx.application import Sphinx
27
28
  from sphinx.builders import Builder
28
29
  from sphinx.environment import BuildEnvironment
30
+ from sphinx.util.typing import OptionSpec, RoleFunction
29
31
 
30
32
  logger = logging.getLogger(__name__)
31
33
 
@@ -214,7 +216,7 @@ class Cmdoption(ObjectDescription[str]):
214
216
 
215
217
  self.state.document.note_explicit_target(signode)
216
218
 
217
- domain = cast(StandardDomain, self.env.get_domain('std'))
219
+ domain = self.env.domains['std']
218
220
  for optname in signode.get('allnames', []):
219
221
  domain.add_program_option(currprogram, optname,
220
222
  self.env.docname, signode['ids'][0])
@@ -381,10 +383,12 @@ class Glossary(SphinxDirective):
381
383
  parts = split_term_classifiers(line)
382
384
  # parse the term with inline markup
383
385
  # classifiers (parts[1:]) will not be shown on doctree
384
- textnodes, sysmsg = self.state.inline_text(parts[0], lineno)
386
+ textnodes, sysmsg = self.state.inline_text(parts[0], # type: ignore[arg-type]
387
+ lineno)
385
388
 
386
389
  # use first classifier as a index key
387
- term = make_glossary_term(self.env, textnodes, parts[1], source, lineno,
390
+ term = make_glossary_term(self.env, textnodes,
391
+ parts[1], source, lineno, # type: ignore[arg-type]
388
392
  node_id=None, document=self.state.document)
389
393
  term.rawsource = line
390
394
  system_messages.extend(sysmsg)
@@ -645,7 +649,7 @@ class StandardDomain(Domain):
645
649
  self._terms[term.lower()] = (self.env.docname, labelid)
646
650
 
647
651
  @property
648
- def progoptions(self) -> dict[tuple[str, str], tuple[str, str]]:
652
+ def progoptions(self) -> dict[tuple[str | None, str], tuple[str, str]]:
649
653
  return self.data.setdefault('progoptions', {}) # (program, name) -> docname, labelid
650
654
 
651
655
  @property
@@ -704,7 +708,7 @@ class StandardDomain(Domain):
704
708
  node = document.ids[labelid]
705
709
  if isinstance(node, nodes.target) and 'refid' in node:
706
710
  # indirect hyperlink targets
707
- node = document.ids.get(node['refid'])
711
+ node = document.ids.get(node['refid']) # type: ignore[assignment]
708
712
  labelid = node['names'][0]
709
713
  if (node.tagname == 'footnote' or
710
714
  'refuri' in node or
@@ -719,11 +723,11 @@ class StandardDomain(Domain):
719
723
  self.anonlabels[name] = docname, labelid
720
724
  if node.tagname == 'section':
721
725
  title = cast(nodes.title, node[0])
722
- sectname: str | None = clean_astext(title)
726
+ sectname = clean_astext(title)
723
727
  elif node.tagname == 'rubric':
724
728
  sectname = clean_astext(node)
725
729
  elif self.is_enumerable_node(node):
726
- sectname = self.get_numfig_title(node)
730
+ sectname = self.get_numfig_title(node) or ''
727
731
  if not sectname:
728
732
  continue
729
733
  else:
@@ -738,13 +742,14 @@ class StandardDomain(Domain):
738
742
  else:
739
743
  toctree = next(node.findall(addnodes.toctree), None)
740
744
  if toctree and toctree.get('caption'):
741
- sectname = toctree.get('caption')
745
+ sectname = toctree['caption']
742
746
  else:
743
747
  # anonymous-only labels
744
748
  continue
745
749
  self.labels[name] = docname, labelid, sectname
746
750
 
747
- def add_program_option(self, program: str, name: str, docname: str, labelid: str) -> None:
751
+ def add_program_option(self, program: str | None, name: str,
752
+ docname: str, labelid: str) -> None:
748
753
  # prefer first command option entry
749
754
  if (program, name) not in self.progoptions:
750
755
  self.progoptions[program, name] = (docname, labelid)
@@ -825,6 +830,7 @@ class StandardDomain(Domain):
825
830
  return None
826
831
 
827
832
  target_node = env.get_doctree(docname).ids.get(labelid)
833
+ assert target_node is not None
828
834
  figtype = self.get_enumerable_node_type(target_node)
829
835
  if figtype is None:
830
836
  return None
@@ -983,9 +989,9 @@ class StandardDomain(Domain):
983
989
  key = (objtype, ltarget)
984
990
  if key in self.objects:
985
991
  docname, labelid = self.objects[key]
986
- results.append(('std:' + self.role_for_objtype(objtype),
987
- make_refnode(builder, fromdocname, docname,
988
- labelid, contnode)))
992
+ role = 'std:' + self.role_for_objtype(objtype) # type: ignore[operator]
993
+ results.append((role, make_refnode(builder, fromdocname, docname,
994
+ labelid, contnode)))
989
995
  return results
990
996
 
991
997
  def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
@@ -1096,7 +1102,7 @@ def warn_missing_reference(app: Sphinx, domain: Domain, node: pending_xref,
1096
1102
  return None
1097
1103
  else:
1098
1104
  target = node['reftarget']
1099
- if target not in domain.anonlabels: # type: ignore
1105
+ if target not in domain.anonlabels: # type: ignore[attr-defined]
1100
1106
  msg = __('undefined label: %r')
1101
1107
  else:
1102
1108
  msg = __('Failed to create a cross reference. A title or caption not found: %r')
@@ -5,23 +5,16 @@ from __future__ import annotations
5
5
  import functools
6
6
  import os
7
7
  import pickle
8
+ import time
8
9
  from collections import defaultdict
9
10
  from copy import copy
10
- from datetime import datetime, timezone
11
11
  from os import path
12
- from typing import TYPE_CHECKING, Any, Callable, Generator, Iterator
13
-
14
- from docutils import nodes
15
- from docutils.nodes import Node
12
+ from typing import TYPE_CHECKING, Any, Callable
16
13
 
17
14
  from sphinx import addnodes
18
- from sphinx.config import Config
19
- from sphinx.domains import Domain
20
- from sphinx.environment.adapters.toctree import TocTree
15
+ from sphinx.environment.adapters import toctree as toctree_adapters
21
16
  from sphinx.errors import BuildEnvironmentError, DocumentError, ExtensionError, SphinxError
22
- from sphinx.events import EventManager
23
17
  from sphinx.locale import __
24
- from sphinx.project import Project
25
18
  from sphinx.transforms import SphinxTransformer
26
19
  from sphinx.util import DownloadFiles, FilenameUniqDict, logging
27
20
  from sphinx.util.docutils import LoggingReporter
@@ -30,8 +23,18 @@ from sphinx.util.nodes import is_translatable
30
23
  from sphinx.util.osutil import canon_path, os_path
31
24
 
32
25
  if TYPE_CHECKING:
26
+ from collections.abc import Generator, Iterator
27
+ from pathlib import Path
28
+
29
+ from docutils import nodes
30
+ from docutils.nodes import Node
31
+
33
32
  from sphinx.application import Sphinx
34
33
  from sphinx.builders import Builder
34
+ from sphinx.config import Config
35
+ from sphinx.domains import Domain
36
+ from sphinx.events import EventManager
37
+ from sphinx.project import Project
35
38
 
36
39
  logger = logging.getLogger(__name__)
37
40
 
@@ -55,9 +58,10 @@ default_settings: dict[str, Any] = {
55
58
 
56
59
  # This is increased every time an environment attribute is added
57
60
  # or changed to properly invalidate pickle files.
58
- ENV_VERSION = 58
61
+ ENV_VERSION = 60
59
62
 
60
63
  # config status
64
+ CONFIG_UNSET = -1
61
65
  CONFIG_OK = 1
62
66
  CONFIG_NEW = 2
63
67
  CONFIG_CHANGED = 3
@@ -76,7 +80,8 @@ versioning_conditions: dict[str, bool | Callable] = {
76
80
  }
77
81
 
78
82
  if TYPE_CHECKING:
79
- from typing import Literal, MutableMapping
83
+ from collections.abc import MutableMapping
84
+ from typing import Literal
80
85
 
81
86
  from typing_extensions import overload
82
87
 
@@ -142,25 +147,25 @@ class BuildEnvironment:
142
147
  # --------- ENVIRONMENT INITIALIZATION -------------------------------------
143
148
 
144
149
  def __init__(self, app: Sphinx):
145
- self.app: Sphinx = None
146
- self.doctreedir: str = None
147
- self.srcdir: str = None
148
- self.config: Config = None
149
- self.config_status: int = None
150
- self.config_status_extra: str = None
151
- self.events: EventManager = None
152
- self.project: Project = None
153
- self.version: dict[str, str] = None
150
+ self.app: Sphinx = app
151
+ self.doctreedir: Path = app.doctreedir
152
+ self.srcdir: Path = app.srcdir
153
+ self.config: Config = None # type: ignore[assignment]
154
+ self.config_status: int = CONFIG_UNSET
155
+ self.config_status_extra: str = ''
156
+ self.events: EventManager = app.events
157
+ self.project: Project = app.project
158
+ self.version: dict[str, str] = app.registry.get_envversion(app)
154
159
 
155
160
  # the method of doctree versioning; see set_versioning_method
156
- self.versioning_condition: bool | Callable = None
157
- self.versioning_compare: bool = None
161
+ self.versioning_condition: bool | Callable | None = None
162
+ self.versioning_compare: bool | None = None
158
163
 
159
164
  # all the registered domains, set by the application
160
165
  self.domains = _DomainsType()
161
166
 
162
167
  # the docutils settings for building
163
- self.settings = default_settings.copy()
168
+ self.settings: dict[str, Any] = default_settings.copy()
164
169
  self.settings['env'] = self
165
170
 
166
171
  # All "docnames" here are /-separated and relative and exclude
@@ -372,7 +377,7 @@ class BuildEnvironment:
372
377
 
373
378
  This possibly comes from a parallel build process.
374
379
  """
375
- docnames = set(docnames) # type: ignore
380
+ docnames = set(docnames) # type: ignore[assignment]
376
381
  for docname in docnames:
377
382
  self.all_docs[docname] = other.all_docs[docname]
378
383
  self.included[docname] = other.included[docname]
@@ -383,7 +388,7 @@ class BuildEnvironment:
383
388
  domain.merge_domaindata(docnames, other.domaindata[domainname])
384
389
  self.events.emit('env-merge-info', self, docnames, other)
385
390
 
386
- def path2doc(self, filename: str) -> str | None:
391
+ def path2doc(self, filename: str | os.PathLike[str]) -> str | None:
387
392
  """Return the docname for the filename if the file is document.
388
393
 
389
394
  *filename* should be absolute or relative to the source directory.
@@ -483,12 +488,9 @@ class BuildEnvironment:
483
488
  mtime = self.all_docs[docname]
484
489
  newmtime = _last_modified_time(self.doc2path(docname))
485
490
  if newmtime > mtime:
486
- # convert integer microseconds to floating-point seconds,
487
- # and then to timezone-aware datetime objects.
488
- mtime_dt = datetime.fromtimestamp(mtime / 1_000_000, tz=timezone.utc)
489
- newmtime_dt = datetime.fromtimestamp(mtime / 1_000_000, tz=timezone.utc)
490
491
  logger.debug('[build target] outdated %r: %s -> %s',
491
- docname, mtime_dt, newmtime_dt)
492
+ docname,
493
+ _format_modified_time(mtime), _format_modified_time(newmtime))
492
494
  changed.add(docname)
493
495
  continue
494
496
  # finally, check the mtime of dependencies
@@ -497,10 +499,19 @@ class BuildEnvironment:
497
499
  # this will do the right thing when dep is absolute too
498
500
  deppath = path.join(self.srcdir, dep)
499
501
  if not path.isfile(deppath):
502
+ logger.debug(
503
+ '[build target] changed %r missing dependency %r',
504
+ docname, deppath,
505
+ )
500
506
  changed.add(docname)
501
507
  break
502
508
  depmtime = _last_modified_time(deppath)
503
509
  if depmtime > mtime:
510
+ logger.debug(
511
+ '[build target] outdated %r from dependency %r: %s -> %s',
512
+ docname, deppath,
513
+ _format_modified_time(mtime), _format_modified_time(depmtime),
514
+ )
504
515
  changed.add(docname)
505
516
  break
506
517
  except OSError:
@@ -625,9 +636,11 @@ class BuildEnvironment:
625
636
 
626
637
  # now, resolve all toctree nodes
627
638
  for toctreenode in doctree.findall(addnodes.toctree):
628
- result = TocTree(self).resolve(docname, builder, toctreenode,
629
- prune=prune_toctrees,
630
- includehidden=includehidden)
639
+ result = toctree_adapters._resolve_toctree(
640
+ self, docname, builder, toctreenode,
641
+ prune=prune_toctrees,
642
+ includehidden=includehidden,
643
+ )
631
644
  if result is None:
632
645
  toctreenode.parent.replace(toctreenode, [])
633
646
  else:
@@ -649,9 +662,14 @@ class BuildEnvironment:
649
662
  If *collapse* is True, all branches not containing docname will
650
663
  be collapsed.
651
664
  """
652
- return TocTree(self).resolve(docname, builder, toctree, prune,
653
- maxdepth, titles_only, collapse,
654
- includehidden)
665
+ return toctree_adapters._resolve_toctree(
666
+ self, docname, builder, toctree,
667
+ prune=prune,
668
+ maxdepth=maxdepth,
669
+ titles_only=titles_only,
670
+ collapse=collapse,
671
+ includehidden=includehidden,
672
+ )
655
673
 
656
674
  def resolve_references(self, doctree: nodes.document, fromdocname: str,
657
675
  builder: Builder) -> None:
@@ -675,38 +693,21 @@ class BuildEnvironment:
675
693
  self.events.emit('doctree-resolved', doctree, docname)
676
694
 
677
695
  def collect_relations(self) -> dict[str, list[str | None]]:
678
- traversed = set()
679
-
680
- def traverse_toctree(
681
- parent: str | None, docname: str,
682
- ) -> Iterator[tuple[str | None, str]]:
683
- if parent == docname:
684
- logger.warning(__('self referenced toctree found. Ignored.'),
685
- location=docname, type='toc',
686
- subtype='circular')
687
- return
688
-
689
- # traverse toctree by pre-order
690
- yield parent, docname
691
- traversed.add(docname)
692
-
693
- for child in (self.toctree_includes.get(docname) or []):
694
- for subparent, subdocname in traverse_toctree(docname, child):
695
- if subdocname not in traversed:
696
- yield subparent, subdocname
697
- traversed.add(subdocname)
696
+ traversed: set[str] = set()
698
697
 
699
698
  relations = {}
700
- docnames = traverse_toctree(None, self.config.root_doc)
701
- prevdoc = None
699
+ docnames = _traverse_toctree(
700
+ traversed, None, self.config.root_doc, self.toctree_includes,
701
+ )
702
+ prev_doc = None
702
703
  parent, docname = next(docnames)
703
- for nextparent, nextdoc in docnames:
704
- relations[docname] = [parent, prevdoc, nextdoc]
705
- prevdoc = docname
706
- docname = nextdoc
707
- parent = nextparent
704
+ for next_parent, next_doc in docnames:
705
+ relations[docname] = [parent, prev_doc, next_doc]
706
+ prev_doc = docname
707
+ docname = next_doc
708
+ parent = next_parent
708
709
 
709
- relations[docname] = [parent, prevdoc, None]
710
+ relations[docname] = [parent, prev_doc, None]
710
711
 
711
712
  return relations
712
713
 
@@ -745,3 +746,34 @@ def _last_modified_time(filename: str | os.PathLike[str]) -> int:
745
746
 
746
747
  # upside-down floor division to get the ceiling
747
748
  return -(os.stat(filename).st_mtime_ns // -1_000)
749
+
750
+
751
+ def _format_modified_time(timestamp: int) -> str:
752
+ """Return an RFC 3339 formatted string representing the given timestamp."""
753
+ seconds, fraction = divmod(timestamp, 10**6)
754
+ return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(seconds)) + f'.{fraction//1_000}'
755
+
756
+
757
+ def _traverse_toctree(
758
+ traversed: set[str],
759
+ parent: str | None,
760
+ docname: str,
761
+ toctree_includes: dict[str, list[str]],
762
+ ) -> Iterator[tuple[str | None, str]]:
763
+ if parent == docname:
764
+ logger.warning(__('self referenced toctree found. Ignored.'),
765
+ location=docname, type='toc',
766
+ subtype='circular')
767
+ return
768
+
769
+ # traverse toctree by pre-order
770
+ yield parent, docname
771
+ traversed.add(docname)
772
+
773
+ for child in toctree_includes.get(docname, ()):
774
+ for sub_parent, sub_docname in _traverse_toctree(
775
+ traversed, docname, child, toctree_includes,
776
+ ):
777
+ if sub_docname not in traversed:
778
+ yield sub_parent, sub_docname
779
+ traversed.add(sub_docname)
@@ -5,13 +5,16 @@ from __future__ import annotations
5
5
  import re
6
6
  import unicodedata
7
7
  from itertools import groupby
8
- from typing import Any, Literal
8
+ from typing import TYPE_CHECKING, Any, Literal
9
9
 
10
- from sphinx.builders import Builder
11
- from sphinx.environment import BuildEnvironment
12
10
  from sphinx.errors import NoUri
13
11
  from sphinx.locale import _, __
14
- from sphinx.util import logging, split_into
12
+ from sphinx.util import logging
13
+ from sphinx.util.index_entries import _split_into
14
+
15
+ if TYPE_CHECKING:
16
+ from sphinx.builders import Builder
17
+ from sphinx.environment import BuildEnvironment
15
18
 
16
19
  logger = logging.getLogger(__name__)
17
20
 
@@ -41,20 +44,20 @@ class IndexEntries:
41
44
  try:
42
45
  if entry_type == 'single':
43
46
  try:
44
- entry, sub_entry = split_into(2, 'single', value)
47
+ entry, sub_entry = _split_into(2, 'single', value)
45
48
  except ValueError:
46
- entry, = split_into(1, 'single', value)
49
+ entry, = _split_into(1, 'single', value)
47
50
  sub_entry = ''
48
51
  _add_entry(entry, sub_entry, main,
49
52
  dic=new, link=uri, key=category_key)
50
53
  elif entry_type == 'pair':
51
- first, second = split_into(2, 'pair', value)
54
+ first, second = _split_into(2, 'pair', value)
52
55
  _add_entry(first, second, main,
53
56
  dic=new, link=uri, key=category_key)
54
57
  _add_entry(second, first, main,
55
58
  dic=new, link=uri, key=category_key)
56
59
  elif entry_type == 'triple':
57
- first, second, third = split_into(3, 'triple', value)
60
+ first, second, third = _split_into(3, 'triple', value)
58
61
  _add_entry(first, second + ' ' + third, main,
59
62
  dic=new, link=uri, key=category_key)
60
63
  _add_entry(second, third + ', ' + first, main,
@@ -62,11 +65,11 @@ class IndexEntries:
62
65
  _add_entry(third, first + ' ' + second, main,
63
66
  dic=new, link=uri, key=category_key)
64
67
  elif entry_type == 'see':
65
- first, second = split_into(2, 'see', value)
68
+ first, second = _split_into(2, 'see', value)
66
69
  _add_entry(first, _('see %s') % second, None,
67
70
  dic=new, link=False, key=category_key)
68
71
  elif entry_type == 'seealso':
69
- first, second = split_into(2, 'see', value)
72
+ first, second = _split_into(2, 'see', value)
70
73
  _add_entry(first, _('see also %s') % second, None,
71
74
  dic=new, link=False, key=category_key)
72
75
  else: