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
@@ -0,0 +1,116 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import zlib
5
+ from typing import TYPE_CHECKING
6
+
7
+ from sphinx.errors import ThemeError
8
+
9
+ if TYPE_CHECKING:
10
+ from pathlib import Path
11
+
12
+
13
+ class _CascadingStyleSheet:
14
+ filename: str | os.PathLike[str]
15
+ priority: int
16
+ attributes: dict[str, str]
17
+
18
+ def __init__(
19
+ self,
20
+ filename: str | os.PathLike[str], /, *,
21
+ priority: int = 500,
22
+ rel: str = 'stylesheet',
23
+ type: str = 'text/css',
24
+ **attributes: str,
25
+ ) -> None:
26
+ object.__setattr__(self, 'filename', filename)
27
+ object.__setattr__(self, 'priority', priority)
28
+ object.__setattr__(self, 'attributes', {'rel': rel, 'type': type, **attributes})
29
+
30
+ def __str__(self):
31
+ attr = ', '.join(f'{k}={v!r}' for k, v in self.attributes.items())
32
+ return (f'{self.__class__.__name__}({self.filename!r}, '
33
+ f'priority={self.priority}, '
34
+ f'{attr})')
35
+
36
+ def __eq__(self, other):
37
+ if not isinstance(other, _CascadingStyleSheet):
38
+ return NotImplemented
39
+ return (self.filename == other.filename
40
+ and self.priority == other.priority
41
+ and self.attributes == other.attributes)
42
+
43
+ def __hash__(self):
44
+ return hash((self.filename, self.priority, *sorted(self.attributes.items())))
45
+
46
+ def __setattr__(self, key, value):
47
+ msg = f'{self.__class__.__name__} is immutable'
48
+ raise AttributeError(msg)
49
+
50
+ def __delattr__(self, key):
51
+ msg = f'{self.__class__.__name__} is immutable'
52
+ raise AttributeError(msg)
53
+
54
+
55
+ class _JavaScript:
56
+ filename: str | os.PathLike[str]
57
+ priority: int
58
+ attributes: dict[str, str]
59
+
60
+ def __init__(
61
+ self,
62
+ filename: str | os.PathLike[str], /, *,
63
+ priority: int = 500,
64
+ **attributes: str,
65
+ ) -> None:
66
+ object.__setattr__(self, 'filename', filename)
67
+ object.__setattr__(self, 'priority', priority)
68
+ object.__setattr__(self, 'attributes', attributes)
69
+
70
+ def __str__(self):
71
+ attr = ''
72
+ if self.attributes:
73
+ attr = ', ' + ', '.join(f'{k}={v!r}' for k, v in self.attributes.items())
74
+ return (f'{self.__class__.__name__}({self.filename!r}, '
75
+ f'priority={self.priority}'
76
+ f'{attr})')
77
+
78
+ def __eq__(self, other):
79
+ if not isinstance(other, _JavaScript):
80
+ return NotImplemented
81
+ return (self.filename == other.filename
82
+ and self.priority == other.priority
83
+ and self.attributes == other.attributes)
84
+
85
+ def __hash__(self):
86
+ return hash((self.filename, self.priority, *sorted(self.attributes.items())))
87
+
88
+ def __setattr__(self, key, value):
89
+ msg = f'{self.__class__.__name__} is immutable'
90
+ raise AttributeError(msg)
91
+
92
+ def __delattr__(self, key):
93
+ msg = f'{self.__class__.__name__} is immutable'
94
+ raise AttributeError(msg)
95
+
96
+
97
+ def _file_checksum(outdir: Path, filename: str | os.PathLike[str]) -> str:
98
+ filename = os.fspath(filename)
99
+ # Don't generate checksums for HTTP URIs
100
+ if '://' in filename:
101
+ return ''
102
+ # Some themes and extensions have used query strings
103
+ # for a similar asset checksum feature.
104
+ # As we cannot safely strip the query string,
105
+ # raise an error to the user.
106
+ if '?' in filename:
107
+ msg = f'Local asset file paths must not contain query strings: {filename!r}'
108
+ raise ThemeError(msg)
109
+ try:
110
+ # Remove all carriage returns to avoid checksum differences
111
+ content = outdir.joinpath(filename).read_bytes().translate(None, b'\r')
112
+ except FileNotFoundError:
113
+ return ''
114
+ if not content:
115
+ return ''
116
+ return f'{zlib.crc32(content):08x}'
@@ -3,14 +3,16 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import re
6
- from typing import Any
6
+ from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from docutils import nodes
9
9
 
