Sphinx 8.1.2__py3-none-any.whl → 8.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of Sphinx might be problematic. Click here for more details.

Files changed (328) hide show
  1. sphinx/__init__.py +8 -4
  2. sphinx/__main__.py +2 -0
  3. sphinx/_cli/__init__.py +2 -5
  4. sphinx/_cli/util/colour.py +34 -11
  5. sphinx/_cli/util/errors.py +128 -61
  6. sphinx/addnodes.py +51 -35
  7. sphinx/application.py +362 -230
  8. sphinx/builders/__init__.py +87 -64
  9. sphinx/builders/_epub_base.py +65 -56
  10. sphinx/builders/changes.py +17 -23
  11. sphinx/builders/dirhtml.py +8 -13
  12. sphinx/builders/epub3.py +70 -38
  13. sphinx/builders/gettext.py +93 -73
  14. sphinx/builders/html/__init__.py +240 -186
  15. sphinx/builders/html/_assets.py +9 -2
  16. sphinx/builders/html/_build_info.py +3 -0
  17. sphinx/builders/latex/__init__.py +64 -54
  18. sphinx/builders/latex/constants.py +14 -11
  19. sphinx/builders/latex/nodes.py +2 -0
  20. sphinx/builders/latex/theming.py +8 -9
  21. sphinx/builders/latex/transforms.py +7 -5
  22. sphinx/builders/linkcheck.py +193 -149
  23. sphinx/builders/manpage.py +17 -17
  24. sphinx/builders/singlehtml.py +28 -16
  25. sphinx/builders/texinfo.py +28 -21
  26. sphinx/builders/text.py +10 -15
  27. sphinx/builders/xml.py +10 -19
  28. sphinx/cmd/build.py +49 -119
  29. sphinx/cmd/make_mode.py +35 -31
  30. sphinx/cmd/quickstart.py +78 -62
  31. sphinx/config.py +265 -163
  32. sphinx/directives/__init__.py +51 -54
  33. sphinx/directives/admonitions.py +107 -0
  34. sphinx/directives/code.py +24 -19
  35. sphinx/directives/other.py +21 -42
  36. sphinx/directives/patches.py +28 -16
  37. sphinx/domains/__init__.py +54 -31
  38. sphinx/domains/_domains_container.py +22 -17
  39. sphinx/domains/_index.py +5 -8
  40. sphinx/domains/c/__init__.py +366 -245
  41. sphinx/domains/c/_ast.py +378 -256
  42. sphinx/domains/c/_ids.py +89 -31
  43. sphinx/domains/c/_parser.py +283 -214
  44. sphinx/domains/c/_symbol.py +269 -198
  45. sphinx/domains/changeset.py +39 -24
  46. sphinx/domains/citation.py +54 -24
  47. sphinx/domains/cpp/__init__.py +517 -362
  48. sphinx/domains/cpp/_ast.py +999 -682
  49. sphinx/domains/cpp/_ids.py +133 -65
  50. sphinx/domains/cpp/_parser.py +746 -588
  51. sphinx/domains/cpp/_symbol.py +692 -489
  52. sphinx/domains/index.py +10 -8
  53. sphinx/domains/javascript.py +152 -74
  54. sphinx/domains/math.py +50 -40
  55. sphinx/domains/python/__init__.py +402 -211
  56. sphinx/domains/python/_annotations.py +134 -61
  57. sphinx/domains/python/_object.py +155 -68
  58. sphinx/domains/rst.py +94 -49
  59. sphinx/domains/std/__init__.py +510 -249
  60. sphinx/environment/__init__.py +345 -61
  61. sphinx/environment/adapters/asset.py +7 -1
  62. sphinx/environment/adapters/indexentries.py +15 -20
  63. sphinx/environment/adapters/toctree.py +19 -9
  64. sphinx/environment/collectors/__init__.py +3 -1
  65. sphinx/environment/collectors/asset.py +18 -15
  66. sphinx/environment/collectors/dependencies.py +8 -10
  67. sphinx/environment/collectors/metadata.py +6 -4
  68. sphinx/environment/collectors/title.py +3 -1
  69. sphinx/environment/collectors/toctree.py +4 -4
  70. sphinx/errors.py +1 -3
  71. sphinx/events.py +4 -4
  72. sphinx/ext/apidoc/__init__.py +66 -0
  73. sphinx/ext/apidoc/__main__.py +9 -0
  74. sphinx/ext/apidoc/_cli.py +356 -0
  75. sphinx/ext/apidoc/_extension.py +262 -0
  76. sphinx/ext/apidoc/_generate.py +356 -0
  77. sphinx/ext/apidoc/_shared.py +99 -0
  78. sphinx/ext/autodoc/__init__.py +837 -483
  79. sphinx/ext/autodoc/directive.py +57 -21
  80. sphinx/ext/autodoc/importer.py +184 -67
  81. sphinx/ext/autodoc/mock.py +25 -10
  82. sphinx/ext/autodoc/preserve_defaults.py +17 -9
  83. sphinx/ext/autodoc/type_comment.py +56 -29
  84. sphinx/ext/autodoc/typehints.py +49 -26
  85. sphinx/ext/autosectionlabel.py +28 -11
  86. sphinx/ext/autosummary/__init__.py +281 -142
  87. sphinx/ext/autosummary/generate.py +121 -51
  88. sphinx/ext/coverage.py +152 -91
  89. sphinx/ext/doctest.py +169 -101
  90. sphinx/ext/duration.py +12 -6
  91. sphinx/ext/extlinks.py +33 -21
  92. sphinx/ext/githubpages.py +8 -8
  93. sphinx/ext/graphviz.py +175 -109
  94. sphinx/ext/ifconfig.py +11 -6
  95. sphinx/ext/imgconverter.py +48 -25
  96. sphinx/ext/imgmath.py +127 -97
  97. sphinx/ext/inheritance_diagram.py +177 -103
  98. sphinx/ext/intersphinx/__init__.py +22 -13
  99. sphinx/ext/intersphinx/__main__.py +3 -1
  100. sphinx/ext/intersphinx/_cli.py +18 -14
  101. sphinx/ext/intersphinx/_load.py +91 -82
  102. sphinx/ext/intersphinx/_resolve.py +108 -74
  103. sphinx/ext/intersphinx/_shared.py +2 -2
  104. sphinx/ext/linkcode.py +28 -12
  105. sphinx/ext/mathjax.py +60 -29
  106. sphinx/ext/napoleon/__init__.py +19 -7
  107. sphinx/ext/napoleon/docstring.py +229 -231
  108. sphinx/ext/todo.py +44 -49
  109. sphinx/ext/viewcode.py +105 -57
  110. sphinx/extension.py +3 -1
  111. sphinx/highlighting.py +13 -7
  112. sphinx/io.py +9 -13
  113. sphinx/jinja2glue.py +29 -26
  114. sphinx/locale/__init__.py +8 -9
  115. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  116. sphinx/locale/ar/LC_MESSAGES/sphinx.po +2155 -2050
  117. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  118. sphinx/locale/bg/LC_MESSAGES/sphinx.po +2045 -1940
  119. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  120. sphinx/locale/bn/LC_MESSAGES/sphinx.po +2175 -2070
  121. sphinx/locale/ca/LC_MESSAGES/sphinx.js +3 -3
  122. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/ca/LC_MESSAGES/sphinx.po +2690 -2585
  124. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.js +63 -0
  125. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.mo +0 -0
  126. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.po +4216 -0
  127. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  128. sphinx/locale/cak/LC_MESSAGES/sphinx.po +2096 -1991
  129. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  130. sphinx/locale/cs/LC_MESSAGES/sphinx.po +2248 -2143
  131. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  132. sphinx/locale/cy/LC_MESSAGES/sphinx.po +2201 -2096
  133. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  134. sphinx/locale/da/LC_MESSAGES/sphinx.po +2282 -2177
  135. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  136. sphinx/locale/de/LC_MESSAGES/sphinx.po +2261 -2156
  137. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  138. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2045 -1940
  139. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  140. sphinx/locale/el/LC_MESSAGES/sphinx.po +2604 -2499
  141. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  142. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2045 -1940
  143. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2045 -1940
  145. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2631 -2526
  147. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  148. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2045 -1940
  149. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  150. sphinx/locale/eo/LC_MESSAGES/sphinx.po +2078 -1973
  151. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/es/LC_MESSAGES/sphinx.po +2633 -2528
  153. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2045 -1940
  155. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/et/LC_MESSAGES/sphinx.po +2449 -2344
  157. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/eu/LC_MESSAGES/sphinx.po +2241 -2136
  159. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/fa/LC_MESSAGES/sphinx.po +504 -500
  161. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/fi/LC_MESSAGES/sphinx.po +499 -495
  163. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/fr/LC_MESSAGES/sphinx.po +513 -509
  165. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +499 -495
  167. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/gl/LC_MESSAGES/sphinx.po +2644 -2539
  169. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/he/LC_MESSAGES/sphinx.po +499 -495
  171. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/hi/LC_MESSAGES/sphinx.po +504 -500
  173. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +499 -495
  175. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/hr/LC_MESSAGES/sphinx.po +501 -497
  177. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/hu/LC_MESSAGES/sphinx.po +499 -495
  179. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  180. sphinx/locale/id/LC_MESSAGES/sphinx.po +2609 -2504
  181. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  182. sphinx/locale/is/LC_MESSAGES/sphinx.po +499 -495
  183. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  184. sphinx/locale/it/LC_MESSAGES/sphinx.po +2265 -2160
  185. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  186. sphinx/locale/ja/LC_MESSAGES/sphinx.po +2621 -2516
  187. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  188. sphinx/locale/ka/LC_MESSAGES/sphinx.po +2567 -2462
  189. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  190. sphinx/locale/ko/LC_MESSAGES/sphinx.po +2631 -2526
  191. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  192. sphinx/locale/lt/LC_MESSAGES/sphinx.po +2214 -2109
  193. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  194. sphinx/locale/lv/LC_MESSAGES/sphinx.po +2218 -2113
  195. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  196. sphinx/locale/mk/LC_MESSAGES/sphinx.po +2088 -1983
  197. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  198. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2247 -2142
  199. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  200. sphinx/locale/ne/LC_MESSAGES/sphinx.po +2227 -2122
  201. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  202. sphinx/locale/nl/LC_MESSAGES/sphinx.po +2316 -2211
  203. sphinx/locale/pl/LC_MESSAGES/sphinx.js +2 -2
  204. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  205. sphinx/locale/pl/LC_MESSAGES/sphinx.po +2442 -2336
  206. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/pt/LC_MESSAGES/sphinx.po +2045 -1940
  208. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2657 -2552
  210. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2243 -2138
  212. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/ro/LC_MESSAGES/sphinx.po +2244 -2139
  214. sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
  215. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  216. sphinx/locale/ru/LC_MESSAGES/sphinx.po +2660 -2555
  217. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  218. sphinx/locale/si/LC_MESSAGES/sphinx.po +2134 -2029
  219. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  220. sphinx/locale/sk/LC_MESSAGES/sphinx.po +2614 -2509
  221. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  222. sphinx/locale/sl/LC_MESSAGES/sphinx.po +2167 -2062
  223. sphinx/locale/sphinx.pot +2069 -1964
  224. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  225. sphinx/locale/sq/LC_MESSAGES/sphinx.po +2661 -2556
  226. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  227. sphinx/locale/sr/LC_MESSAGES/sphinx.po +2213 -2108
  228. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  229. sphinx/locale/sv/LC_MESSAGES/sphinx.po +2229 -2124
  230. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  231. sphinx/locale/te/LC_MESSAGES/sphinx.po +2045 -1940
  232. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  233. sphinx/locale/tr/LC_MESSAGES/sphinx.po +2608 -2503
  234. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  235. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2167 -2062
  236. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  237. sphinx/locale/ur/LC_MESSAGES/sphinx.po +2045 -1940
  238. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  239. sphinx/locale/vi/LC_MESSAGES/sphinx.po +2204 -2099
  240. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  241. sphinx/locale/yue/LC_MESSAGES/sphinx.po +2045 -1940
  242. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  243. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2045 -1940
  244. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  245. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2659 -2554
  246. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  247. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2045 -1940
  248. sphinx/parsers.py +8 -7
  249. sphinx/project.py +2 -2
  250. sphinx/pycode/__init__.py +31 -21
  251. sphinx/pycode/ast.py +6 -3
  252. sphinx/pycode/parser.py +14 -8
  253. sphinx/pygments_styles.py +4 -5
  254. sphinx/registry.py +192 -92
  255. sphinx/roles.py +58 -7
  256. sphinx/search/__init__.py +75 -54
  257. sphinx/search/en.py +11 -13
  258. sphinx/search/fi.py +1 -1
  259. sphinx/search/ja.py +8 -6
  260. sphinx/search/nl.py +1 -1
  261. sphinx/search/zh.py +19 -21
  262. sphinx/testing/fixtures.py +26 -29
  263. sphinx/testing/path.py +26 -62
  264. sphinx/testing/restructuredtext.py +14 -8
  265. sphinx/testing/util.py +21 -19
  266. sphinx/texinputs/make.bat.jinja +50 -50
  267. sphinx/texinputs/sphinx.sty +4 -3
  268. sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
  269. sphinx/texinputs/sphinxlatexobjects.sty +29 -10
  270. sphinx/themes/basic/static/searchtools.js +8 -5
  271. sphinx/theming.py +49 -61
  272. sphinx/transforms/__init__.py +17 -38
  273. sphinx/transforms/compact_bullet_list.py +5 -3
  274. sphinx/transforms/i18n.py +8 -21
  275. sphinx/transforms/post_transforms/__init__.py +142 -93
  276. sphinx/transforms/post_transforms/code.py +5 -5
  277. sphinx/transforms/post_transforms/images.py +28 -24
  278. sphinx/transforms/references.py +3 -1
  279. sphinx/util/__init__.py +109 -60
  280. sphinx/util/_files.py +39 -23
  281. sphinx/util/_importer.py +4 -1
  282. sphinx/util/_inventory_file_reader.py +76 -0
  283. sphinx/util/_io.py +2 -2
  284. sphinx/util/_lines.py +6 -3
  285. sphinx/util/_pathlib.py +40 -2
  286. sphinx/util/build_phase.py +2 -0
  287. sphinx/util/cfamily.py +19 -14
  288. sphinx/util/console.py +44 -179
  289. sphinx/util/display.py +9 -10
  290. sphinx/util/docfields.py +140 -122
  291. sphinx/util/docstrings.py +1 -1
  292. sphinx/util/docutils.py +118 -77
  293. sphinx/util/fileutil.py +25 -26
  294. sphinx/util/http_date.py +2 -0
  295. sphinx/util/i18n.py +77 -64
  296. sphinx/util/images.py +8 -6
  297. sphinx/util/inspect.py +147 -38
  298. sphinx/util/inventory.py +215 -116
  299. sphinx/util/logging.py +33 -33
  300. sphinx/util/matching.py +12 -4
  301. sphinx/util/nodes.py +18 -13
  302. sphinx/util/osutil.py +38 -39
  303. sphinx/util/parallel.py +22 -13
  304. sphinx/util/parsing.py +2 -1
  305. sphinx/util/png.py +6 -2
  306. sphinx/util/requests.py +33 -2
  307. sphinx/util/rst.py +3 -2
  308. sphinx/util/tags.py +1 -1
  309. sphinx/util/template.py +18 -10
  310. sphinx/util/texescape.py +8 -6
  311. sphinx/util/typing.py +148 -122
  312. sphinx/versioning.py +3 -3
  313. sphinx/writers/html.py +3 -1
  314. sphinx/writers/html5.py +63 -52
  315. sphinx/writers/latex.py +83 -67
  316. sphinx/writers/manpage.py +19 -38
  317. sphinx/writers/texinfo.py +47 -47
  318. sphinx/writers/text.py +50 -32
  319. sphinx/writers/xml.py +11 -8
  320. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/LICENSE.rst +1 -1
  321. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/METADATA +25 -15
  322. sphinx-8.2.0.dist-info/RECORD +606 -0
  323. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/WHEEL +1 -1
  324. sphinx/builders/html/transforms.py +0 -90
  325. sphinx/ext/apidoc.py +0 -721
  326. sphinx/util/exceptions.py +0 -74
  327. sphinx-8.1.2.dist-info/RECORD +0 -598
  328. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/entry_points.txt +0 -0
@@ -2,21 +2,23 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from os import path
6
- from typing import TYPE_CHECKING, Any
5
+ import warnings
6
+ from typing import TYPE_CHECKING
7
7
 
8
8
  from docutils import nodes
9
9
 
10
+ from sphinx._cli.util.colour import darkgreen
10
11
  from sphinx.builders.html import StandaloneHTMLBuilder
12
+ from sphinx.deprecation import RemovedInSphinx10Warning
11
13
  from sphinx.environment.adapters.toctree import global_toctree_for_doc
12
14
  from sphinx.locale import __
13
15
  from sphinx.util import logging
14
- from sphinx.util.console import darkgreen
15
16
  from sphinx.util.display import progress_message
16
17
  from sphinx.util.nodes import inline_all_toctrees
17
18
 
18
19
  if TYPE_CHECKING:
19
20
  from collections.abc import Set
21
+ from typing import Any
20
22
 
21
23
  from docutils.nodes import Node
22
24
 
@@ -27,10 +29,7 @@ logger = logging.getLogger(__name__)
27
29
 
28
30
 
29
31
  class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
30
- """
31
- A StandaloneHTMLBuilder subclass that puts the whole document tree on one
32
- HTML page.
33
- """
32
+ """Builds the whole document tree as a single HTML page."""
34
33
 