10
- from sphinx.application import Sphinx
11
10
  from sphinx.transforms.post_transforms import SphinxPostTransform
12
11
  from sphinx.util.nodes import NodeMatcher
13
12
 
13
+ if TYPE_CHECKING:
14
+ from sphinx.application import Sphinx
15
+
14
16
 
15
17
  class KeyboardTransform(SphinxPostTransform):
16
18
  """Transform :kbd: role to more detailed form.
@@ -5,14 +5,12 @@ from __future__ import annotations
5
5
  import os
6
6
  import warnings
7
7
  from os import path
8
- from typing import Any, Iterable
8
+ from typing import TYPE_CHECKING, Any
9
9
 
10
10
  from docutils.frontend import OptionParser
11
- from docutils.nodes import Node
12
11
 
13
12
  import sphinx.builders.latex.nodes # noqa: F401,E501 # Workaround: import this before writer to avoid ImportError
14
13
  from sphinx import addnodes, highlighting, package_dir
15
- from sphinx.application import Sphinx
16
14
  from sphinx.builders import Builder
17
15
  from sphinx.builders.latex.constants import ADDITIONAL_SETTINGS, DEFAULT_SETTINGS, SHORTHANDOFF
18
16
  from sphinx.builders.latex.theming import Theme, ThemeFactory
@@ -22,7 +20,7 @@ from sphinx.environment.adapters.asset import ImageAdapter
22
20
  from sphinx.errors import NoUri, SphinxError
23
21
  from sphinx.locale import _, __
24
22
  from sphinx.util import logging, texescape
25
- from sphinx.util.console import bold, darkgreen # type: ignore
23
+ from sphinx.util.console import bold, darkgreen # type: ignore[attr-defined]
26
24
  from sphinx.util.display import progress_message, status_iterator
27
25
  from sphinx.util.docutils import SphinxFileOutput, new_document
28
26
  from sphinx.util.fileutil import copy_asset_file
@@ -35,6 +33,13 @@ from sphinx.writers.latex import LaTeXTranslator, LaTeXWriter
35
33
  # load docutils.nodes after loading sphinx.builders.latex.nodes
36
34
  from docutils import nodes # isort:skip
37
35
 
36
+ if TYPE_CHECKING:
37
+ from collections.abc import Iterable
38
+
39
+ from docutils.nodes import Node
40
+
41
+ from sphinx.application import Sphinx
42
+
38
43
  XINDY_LANG_OPTIONS = {
39
44
  # language codes from docutils.writers.latex2e.Babel
40
45
  # ! xindy language names may differ from those in use by LaTeX/babel
@@ -116,7 +121,7 @@ class LaTeXBuilder(Builder):
116
121
  default_translator_class = LaTeXTranslator
117
122
 
118
123
  def init(self) -> None:
119
- self.babel: ExtBabel = None
124
+ self.babel: ExtBabel
120
125
  self.context: dict[str, Any] = {}
121
126
  self.docnames: Iterable[str] = {}
122
127
  self.document_data: list[tuple[str, str, str, str, str, bool]] = []
@@ -153,7 +158,7 @@ class LaTeXBuilder(Builder):
153
158
  logger.warning(__('"latex_documents" config value references unknown '
154
159
  'document %s'), docname)
155
160
  continue
156
- self.document_data.append(entry) # type: ignore
161
+ self.document_data.append(entry) # type: ignore[arg-type]
157
162
  if docname.endswith(SEP + 'index'):
158
163
  docname = docname[:-5]
159
164
  self.titles.append((docname, entry[2]))
@@ -314,7 +319,7 @@ class LaTeXBuilder(Builder):
314
319
 
315
320
  def get_contentsname(self, indexfile: str) -> str:
316
321
  tree = self.env.get_doctree(indexfile)
317
- contentsname = None
322
+ contentsname = ''
318
323
  for toctree in tree.findall(addnodes.toctree):
319
324
  if 'caption' in toctree:
320
325
  contentsname = toctree['caption']
@@ -4,13 +4,16 @@ from __future__ import annotations
4
4
 
5
5
  import configparser
6
6
  from os import path
7
+ from typing import TYPE_CHECKING
7
8
 
8
- from sphinx.application import Sphinx
9
- from sphinx.config import Config
10
9
  from sphinx.errors import ThemeError
11
10
  from sphinx.locale import __
12
11
  from sphinx.util import logging
13
12
 
13
+ if TYPE_CHECKING:
14
+ from sphinx.application import Sphinx
15
+ from sphinx.config import Config
16
+
14
17
  logger = logging.getLogger(__name__)
15
18
 
16
19
 
@@ -2,14 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, cast
5
+ from typing import TYPE_CHECKING, Any, cast
6
6
 
7
7
  from docutils import nodes
8
- from docutils.nodes import Element, Node
9
8
  from docutils.transforms.references import Substitutions
10
9
 
11
10
  from sphinx import addnodes
12
- from sphinx.application import Sphinx
13
11
  from sphinx.builders.latex.nodes import (
14
12
  captioned_literal_block,
15
13
  footnotemark,
@@ -23,6 +21,11 @@ from sphinx.transforms import SphinxTransform
23
21
  from sphinx.transforms.post_transforms import SphinxPostTransform
24
22
  from sphinx.util.nodes import NodeMatcher
25
23
 
24
+ if TYPE_CHECKING:
25
+ from docutils.nodes import Element, Node
26
+
27
+ from sphinx.application import Sphinx
28
+
26
29
  URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
27
30
 
28
31
 
@@ -2,12 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import contextlib
5
6
  import json
6
7
  import re
7
8
  import socket
8
9
  import time
9
- from datetime import datetime, timezone
10
- from email.utils import parsedate_to_datetime
10
+ from email.utils import parsedate_tz
11
11
  from html.parser import HTMLParser
12
12
  from os import path
13
13
  from queue import PriorityQueue, Queue
@@ -22,11 +22,18 @@ from sphinx.builders.dummy import DummyBuilder
22
22
  from sphinx.locale import __
23
23
  from sphinx.transforms.post_transforms import SphinxPostTransform
24
24
  from sphinx.util import encode_uri, logging, requests
25
- from sphinx.util.console import darkgray, darkgreen, purple, red, turquoise # type: ignore
25
+ from sphinx.util.console import ( # type: ignore[attr-defined]
26
+ darkgray,
27
+ darkgreen,
28
+ purple,
29
+ red,
30
+ turquoise,
31
+ )
26
32
  from sphinx.util.nodes import get_node_line
27
33
 
28
34
  if TYPE_CHECKING:
29
- from typing import Any, Callable, Generator, Iterator
35
+ from collections.abc import Generator, Iterator
36
+ from typing import Any, Callable
30
37
 
31
38
  from requests import Response
32
39
 
@@ -65,7 +72,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
65
72
 
66
73
  output_text = path.join(self.outdir, 'output.txt')
67
74
  output_json = path.join(self.outdir, 'output.json')
68
- with open(output_text, 'w', encoding='utf-8') as self.txt_outfile,\
75
+ with open(output_text, 'w', encoding='utf-8') as self.txt_outfile, \
69
76
  open(output_json, 'w', encoding='utf-8') as self.json_outfile:
70
77
  for result in checker.check(self.hyperlinks):
71
78
  self.process_result(result)
@@ -298,14 +305,12 @@ class HyperlinkAvailabilityCheckWorker(Thread):
298
305
  break
299
306
 
300
307
  netloc = urlsplit(uri).netloc
301
- try:
308
+ with contextlib.suppress(KeyError):
302
309
  # Refresh rate limit.
303
310
  # When there are many links in the queue, workers are all stuck waiting
304
311
  # for responses, but the builder keeps queuing. Links in the queue may
305
312
  # have been queued before rate limits were discovered.
306
313
  next_check = self.rate_limits[netloc].next_check
307
- except KeyError:
308
- pass
309
314
  if next_check > time.time():
310
315
  # Sleep before putting message back in the queue to avoid
311
316
  # waking up other threads.
@@ -406,7 +411,8 @@ class HyperlinkAvailabilityCheckWorker(Thread):
406
411
  _user_agent=self.user_agent,
407
412
  _tls_info=(self.tls_verify, self.tls_cacerts),
408
413
  ) as response:
409
- if response.ok and anchor and not contains_anchor(response, anchor):
414
+ if (self.check_anchors and response.ok and anchor
415
+ and not contains_anchor(response, anchor)):
410
416
  raise Exception(__(f'Anchor {anchor!r} not found'))
411
417
 
412
418
  # Copy data we need from the (closed) response
@@ -482,14 +488,16 @@ class HyperlinkAvailabilityCheckWorker(Thread):
482
488
  except ValueError:
483
489
  try:
484
490
  # An HTTP-date: time of next attempt.
485
- until = parsedate_to_datetime(retry_after)
486
- except (TypeError, ValueError):
491
+ parsed = parsedate_tz(retry_after)
492
+ assert parsed is not None
493
+ # the 10th element is the GMT offset in seconds
494
+ next_check = time.mktime(parsed[:9]) - (parsed[9] or 0)
495
+ except (AssertionError, TypeError, ValueError):
487
496
  # TypeError: Invalid date format.
488
497
  # ValueError: Invalid date, e.g. Oct 52th.
489
498
  pass
490
499
  else:
491
- next_check = datetime.timestamp(until)
492
- delay = (until - datetime.now(timezone.utc)).total_seconds()
500
+ delay = next_check - time.time()
493
501
  else:
494
502
  next_check = time.time() + delay
495
503
  netloc = urlsplit(response_url).netloc
@@ -4,23 +4,25 @@ from __future__ import annotations
4
4
 
5
5
  import warnings
6
6
  from os import path
7
- from typing import Any
7
+ from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from docutils.frontend import OptionParser
10
10
  from docutils.io import FileOutput
11
11
 
12
12
  from sphinx import addnodes
13
- from sphinx.application import Sphinx
14
13
  from sphinx.builders import Builder
15
- from sphinx.config import Config
16
14
  from sphinx.locale import __
17
15
  from sphinx.util import logging
18
- from sphinx.util.console import darkgreen # type: ignore
16
+ from sphinx.util.console import darkgreen # type: ignore[attr-defined]
19
17
  from sphinx.util.display import progress_message
20
18
  from sphinx.util.nodes import inline_all_toctrees
21
19
  from sphinx.util.osutil import ensuredir, make_filename_from_project
22
20
  from sphinx.writers.manpage import ManualPageTranslator, ManualPageWriter
23
21
 
22
+ if TYPE_CHECKING:
23
+ from sphinx.application import Sphinx
24
+ from sphinx.config import Config
25
+
24
26
  logger = logging.getLogger(__name__)
25
27
 
26
28
 
@@ -3,20 +3,23 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from os import path
6
- from typing import Any
6
+ from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from docutils import nodes
9
- from docutils.nodes import Node
10
9
 
11
- from sphinx.application import Sphinx
12
10
  from sphinx.builders.html import StandaloneHTMLBuilder
13
- from sphinx.environment.adapters.toctree import TocTree
11
+ from sphinx.environment.adapters.toctree import global_toctree_for_doc
14
12
  from sphinx.locale import __
15
13
  from sphinx.util import logging
16
- from sphinx.util.console import darkgreen # type: ignore
14
+ from sphinx.util.console import darkgreen # type: ignore[attr-defined]
17
15
  from sphinx.util.display import progress_message
18
16
  from sphinx.util.nodes import inline_all_toctrees
19
17
 
18
+ if TYPE_CHECKING:
19
+ from docutils.nodes import Node
20
+
21
+ from sphinx.application import Sphinx
22
+
20
23
  logger = logging.getLogger(__name__)
21
24
 
22
25
 
@@ -61,9 +64,13 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
61
64
  refnode['refuri'] = fname + refuri[hashindex:]
62
65
 
63
66
  def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
64
- if 'includehidden' not in kwargs:
67
+ if kwargs.get('includehidden', 'false').lower() == 'false':
65
68
  kwargs['includehidden'] = False
66
- toctree = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwargs)
69
+ elif kwargs['includehidden'].lower() == 'true':
70
+ kwargs['includehidden'] = True
71
+ if kwargs.get('maxdepth') == '':
72
+ kwargs.pop('maxdepth')
73
+ toctree = global_toctree_for_doc(self.env, docname, self, collapse=collapse, **kwargs)
67
74
  if toctree is not None:
68
75
  self.fix_refuris(toctree)
69
76
  return self.render_partial(toctree)['fragment']
@@ -118,7 +125,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
118
125
 
119
126
  def get_doc_context(self, docname: str, body: str, metatags: str) -> dict[str, Any]:
120
127
  # no relation links...
121
- toctree = TocTree(self.env).get_toctree_for(self.config.root_doc, self, False)
128
+ toctree = global_toctree_for_doc(self.env, self.config.root_doc, self, collapse=False)
122
129
  # if there is no toctree, toc is None
123
130
  if toctree:
124
131
  self.fix_refuris(toctree)
@@ -146,7 +153,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
146
153
  docnames = self.env.all_docs
147
154
 
148
155
  with progress_message(__('preparing documents')):
149
- self.prepare_writing(docnames) # type: ignore
156
+ self.prepare_writing(docnames) # type: ignore[arg-type]
150
157
 
151
158
  with progress_message(__('assembling single document')):
152
159
  doctree = self.assemble_doctree()
@@ -5,22 +5,19 @@ from __future__ import annotations
5
5
  import os
6
6
  import warnings
7
7
  from os import path
8
- from typing import Any, Iterable
8
+ from typing import TYPE_CHECKING, Any
9
9
 
10
10
  from docutils import nodes
11
11
  from docutils.frontend import OptionParser
12
12
  from docutils.io import FileOutput
13
- from docutils.nodes import Node
14
13
 
15
14
  from sphinx import addnodes, package_dir
16
- from sphinx.application import Sphinx
17
15
  from sphinx.builders import Builder
18
- from sphinx.config import Config
19
16
  from sphinx.environment.adapters.asset import ImageAdapter
20
17
  from sphinx.errors import NoUri
21
18
  from sphinx.locale import _, __
22
19
  from sphinx.util import logging
23
- from sphinx.util.console import darkgreen # type: ignore
20
+ from sphinx.util.console import darkgreen # type: ignore[attr-defined]
24
21
  from sphinx.util.display import progress_message, status_iterator
25
22
  from sphinx.util.docutils import new_document
26
23
  from sphinx.util.fileutil import copy_asset_file
@@ -28,6 +25,14 @@ from sphinx.util.nodes import inline_all_toctrees
28
25
  from sphinx.util.osutil import SEP, ensuredir, make_filename_from_project
29
26
  from sphinx.writers.texinfo import TexinfoTranslator, TexinfoWriter
30
27
 
28
+ if TYPE_CHECKING:
29
+ from collections.abc import Iterable
30
+
31
+ from docutils.nodes import Node
32
+
33
+ from sphinx.application import Sphinx
34
+ from sphinx.config import Config
35
+
31
36
  logger = logging.getLogger(__name__)
32
37
  template_dir = os.path.join(package_dir, 'templates', 'texinfo')
33
38
 
@@ -78,7 +83,7 @@ class TexinfoBuilder(Builder):
78
83
  logger.warning(__('"texinfo_documents" config value references unknown '
79
84
  'document %s'), docname)
80
85
  continue
81
- self.document_data.append(entry) # type: ignore
86
+ self.document_data.append(entry) # type: ignore[arg-type]
82
87
  if docname.endswith(SEP + 'index'):
83
88
  docname = docname[:-5]
84
89
  self.titles.append((docname, entry[2]))
sphinx/builders/text.py CHANGED
@@ -3,18 +3,23 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from os import path
6
- from typing import Any, Iterator
6
+ from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from docutils.io import StringOutput
9
- from docutils.nodes import Node
10
9
 
11
- from sphinx.application import Sphinx
12
10
  from sphinx.builders import Builder
13
11
  from sphinx.locale import __
14
12
  from sphinx.util import logging
15
13
  from sphinx.util.osutil import ensuredir, os_path
16
14
  from sphinx.writers.text import TextTranslator, TextWriter
17
15
 
16
+ if TYPE_CHECKING:
17
+ from collections.abc import Iterator
18
+
19
+ from docutils.nodes import Node
20
+
21
+ from sphinx.application import Sphinx
22
+
18
23
  logger = logging.getLogger(__name__)
19
24
 
20
25
 
sphinx/builders/xml.py CHANGED
@@ -3,20 +3,25 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from os import path
6
- from typing import Any, Iterator
6
+ from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from docutils import nodes
9
9
  from docutils.io import StringOutput
10
- from docutils.nodes import Node
11
10
  from docutils.writers.docutils_xml import XMLTranslator
12
11
 
13
- from sphinx.application import Sphinx
14
12
  from sphinx.builders import Builder
15
13
  from sphinx.locale import __
16
14
  from sphinx.util import logging
17
15
  from sphinx.util.osutil import ensuredir, os_path
18
16
  from sphinx.writers.xml import PseudoXMLWriter, XMLWriter
19
17
 
18
+ if TYPE_CHECKING:
19
+ from collections.abc import Iterator
20
+
21
+ from docutils.nodes import Node
22
+
23
+ from sphinx.application import Sphinx
24
+
20
25
  logger = logging.getLogger(__name__)
21
26
 
22
27
 
@@ -68,7 +73,7 @@ class XMLBuilder(Builder):
68
73
  doctree = doctree.deepcopy()
69
74
  for domain in self.env.domains.values():
70
75
  xmlns = "xmlns:" + domain.name
71
- doctree[xmlns] = "https://www.sphinx-doc.org/" # type: ignore
76
+ doctree[xmlns] = "https://www.sphinx-doc.org/" # type: ignore[index]
72
77
  for node in doctree.findall(nodes.Element):
73
78
  for att, value in node.attributes.items():
74
79
  if isinstance(value, tuple):
sphinx/cmd/build.py CHANGED
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import argparse
6
6
  import bdb
7
+ import contextlib
7
8
  import locale
8
9
  import multiprocessing
9
10
  import os
@@ -11,7 +12,7 @@ import pdb # NoQA: T100
11
12
  import sys
12
13
  import traceback
13
14
  from os import path
14
- from typing import Any, TextIO
15
+ from typing import TYPE_CHECKING, Any, TextIO
15
16
 
16
17
  from docutils.utils import SystemMessage
17
18
 
@@ -21,10 +22,18 @@ from sphinx.application import Sphinx
21
22
  from sphinx.errors import SphinxError, SphinxParallelError
22
23
  from sphinx.locale import __
23
24
  from sphinx.util import Tee
24
- from sphinx.util.console import color_terminal, nocolor, red, terminal_safe # type: ignore
25
+ from sphinx.util.console import ( # type: ignore[attr-defined]
26
+ color_terminal,
27
+ nocolor,
28
+ red,
29
+ terminal_safe,
30
+ )
25
31
  from sphinx.util.docutils import docutils_namespace, patch_docutils
26
32
  from sphinx.util.exceptions import format_exception_cut_frames, save_traceback
27
- from sphinx.util.osutil import abspath, ensuredir
33
+ from sphinx.util.osutil import ensuredir
34
+
35
+ if TYPE_CHECKING:
36
+ from collections.abc import Sequence
28
37
 
29
38
 
30
39
  def handle_exception(
@@ -198,13 +207,13 @@ files can be built by specifying individual filenames.
198
207
  return parser
199
208
 
200
209
 
201
- def make_main(argv: list[str] = sys.argv[1:]) -> int:
210
+ def make_main(argv: Sequence[str]) -> int:
202
211
  """Sphinx build "make mode" entry."""
203
212
  from sphinx.cmd import make_mode
204
213
  return make_mode.run_make_mode(argv[1:])
205
214
 
206
215
 
207
- def _parse_arguments(argv: list[str] = sys.argv[1:]) -> argparse.Namespace:
216
+ def _parse_arguments(argv: Sequence[str]) -> argparse.Namespace:
208
217
  parser = get_parser()
209
218
  args = parser.parse_args(argv)
210
219
 
@@ -234,13 +243,13 @@ def _parse_arguments(argv: list[str] = sys.argv[1:]) -> argparse.Namespace:
234
243
 
235
244
  if warning and args.warnfile:
236
245
  try:
237
- warnfile = abspath(args.warnfile)
246
+ warnfile = path.abspath(args.warnfile)
238
247
  ensuredir(path.dirname(warnfile))
239
- warnfp = open(args.warnfile, 'w', encoding="utf-8")
248
+ warnfp = open(args.warnfile, 'w', encoding="utf-8") # NoQA: SIM115
240
249
  except Exception as exc:
241
250
  parser.error(__('cannot open warning file %r: %s') % (
242
251
  args.warnfile, exc))
243
- warning = Tee(warning, warnfp) # type: ignore
252
+ warning = Tee(warning, warnfp) # type: ignore[assignment]
244
253
  error = warning
245
254
 
246
255
  args.status = status
@@ -260,10 +269,9 @@ def _parse_arguments(argv: list[str] = sys.argv[1:]) -> argparse.Namespace:
260
269
  key, val = val.split('=')
261
270
  except ValueError:
262
271
  parser.error(__('-A option argument must be in the form name=value'))
263
- try:
272
+ with contextlib.suppress(ValueError):
264
273
  val = int(val)
265
- except ValueError:
266
- pass
274
+
267
275
  confoverrides['html_context.%s' % key] = val
268
276
 
269
277
  if args.nitpicky:
@@ -274,7 +282,7 @@ def _parse_arguments(argv: list[str] = sys.argv[1:]) -> argparse.Namespace:
274
282
  return args
275
283
 
276
284
 
277
- def build_main(argv: list[str] = sys.argv[1:]) -> int:
285
+ def build_main(argv: Sequence[str]) -> int:
278
286
  """Sphinx build "main" command-line entry."""
279
287
  args = _parse_arguments(argv)
280
288
 
@@ -314,17 +322,22 @@ def _bug_report_info() -> int:
314
322
  return 0
315
323
 
316
324
 
317
- def main(argv: list[str] = sys.argv[1:]) -> int:
325
+ def main(argv: Sequence[str] = (), /) -> int:
318
326
  locale.setlocale(locale.LC_ALL, '')
319
327
  sphinx.locale.init_console()
320
328
 
329
+ if not argv:
330
+ argv = sys.argv[1:]
331
+
321
332
  if argv[:1] == ['--bug-report']:
322
333
  return _bug_report_info()
323
334
  if argv[:1] == ['-M']:
324
335
  return make_main(argv)
336
+ elif argv[:1] == ['build']:
337
+ return build_main(argv[1:])
325
338
  else:
326
339
  return build_main(argv)
327
340
 
328
341
 
329
342
  if __name__ == '__main__':
330
- raise SystemExit(main())
343
+ raise SystemExit(main(sys.argv[1:]))