35
34
  name = 'singlehtml'
36
35
  epilog = __('The HTML page is in %(outdir)s.')
@@ -53,6 +52,14 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
53
52
  return self.get_target_uri(to, typ)
54
53
 
55
54
  def fix_refuris(self, tree: Node) -> None:
55
+ deprecation_msg = (
56
+ "The 'SingleFileHTMLBuilder.fix_refuris' method is no longer used "
57
+ 'within the builder and is planned for removal in Sphinx 10. '
58
+ 'Please report malformed URIs generated by the Sphinx singlehtml '
59
+ 'builder as bugreports.'
60
+ )
61
+ warnings.warn(deprecation_msg, RemovedInSphinx10Warning, stacklevel=2)
62
+
56
63
  # fix refuris with double anchor
57
64
  for refnode in tree.findall(nodes.reference):
58
65
  if 'refuri' not in refnode:
@@ -74,13 +81,11 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
74
81
  kwargs['includehidden'] = False
75
82
  elif includehidden.lower() == 'true':
76
83
  kwargs['includehidden'] = True
77
- if kwargs.get('maxdepth') == '':
84
+ if kwargs.get('maxdepth') == '': # NoQA: PLC1901
78
85
  kwargs.pop('maxdepth')
79
86
  toctree = global_toctree_for_doc(
80
87
  self.env, docname, self, collapse=collapse, **kwargs
81
88
  )
82
- if toctree is not None:
83
- self.fix_refuris(toctree)
84
89
  return self.render_partial(toctree)['fragment']
85
90
 
86
91
  def assemble_doctree(self) -> nodes.document:
@@ -90,7 +95,6 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
90
95
  tree = inline_all_toctrees(self, set(), master, tree, darkgreen, [master])
91
96
  tree['docname'] = master
92
97
  self.env.resolve_references(tree, master, self)
93
- self.fix_refuris(tree)
94
98
  return tree
95
99
 
96
100
  def assemble_toc_secnumbers(self) -> dict[str, dict[str, tuple[int, ...]]]:
@@ -141,7 +145,6 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
141
145
  )
142
146
  # if there is no toctree, toc is None
143
147
  if toctree:
144
- self.fix_refuris(toctree)
145
148
  toc = self.render_partial(toctree)['fragment']
146
149
  display_toc = True
147
150
  else:
@@ -189,20 +192,29 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
189
192
 
190
193
  # additional pages from conf.py
191
194
  for pagename, template in self.config.html_additional_pages.items():
192
- logger.info(' ' + pagename, nonl=True)
195
+ logger.info(' %s', pagename, nonl=True)
193
196
  self.handle_page(pagename, {}, template)
194
197
 
195
198
  if self.config.html_use_opensearch:
196
199
  logger.info(' opensearch', nonl=True)
197
- fn = path.join(self.outdir, '_static', 'opensearch.xml')
198
- self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
200
+ self.handle_page(
201
+ 'opensearch',
202
+ {},
203
+ 'opensearch.xml',
204
+ outfilename=self._static_dir / 'opensearch.xml',
205
+ )
199
206
 
200
207
 
201
208
  def setup(app: Sphinx) -> ExtensionMetadata:
202
209
  app.setup_extension('sphinx.builders.html')
203
210
 
204
211
  app.add_builder(SingleFileHTMLBuilder)
205
- app.add_config_value('singlehtml_sidebars', lambda self: self.html_sidebars, 'html')
212
+ app.add_config_value(
213
+ 'singlehtml_sidebars',
214
+ lambda self: self.html_sidebars,
215
+ 'html',
216
+ types=frozenset({dict}),
217
+ )
206
218
 
207
219
  return {
208
220
  'version': 'builtin',
@@ -2,22 +2,21 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import os
5
+ import os.path
6
6
  import warnings
7
- from os import path
8
- from typing import TYPE_CHECKING, Any
7
+ from typing import TYPE_CHECKING
9
8
 
10
9
  from docutils import nodes
11
10
  from docutils.frontend import OptionParser
12
11
  from docutils.io import FileOutput
13
12
 
14
13
  from sphinx import addnodes, package_dir
14
+ from sphinx._cli.util.colour import darkgreen
15
15
  from sphinx.builders import Builder
16
16
  from sphinx.environment.adapters.asset import ImageAdapter
17
17
  from sphinx.errors import NoUri
18
18
  from sphinx.locale import _, __
19
19
  from sphinx.util import logging
20
- from sphinx.util.console import darkgreen
21
20
  from sphinx.util.display import progress_message, status_iterator
22
21
  from sphinx.util.docutils import new_document
23
22
  from sphinx.util.nodes import inline_all_toctrees
@@ -26,6 +25,7 @@ from sphinx.writers.texinfo import TexinfoTranslator, TexinfoWriter
26
25
 
27
26
  if TYPE_CHECKING:
28
27
  from collections.abc import Iterable, Set
28
+ from typing import Any
29
29
 
30
30
  from docutils.nodes import Node
31
31
 
@@ -34,13 +34,11 @@ if TYPE_CHECKING:
34
34
  from sphinx.util.typing import ExtensionMetadata
35
35
 
36
36
  logger = logging.getLogger(__name__)
37
- template_dir = os.path.join(package_dir, 'templates', 'texinfo')
37
+ template_dir = package_dir.joinpath('templates', 'texinfo')
38
38
 
39
39
 
40
40
  class TexinfoBuilder(Builder):
41
- """
42
- Builds Texinfo output to create Info documentation.
43
- """
41
+ """Builds Texinfo output to create Info documentation."""
44
42
 
45
43
  name = 'texinfo'
46
44
  format = 'texinfo'
@@ -109,7 +107,8 @@ class TexinfoBuilder(Builder):
109
107
  if len(entry) > 7:
110
108
  toctree_only = entry[7]
111
109
  destination = FileOutput(
112
- destination_path=path.join(self.outdir, targetname), encoding='utf-8'
110
+ destination_path=self.outdir / targetname,
111
+ encoding='utf-8',
113
112
  )
114
113
  with progress_message(__('processing %s') % targetname, nonl=False):
115
114
  appendices = self.config.texinfo_appendices or []
@@ -185,8 +184,6 @@ class TexinfoBuilder(Builder):
185
184
  nodes.Text(')'),
186
185
  ))
187
186
  break
188
- else:
189
- pass
190
187
  pendingnode.replace_self(newnodes)
191
188
  return largetree
192
189
 
@@ -195,7 +192,7 @@ class TexinfoBuilder(Builder):
195
192
 
196
193
  def copy_image_files(self, targetname: str) -> None:
197
194
  if self.images:
198
- stringify_func = ImageAdapter(self.app.env).get_original_image_uri
195
+ stringify_func = ImageAdapter(self.env).get_original_image_uri
199
196
  for src in status_iterator(
200
197
  self.images,
201
198
  __('copying images... '),
@@ -216,7 +213,7 @@ class TexinfoBuilder(Builder):
216
213
  except Exception as err:
217
214
  logger.warning(
218
215
  __('cannot copy image file %r: %s'),
219
- path.join(self.srcdir, src),
216
+ self.srcdir / src,
220
217
  err,
221
218
  )
222
219
 
@@ -225,7 +222,7 @@ class TexinfoBuilder(Builder):
225
222
  with progress_message(__('copying Texinfo support files')):
226
223
  logger.info('Makefile ', nonl=True)
227
224
  copyfile(
228
- os.path.join(template_dir, 'Makefile'),
225
+ template_dir / 'Makefile',
229
226
  self.outdir / 'Makefile',
230
227
  force=True,
231
228
  )
@@ -254,13 +251,23 @@ def default_texinfo_documents(
254
251
  def setup(app: Sphinx) -> ExtensionMetadata:
255
252
  app.add_builder(TexinfoBuilder)
256
253
 
257
- app.add_config_value('texinfo_documents', default_texinfo_documents, '')
258
- app.add_config_value('texinfo_appendices', [], '')
259
- app.add_config_value('texinfo_elements', {}, '')
260
- app.add_config_value('texinfo_domain_indices', True, '', types={set, list})
261
- app.add_config_value('texinfo_show_urls', 'footnote', '')
262
- app.add_config_value('texinfo_no_detailmenu', False, '')
263
- app.add_config_value('texinfo_cross_references', True, '')
254
+ app.add_config_value(
255
+ 'texinfo_documents',
256
+ default_texinfo_documents,
257
+ '',
258
+ types=frozenset({list, tuple}),
259
+ )
260
+ app.add_config_value('texinfo_appendices', [], '', types=frozenset({list, tuple}))
261
+ app.add_config_value('texinfo_elements', {}, '', types=frozenset({dict}))
262
+ app.add_config_value(
263
+ 'texinfo_domain_indices',
264
+ True,
265
+ '',
266
+ types=frozenset({frozenset, list, set, tuple}),
267
+ )
268
+ app.add_config_value('texinfo_show_urls', 'footnote', '', types=frozenset({str}))
269
+ app.add_config_value('texinfo_no_detailmenu', False, '', types=frozenset({bool}))
270
+ app.add_config_value('texinfo_cross_references', True, '', types=frozenset({bool}))
264
271
 
265
272
  return {
266
273
  'version': 'builtin',
sphinx/builders/text.py CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from os import path
6
5
  from typing import TYPE_CHECKING
7
6
 
8
7
  from docutils.io import StringOutput
@@ -10,11 +9,7 @@ from docutils.io import StringOutput
10
9
  from sphinx.builders import Builder
11
10
  from sphinx.locale import __
12
11
  from sphinx.util import logging
13
- from sphinx.util.osutil import (
14
- _last_modified_time,
15
- ensuredir,
16
- os_path,
17
- )
12
+ from sphinx.util.osutil import _last_modified_time
18
13
  from sphinx.writers.text import TextTranslator, TextWriter
19
14
 
20
15
  if TYPE_CHECKING:
@@ -48,7 +43,7 @@ class TextBuilder(Builder):
48
43
  if docname not in self.env.all_docs:
49
44
  yield docname
50
45
  continue
51
- targetname = path.join(self.outdir, docname + self.out_suffix)
46
+ targetname = self.outdir / (docname + self.out_suffix)
52
47
  try:
53
48
  targetmtime = _last_modified_time(targetname)
54
49
  except Exception:
@@ -72,13 +67,13 @@ class TextBuilder(Builder):
72
67
  self.secnumbers = self.env.toc_secnumbers.get(docname, {})
73
68
  destination = StringOutput(encoding='utf-8')
74
69
  self.writer.write(doctree, destination)
75
- outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
76
- ensuredir(path.dirname(outfilename))
70
+ out_file_name = self.outdir / (docname + self.out_suffix)
71
+ out_file_name.parent.mkdir(parents=True, exist_ok=True)
77
72
  try:
78
- with open(outfilename, 'w', encoding='utf-8') as f:
73
+ with open(out_file_name, 'w', encoding='utf-8') as f:
79
74
  f.write(self.writer.output)
80
75
  except OSError as err:
81
- logger.warning(__('error writing file %s: %s'), outfilename, err)
76
+ logger.warning(__('error writing file %s: %s'), out_file_name, err)
82
77
 
83
78
  def finish(self) -> None:
84
79
  pass
@@ -87,10 +82,10 @@ class TextBuilder(Builder):
87
82
  def setup(app: Sphinx) -> ExtensionMetadata:
88
83
  app.add_builder(TextBuilder)
89
84
 
90
- app.add_config_value('text_sectionchars', '*=-~"+`', 'env')
91
- app.add_config_value('text_newlines', 'unix', 'env')
92
- app.add_config_value('text_add_secnumbers', True, 'env')
93
- app.add_config_value('text_secnumber_suffix', '. ', 'env')
85
+ app.add_config_value('text_sectionchars', '*=-~"+`', 'env', types=frozenset({str}))
86
+ app.add_config_value('text_newlines', 'unix', 'env', types=frozenset({str}))
87
+ app.add_config_value('text_add_secnumbers', True, 'env', types=frozenset({bool}))
88
+ app.add_config_value('text_secnumber_suffix', '. ', 'env', types=frozenset({str}))
94
89
 
95
90
  return {
96
91
  'version': 'builtin',
sphinx/builders/xml.py CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from os import path
6
5
  from typing import TYPE_CHECKING
7
6
 
8
7
  from docutils import nodes
@@ -12,11 +11,7 @@ from docutils.writers.docutils_xml import XMLTranslator
12
11
  from sphinx.builders import Builder
13
12
  from sphinx.locale import __
14
13
  from sphinx.util import logging
15
- from sphinx.util.osutil import (
16
- _last_modified_time,
17
- ensuredir,
18
- os_path,
19
- )
14
+ from sphinx.util.osutil import _last_modified_time
20
15
  from sphinx.writers.xml import PseudoXMLWriter, XMLWriter
21
16
 
22
17
  if TYPE_CHECKING:
@@ -29,9 +24,7 @@ logger = logging.getLogger(__name__)
29
24
 
30
25
 
31
26
  class XMLBuilder(Builder):
32
- """
33
- Builds Docutils-native XML.
34
- """
27
+ """Builds Docutils-native XML."""
35
28
 
36
29
  name = 'xml'
37
30
  format = 'xml'
@@ -40,7 +33,7 @@ class XMLBuilder(Builder):
40
33
  out_suffix = '.xml'
41
34
  allow_parallel = True
42
35
 
43
- _writer_class: type[XMLWriter] | type[PseudoXMLWriter] = XMLWriter
36
+ _writer_class: type[XMLWriter | PseudoXMLWriter] = XMLWriter
44
37
  writer: XMLWriter | PseudoXMLWriter
45
38
  default_translator_class = XMLTranslator
46
39
 
@@ -52,7 +45,7 @@ class XMLBuilder(Builder):
52
45
  if docname not in self.env.all_docs:
53
46
  yield docname
54
47
  continue
55
- targetname = path.join(self.outdir, docname + self.out_suffix)
48
+ targetname = self.outdir / (docname + self.out_suffix)
56
49
  try:
57
50
  targetmtime = _last_modified_time(targetname)
58
51
  except Exception:
@@ -88,22 +81,20 @@ class XMLBuilder(Builder):
88
81
  value[i] = list(val)
89
82
  destination = StringOutput(encoding='utf-8')
90
83
  self.writer.write(doctree, destination)
91
- outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
92
- ensuredir(path.dirname(outfilename))
84
+ out_file_name = self.outdir / (docname + self.out_suffix)
85
+ out_file_name.parent.mkdir(parents=True, exist_ok=True)
93
86
  try:
94
- with open(outfilename, 'w', encoding='utf-8') as f:
87
+ with open(out_file_name, 'w', encoding='utf-8') as f:
95
88
  f.write(self.writer.output)
96
89
  except OSError as err:
97
- logger.warning(__('error writing file %s: %s'), outfilename, err)
90
+ logger.warning(__('error writing file %s: %s'), out_file_name, err)
98
91
 
99
92
  def finish(self) -> None:
100
93
  pass
101
94
 
102
95
 
103
96
  class PseudoXMLBuilder(XMLBuilder):
104
- """
105
- Builds pseudo-XML for display purposes.
106
- """
97
+ """Builds pseudo-XML for display purposes."""
107
98
 
108
99
  name = 'pseudoxml'
109
100
  format = 'pseudoxml'
@@ -118,7 +109,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
118
109
  app.add_builder(XMLBuilder)
119
110
  app.add_builder(PseudoXMLBuilder)
120
111
 
121
- app.add_config_value('xml_pretty', True, 'env')
112
+ app.add_config_value('xml_pretty', True, 'env', types=frozenset({bool}))
122
113
 
123
114
  return {
124
115
  'version': 'builtin',
sphinx/cmd/build.py CHANGED
@@ -3,36 +3,29 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import argparse
6
- import bdb
7
6
  import contextlib
8
7
  import locale
9
8
  import multiprocessing
10
- import os
11
- import pdb # NoQA: T100
12
9
  import sys
13
- import traceback
14
- from os import path
15
- from typing import TYPE_CHECKING, Any, TextIO
16
-
17
- from docutils.utils import SystemMessage
10
+ from pathlib import Path
11
+ from typing import TYPE_CHECKING
18
12
 
13
+ import sphinx._cli.util.errors
19
14
  import sphinx.locale
20
15
  from sphinx import __display_version__
16
+ from sphinx._cli.util.colour import disable_colour, terminal_supports_colour
21
17
  from sphinx.application import Sphinx
22
- from sphinx.errors import SphinxError, SphinxParallelError
23
18
  from sphinx.locale import __
24
19
  from sphinx.util._io import TeeStripANSI
25
- from sphinx.util.console import color_terminal, nocolor, red, terminal_safe
20
+ from sphinx.util._pathlib import _StrPath
26
21
  from sphinx.util.docutils import docutils_namespace, patch_docutils
27
- from sphinx.util.exceptions import format_exception_cut_frames, save_traceback
28
22
  from sphinx.util.osutil import ensuredir
29
23
 
30
24
  if TYPE_CHECKING:
31
- from collections.abc import Sequence
32
- from typing import Protocol
25
+ from collections.abc import Collection, Sequence
26
+ from typing import Any, TextIO
33
27
 
34
- class SupportsWrite(Protocol):
35
- def write(self, text: str, /) -> int | None: ... # NoQA: E704
28
+ from sphinx.extension import Extension
36
29
 
37
30
 
38
31
  def handle_exception(
@@ -41,99 +34,26 @@ def handle_exception(
41
34
  exception: BaseException,
42
35
  stderr: TextIO = sys.stderr,
43
36
  ) -> None:
44
- if isinstance(exception, bdb.BdbQuit):
45
- return
46
-
47
- if args.pdb:
48
- print(
49
- red(__('Exception occurred while building, starting debugger:')),
50
- file=stderr,
51
- )
52
- traceback.print_exc()
53
- pdb.post_mortem(sys.exc_info()[2])
37
+ if app is not None:
38
+ message_log: Sequence[str] = app.messagelog
39
+ extensions: Collection[Extension] = app.extensions.values()
54
40
  else:
55
- print(file=stderr)
56
- if args.verbosity or args.traceback:
57
- exc = sys.exc_info()[1]
58
- if isinstance(exc, SphinxParallelError):
59
- exc_format = '(Error in parallel process)\n' + exc.traceback
60
- print(exc_format, file=stderr)
61
- else:
62
- traceback.print_exc(None, stderr)
63
- print(file=stderr)
64
- if isinstance(exception, KeyboardInterrupt):
65
- print(__('Interrupted!'), file=stderr)
66
- elif isinstance(exception, SystemMessage):
67
- print(red(__('reST markup error:')), file=stderr)
68
- print(terminal_safe(exception.args[0]), file=stderr)
69
- elif isinstance(exception, SphinxError):
70
- print(red('%s:' % exception.category), file=stderr)
71
- print(str(exception), file=stderr)
72
- elif isinstance(exception, UnicodeError):
73
- print(red(__('Encoding error:')), file=stderr)
74
- print(terminal_safe(str(exception)), file=stderr)
75
- tbpath = save_traceback(app, exception)
76
- print(
77
- red(
78
- __(
79
- 'The full traceback has been saved in %s, if you want '
80
- 'to report the issue to the developers.'
81
- )
82
- % tbpath
83
- ),
84
- file=stderr,
85
- )
86
- elif (
87
- isinstance(exception, RuntimeError)
88
- and 'recursion depth' in str(exception)
89
- ): # fmt: skip
90
- print(red(__('Recursion error:')), file=stderr)
91
- print(terminal_safe(str(exception)), file=stderr)
92
- print(file=stderr)
93
- print(
94
- __(
95
- 'This can happen with very large or deeply nested source '
96
- 'files. You can carefully increase the default Python '
97
- 'recursion limit of 1000 in conf.py with e.g.:'
98
- ),
99
- file=stderr,
100
- )
101
- print(' import sys; sys.setrecursionlimit(1500)', file=stderr)
102
- else:
103
- print(red(__('Exception occurred:')), file=stderr)
104
- print(format_exception_cut_frames().rstrip(), file=stderr)
105
- tbpath = save_traceback(app, exception)
106
- print(
107
- red(
108
- __(
109
- 'The full traceback has been saved in %s, if you '
110
- 'want to report the issue to the developers.'
111
- )
112
- % tbpath
113
- ),
114
- file=stderr,
115
- )
116
- print(
117
- __(
118
- 'Please also report this if it was a user error, so '
119
- 'that a better error message can be provided next time.'
120
- ),
121
- file=stderr,
122
- )
123
- print(
124
- __(
125
- 'A bug report can be filed in the tracker at '
126
- '<https://github.com/sphinx-doc/sphinx/issues>. Thanks!'
127
- ),
128
- file=stderr,
129
- )
41
+ message_log = extensions = ()
42
+ return sphinx._cli.util.errors.handle_exception(
43
+ exception,
44
+ stderr=stderr,
45
+ use_pdb=args.pdb,
46
+ print_traceback=args.verbosity or args.traceback,
47
+ message_log=message_log,
48
+ extensions=extensions,
49
+ )
130
50
 
131
51
 
132
52
  def jobs_argument(value: str) -> int:
133
- """
134
- Special type to handle 'auto' flags passed to 'sphinx-build' via -j flag. Can
135
- be expanded to handle other special scaling requests, such as setting job count
136
- to cpu_count.
53
+ """Parse the ``--jobs`` flag.
54
+
55
+ Return the number of CPUs if 'auto' is used,
56
+ otherwise ensure *value* is a positive integer.
137
57
  """
138
58
  if value == 'auto':
139
59
  return multiprocessing.cpu_count()
@@ -217,14 +137,14 @@ files can be built by specifying individual filenames.
217
137
  '-a',
218
138
  action='store_true',
219
139
  dest='force_all',
220
- help=__('write all files (default: only write new and ' 'changed files)'),
140
+ help=__('write all files (default: only write new and changed files)'),
221
141
  )
222
142
  group.add_argument(
223
143
  '--fresh-env',
224
144
  '-E',
225
145
  action='store_true',
226
146
  dest='freshenv',
227
- help=__("don't use a saved environment, always read " 'all files'),
147
+ help=__("don't use a saved environment, always read all files"),
228
148
  )
229
149
 
230
150
  group = parser.add_argument_group(__('path options'))
@@ -243,9 +163,7 @@ files can be built by specifying individual filenames.
243
163
  '-c',
244
164
  metavar='PATH',
245
165
  dest='confdir',
246
- help=__(
247
- 'directory for the configuration file (conf.py) ' '(default: SOURCE_DIR)'
248
- ),
166
+ help=__('directory for the configuration file (conf.py) (default: SOURCE_DIR)'),
249
167
  )
250
168
 
251
169
  group = parser.add_argument_group('build configuration options')
@@ -392,10 +310,10 @@ def _parse_confdir(noconfig: bool, confdir: str, sourcedir: str) -> str | None:
392
310
  return confdir
393
311
 
394
312
 
395
- def _parse_doctreedir(doctreedir: str, outputdir: str) -> str:
313
+ def _parse_doctreedir(doctreedir: str, outputdir: str) -> _StrPath:
396
314
  if doctreedir:
397
- return doctreedir
398
- return os.path.join(outputdir, '.doctrees')
315
+ return _StrPath(doctreedir)
316
+ return _StrPath(outputdir, '.doctrees')
399
317
 
400
318
 
401
319
  def _validate_filenames(
@@ -408,8 +326,8 @@ def _validate_filenames(
408
326
 
409
327
 
410
328
  def _validate_colour_support(colour: str) -> None:
411
- if colour == 'no' or (colour == 'auto' and not color_terminal()):
412
- nocolor()
329
+ if colour == 'no' or (colour == 'auto' and not terminal_supports_colour()):
330
+ disable_colour()
413
331
 
414
332
 
415
333
  def _parse_logging(
@@ -431,12 +349,12 @@ def _parse_logging(
431
349
  warnfp = None
432
350
  if warning and warnfile:
433
351
  try:
434
- warnfile = path.abspath(warnfile)
435
- ensuredir(path.dirname(warnfile))
352
+ warn_file = Path(warnfile).resolve()
353
+ ensuredir(warn_file.parent)
436
354
  # the caller is responsible for closing this file descriptor
437
- warnfp = open(warnfile, 'w', encoding='utf-8') # NoQA: SIM115
355
+ warnfp = open(warn_file, 'w', encoding='utf-8') # NoQA: SIM115
438
356
  except Exception as exc:
439
- parser.error(__('cannot open warning file %r: %s') % (warnfile, exc))
357
+ parser.error(__("cannot open warning file '%s': %s") % (warn_file, exc))
440
358
  warning = TeeStripANSI(warning, warnfp) # type: ignore[assignment]
441
359
  error = warning
442
360
 
@@ -514,7 +432,19 @@ def build_main(argv: Sequence[str]) -> int:
514
432
  app.build(args.force_all, args.filenames)
515
433
  return app.statuscode
516
434
  except (Exception, KeyboardInterrupt) as exc:
517
- handle_exception(app, args, exc, args.error)
435
+ if app is not None:
436
+ message_log: Sequence[str] = app.messagelog
437
+ extensions: Collection[Extension] = app.extensions.values()
438
+ else:
439
+ message_log = extensions = ()
440
+ sphinx._cli.util.errors.handle_exception(
441
+ exc,
442
+ stderr=args.error,
443
+ use_pdb=args.pdb,
444
+ print_traceback=args.verbosity or args.traceback,
445
+ message_log=message_log,
446
+ extensions=extensions,
447
+ )
518
448
  return 2
519
449
  finally:
520
450
  if warnfp is not None: