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

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

Potentially problematic release.


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

Files changed (328) hide show
  1. sphinx/__init__.py +8 -4
  2. sphinx/__main__.py +2 -0
  3. sphinx/_cli/__init__.py +2 -5
  4. sphinx/_cli/util/colour.py +34 -11
  5. sphinx/_cli/util/errors.py +128 -61
  6. sphinx/addnodes.py +51 -35
  7. sphinx/application.py +362 -230
  8. sphinx/builders/__init__.py +87 -64
  9. sphinx/builders/_epub_base.py +65 -56
  10. sphinx/builders/changes.py +17 -23
  11. sphinx/builders/dirhtml.py +8 -13
  12. sphinx/builders/epub3.py +70 -38
  13. sphinx/builders/gettext.py +93 -73
  14. sphinx/builders/html/__init__.py +240 -186
  15. sphinx/builders/html/_assets.py +9 -2
  16. sphinx/builders/html/_build_info.py +3 -0
  17. sphinx/builders/latex/__init__.py +64 -54
  18. sphinx/builders/latex/constants.py +14 -11
  19. sphinx/builders/latex/nodes.py +2 -0
  20. sphinx/builders/latex/theming.py +8 -9
  21. sphinx/builders/latex/transforms.py +7 -5
  22. sphinx/builders/linkcheck.py +193 -149
  23. sphinx/builders/manpage.py +17 -17
  24. sphinx/builders/singlehtml.py +28 -16
  25. sphinx/builders/texinfo.py +28 -21
  26. sphinx/builders/text.py +10 -15
  27. sphinx/builders/xml.py +10 -19
  28. sphinx/cmd/build.py +49 -119
  29. sphinx/cmd/make_mode.py +35 -31
  30. sphinx/cmd/quickstart.py +78 -62
  31. sphinx/config.py +265 -163
  32. sphinx/directives/__init__.py +51 -54
  33. sphinx/directives/admonitions.py +107 -0
  34. sphinx/directives/code.py +24 -19
  35. sphinx/directives/other.py +21 -42
  36. sphinx/directives/patches.py +28 -16
  37. sphinx/domains/__init__.py +54 -31
  38. sphinx/domains/_domains_container.py +22 -17
  39. sphinx/domains/_index.py +5 -8
  40. sphinx/domains/c/__init__.py +366 -245
  41. sphinx/domains/c/_ast.py +378 -256
  42. sphinx/domains/c/_ids.py +89 -31
  43. sphinx/domains/c/_parser.py +283 -214
  44. sphinx/domains/c/_symbol.py +269 -198
  45. sphinx/domains/changeset.py +39 -24
  46. sphinx/domains/citation.py +54 -24
  47. sphinx/domains/cpp/__init__.py +517 -362
  48. sphinx/domains/cpp/_ast.py +999 -682
  49. sphinx/domains/cpp/_ids.py +133 -65
  50. sphinx/domains/cpp/_parser.py +746 -588
  51. sphinx/domains/cpp/_symbol.py +692 -489
  52. sphinx/domains/index.py +10 -8
  53. sphinx/domains/javascript.py +152 -74
  54. sphinx/domains/math.py +50 -40
  55. sphinx/domains/python/__init__.py +402 -211
  56. sphinx/domains/python/_annotations.py +134 -61
  57. sphinx/domains/python/_object.py +155 -68
  58. sphinx/domains/rst.py +94 -49
  59. sphinx/domains/std/__init__.py +510 -249
  60. sphinx/environment/__init__.py +345 -61
  61. sphinx/environment/adapters/asset.py +7 -1
  62. sphinx/environment/adapters/indexentries.py +15 -20
  63. sphinx/environment/adapters/toctree.py +19 -9
  64. sphinx/environment/collectors/__init__.py +3 -1
  65. sphinx/environment/collectors/asset.py +18 -15
  66. sphinx/environment/collectors/dependencies.py +8 -10
  67. sphinx/environment/collectors/metadata.py +6 -4
  68. sphinx/environment/collectors/title.py +3 -1
  69. sphinx/environment/collectors/toctree.py +4 -4
  70. sphinx/errors.py +1 -3
  71. sphinx/events.py +4 -4
  72. sphinx/ext/apidoc/__init__.py +66 -0
  73. sphinx/ext/apidoc/__main__.py +9 -0
  74. sphinx/ext/apidoc/_cli.py +356 -0
  75. sphinx/ext/apidoc/_extension.py +262 -0
  76. sphinx/ext/apidoc/_generate.py +356 -0
  77. sphinx/ext/apidoc/_shared.py +99 -0
  78. sphinx/ext/autodoc/__init__.py +837 -483
  79. sphinx/ext/autodoc/directive.py +57 -21
  80. sphinx/ext/autodoc/importer.py +184 -67
  81. sphinx/ext/autodoc/mock.py +25 -10
  82. sphinx/ext/autodoc/preserve_defaults.py +17 -9
  83. sphinx/ext/autodoc/type_comment.py +56 -29
  84. sphinx/ext/autodoc/typehints.py +49 -26
  85. sphinx/ext/autosectionlabel.py +28 -11
  86. sphinx/ext/autosummary/__init__.py +281 -142
  87. sphinx/ext/autosummary/generate.py +121 -51
  88. sphinx/ext/coverage.py +152 -91
  89. sphinx/ext/doctest.py +169 -101
  90. sphinx/ext/duration.py +12 -6
  91. sphinx/ext/extlinks.py +33 -21
  92. sphinx/ext/githubpages.py +8 -8
  93. sphinx/ext/graphviz.py +175 -109
  94. sphinx/ext/ifconfig.py +11 -6
  95. sphinx/ext/imgconverter.py +48 -25
  96. sphinx/ext/imgmath.py +127 -97
  97. sphinx/ext/inheritance_diagram.py +177 -103
  98. sphinx/ext/intersphinx/__init__.py +22 -13
  99. sphinx/ext/intersphinx/__main__.py +3 -1
  100. sphinx/ext/intersphinx/_cli.py +18 -14
  101. sphinx/ext/intersphinx/_load.py +91 -82
  102. sphinx/ext/intersphinx/_resolve.py +108 -74
  103. sphinx/ext/intersphinx/_shared.py +2 -2
  104. sphinx/ext/linkcode.py +28 -12
  105. sphinx/ext/mathjax.py +60 -29
  106. sphinx/ext/napoleon/__init__.py +19 -7
  107. sphinx/ext/napoleon/docstring.py +229 -231
  108. sphinx/ext/todo.py +44 -49
  109. sphinx/ext/viewcode.py +105 -57
  110. sphinx/extension.py +3 -1
  111. sphinx/highlighting.py +13 -7
  112. sphinx/io.py +9 -13
  113. sphinx/jinja2glue.py +29 -26
  114. sphinx/locale/__init__.py +8 -9
  115. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  116. sphinx/locale/ar/LC_MESSAGES/sphinx.po +2155 -2050
  117. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  118. sphinx/locale/bg/LC_MESSAGES/sphinx.po +2045 -1940
  119. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  120. sphinx/locale/bn/LC_MESSAGES/sphinx.po +2175 -2070
  121. sphinx/locale/ca/LC_MESSAGES/sphinx.js +3 -3
  122. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/ca/LC_MESSAGES/sphinx.po +2690 -2585
  124. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.js +63 -0
  125. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.mo +0 -0
  126. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.po +4216 -0
  127. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  128. sphinx/locale/cak/LC_MESSAGES/sphinx.po +2096 -1991
  129. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  130. sphinx/locale/cs/LC_MESSAGES/sphinx.po +2248 -2143
  131. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  132. sphinx/locale/cy/LC_MESSAGES/sphinx.po +2201 -2096
  133. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  134. sphinx/locale/da/LC_MESSAGES/sphinx.po +2282 -2177
  135. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  136. sphinx/locale/de/LC_MESSAGES/sphinx.po +2261 -2156
  137. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  138. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2045 -1940
  139. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  140. sphinx/locale/el/LC_MESSAGES/sphinx.po +2604 -2499
  141. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  142. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2045 -1940
  143. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2045 -1940
  145. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2631 -2526
  147. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  148. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2045 -1940
  149. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  150. sphinx/locale/eo/LC_MESSAGES/sphinx.po +2078 -1973
  151. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/es/LC_MESSAGES/sphinx.po +2633 -2528
  153. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2045 -1940
  155. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/et/LC_MESSAGES/sphinx.po +2449 -2344
  157. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/eu/LC_MESSAGES/sphinx.po +2241 -2136
  159. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/fa/LC_MESSAGES/sphinx.po +504 -500
  161. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/fi/LC_MESSAGES/sphinx.po +499 -495
  163. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/fr/LC_MESSAGES/sphinx.po +513 -509
  165. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +499 -495
  167. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/gl/LC_MESSAGES/sphinx.po +2644 -2539
  169. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/he/LC_MESSAGES/sphinx.po +499 -495
  171. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/hi/LC_MESSAGES/sphinx.po +504 -500
  173. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +499 -495
  175. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/hr/LC_MESSAGES/sphinx.po +501 -497
  177. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/hu/LC_MESSAGES/sphinx.po +499 -495
  179. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  180. sphinx/locale/id/LC_MESSAGES/sphinx.po +2609 -2504
  181. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  182. sphinx/locale/is/LC_MESSAGES/sphinx.po +499 -495
  183. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  184. sphinx/locale/it/LC_MESSAGES/sphinx.po +2265 -2160
  185. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  186. sphinx/locale/ja/LC_MESSAGES/sphinx.po +2621 -2516
  187. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  188. sphinx/locale/ka/LC_MESSAGES/sphinx.po +2567 -2462
  189. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  190. sphinx/locale/ko/LC_MESSAGES/sphinx.po +2631 -2526
  191. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  192. sphinx/locale/lt/LC_MESSAGES/sphinx.po +2214 -2109
  193. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  194. sphinx/locale/lv/LC_MESSAGES/sphinx.po +2218 -2113
  195. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  196. sphinx/locale/mk/LC_MESSAGES/sphinx.po +2088 -1983
  197. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  198. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2247 -2142
  199. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  200. sphinx/locale/ne/LC_MESSAGES/sphinx.po +2227 -2122
  201. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  202. sphinx/locale/nl/LC_MESSAGES/sphinx.po +2316 -2211
  203. sphinx/locale/pl/LC_MESSAGES/sphinx.js +2 -2
  204. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  205. sphinx/locale/pl/LC_MESSAGES/sphinx.po +2442 -2336
  206. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/pt/LC_MESSAGES/sphinx.po +2045 -1940
  208. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2657 -2552
  210. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2243 -2138
  212. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/ro/LC_MESSAGES/sphinx.po +2244 -2139
  214. sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
  215. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  216. sphinx/locale/ru/LC_MESSAGES/sphinx.po +2660 -2555
  217. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  218. sphinx/locale/si/LC_MESSAGES/sphinx.po +2134 -2029
  219. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  220. sphinx/locale/sk/LC_MESSAGES/sphinx.po +2614 -2509
  221. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  222. sphinx/locale/sl/LC_MESSAGES/sphinx.po +2167 -2062
  223. sphinx/locale/sphinx.pot +2069 -1964
  224. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  225. sphinx/locale/sq/LC_MESSAGES/sphinx.po +2661 -2556
  226. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  227. sphinx/locale/sr/LC_MESSAGES/sphinx.po +2213 -2108
  228. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  229. sphinx/locale/sv/LC_MESSAGES/sphinx.po +2229 -2124
  230. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  231. sphinx/locale/te/LC_MESSAGES/sphinx.po +2045 -1940
  232. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  233. sphinx/locale/tr/LC_MESSAGES/sphinx.po +2608 -2503
  234. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  235. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2167 -2062
  236. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  237. sphinx/locale/ur/LC_MESSAGES/sphinx.po +2045 -1940
  238. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  239. sphinx/locale/vi/LC_MESSAGES/sphinx.po +2204 -2099
  240. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  241. sphinx/locale/yue/LC_MESSAGES/sphinx.po +2045 -1940
  242. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  243. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2045 -1940
  244. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  245. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2659 -2554
  246. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  247. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2045 -1940
  248. sphinx/parsers.py +8 -7
  249. sphinx/project.py +2 -2
  250. sphinx/pycode/__init__.py +31 -21
  251. sphinx/pycode/ast.py +6 -3
  252. sphinx/pycode/parser.py +14 -8
  253. sphinx/pygments_styles.py +4 -5
  254. sphinx/registry.py +192 -92
  255. sphinx/roles.py +58 -7
  256. sphinx/search/__init__.py +75 -54
  257. sphinx/search/en.py +11 -13
  258. sphinx/search/fi.py +1 -1
  259. sphinx/search/ja.py +8 -6
  260. sphinx/search/nl.py +1 -1
  261. sphinx/search/zh.py +19 -21
  262. sphinx/testing/fixtures.py +26 -29
  263. sphinx/testing/path.py +26 -62
  264. sphinx/testing/restructuredtext.py +14 -8
  265. sphinx/testing/util.py +21 -19
  266. sphinx/texinputs/make.bat.jinja +50 -50
  267. sphinx/texinputs/sphinx.sty +4 -3
  268. sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
  269. sphinx/texinputs/sphinxlatexobjects.sty +29 -10
  270. sphinx/themes/basic/static/searchtools.js +8 -5
  271. sphinx/theming.py +49 -61
  272. sphinx/transforms/__init__.py +17 -38
  273. sphinx/transforms/compact_bullet_list.py +5 -3
  274. sphinx/transforms/i18n.py +8 -21
  275. sphinx/transforms/post_transforms/__init__.py +142 -93
  276. sphinx/transforms/post_transforms/code.py +5 -5
  277. sphinx/transforms/post_transforms/images.py +28 -24
  278. sphinx/transforms/references.py +3 -1
  279. sphinx/util/__init__.py +109 -60
  280. sphinx/util/_files.py +39 -23
  281. sphinx/util/_importer.py +4 -1
  282. sphinx/util/_inventory_file_reader.py +76 -0
  283. sphinx/util/_io.py +2 -2
  284. sphinx/util/_lines.py +6 -3
  285. sphinx/util/_pathlib.py +40 -2
  286. sphinx/util/build_phase.py +2 -0
  287. sphinx/util/cfamily.py +19 -14
  288. sphinx/util/console.py +44 -179
  289. sphinx/util/display.py +9 -10
  290. sphinx/util/docfields.py +140 -122
  291. sphinx/util/docstrings.py +1 -1
  292. sphinx/util/docutils.py +118 -77
  293. sphinx/util/fileutil.py +25 -26
  294. sphinx/util/http_date.py +2 -0
  295. sphinx/util/i18n.py +77 -64
  296. sphinx/util/images.py +8 -6
  297. sphinx/util/inspect.py +147 -38
  298. sphinx/util/inventory.py +215 -116
  299. sphinx/util/logging.py +33 -33
  300. sphinx/util/matching.py +12 -4
  301. sphinx/util/nodes.py +18 -13
  302. sphinx/util/osutil.py +38 -39
  303. sphinx/util/parallel.py +22 -13
  304. sphinx/util/parsing.py +2 -1
  305. sphinx/util/png.py +6 -2
  306. sphinx/util/requests.py +33 -2
  307. sphinx/util/rst.py +3 -2
  308. sphinx/util/tags.py +1 -1
  309. sphinx/util/template.py +18 -10
  310. sphinx/util/texescape.py +8 -6
  311. sphinx/util/typing.py +148 -122
  312. sphinx/versioning.py +3 -3
  313. sphinx/writers/html.py +3 -1
  314. sphinx/writers/html5.py +63 -52
  315. sphinx/writers/latex.py +83 -67
  316. sphinx/writers/manpage.py +19 -38
  317. sphinx/writers/texinfo.py +47 -47
  318. sphinx/writers/text.py +50 -32
  319. sphinx/writers/xml.py +11 -8
  320. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/LICENSE.rst +1 -1
  321. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/METADATA +25 -15
  322. sphinx-8.2.0.dist-info/RECORD +606 -0
  323. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/WHEEL +1 -1
  324. sphinx/builders/html/transforms.py +0 -90
  325. sphinx/ext/apidoc.py +0 -721
  326. sphinx/util/exceptions.py +0 -74
  327. sphinx-8.1.2.dist-info/RECORD +0 -598
  328. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/entry_points.txt +0 -0
sphinx/cmd/make_mode.py CHANGED
@@ -12,19 +12,15 @@ from __future__ import annotations
12
12
  import os
13
13
  import subprocess
14
14
  import sys
15
- from os import path
15
+ from contextlib import chdir
16
16
  from typing import TYPE_CHECKING
17
17
 
18
18
  import sphinx
19
+ from sphinx._cli.util.colour import blue, bold, disable_colour, terminal_supports_colour
19
20
  from sphinx.cmd.build import build_main
20
- from sphinx.util.console import blue, bold, color_terminal, nocolor
21
+ from sphinx.util._pathlib import _StrPath
21
22
  from sphinx.util.osutil import rmtree
22
23
 
23
- if sys.version_info >= (3, 11):
24
- from contextlib import chdir
25
- else:
26
- from sphinx.util.osutil import _chdir as chdir
27
-
28
24
  if TYPE_CHECKING:
29
25
  from collections.abc import Sequence
30
26
 
@@ -53,7 +49,7 @@ BUILDERS = [
53
49
  (
54
50
  '',
55
51
  'doctest',
56
- 'to run all doctests embedded in the documentation ' '(if enabled)',
52
+ 'to run all doctests embedded in the documentation (if enabled)',
57
53
  ),
58
54
  ('', 'coverage', 'to run coverage check of the documentation (if enabled)'),
59
55
  ('', 'clean', 'to remove everything in the build directory'),
@@ -61,36 +57,42 @@ BUILDERS = [
61
57
 
62
58
 
63
59
  class Make:
64
- def __init__(self, *, source_dir: str, build_dir: str, opts: Sequence[str]) -> None:
65
- self.source_dir = source_dir
66
- self.build_dir = build_dir
60
+ def __init__(
61
+ self,
62
+ *,
63
+ source_dir: str | os.PathLike[str],
64
+ build_dir: str | os.PathLike[str],
65
+ opts: Sequence[str],
66
+ ) -> None:
67
+ self.source_dir = _StrPath(source_dir)
68
+ self.build_dir = _StrPath(build_dir)
67
69
  self.opts = [*opts]
68
70
 
69
- def build_dir_join(self, *comps: str) -> str:
70
- return path.join(self.build_dir, *comps)
71
+ def build_dir_join(self, *comps: str | os.PathLike[str]) -> _StrPath:
72
+ return self.build_dir.joinpath(*comps)
71
73
 
72
74
  def build_clean(self) -> int:
73
- source_dir = path.abspath(self.source_dir)
74
- build_dir = path.abspath(self.build_dir)
75
- if not path.exists(self.build_dir):
75
+ source_dir = self.source_dir.resolve()
76
+ build_dir = self.build_dir.resolve()
77
+ if not self.build_dir.exists():
76
78
  return 0
77
- elif not path.isdir(self.build_dir):
78
- print('Error: %r is not a directory!' % self.build_dir)
79
+ elif not self.build_dir.is_dir():
80
+ print("Error: '%s' is not a directory!" % self.build_dir)
79
81
  return 1
80
82
  elif source_dir == build_dir:
81
- print('Error: %r is same as source directory!' % self.build_dir)
83
+ print("Error: '%s' is same as source directory!" % self.build_dir)
82
84
  return 1
83
- elif path.commonpath([source_dir, build_dir]) == build_dir:
84
- print('Error: %r directory contains source directory!' % self.build_dir)
85
+ elif source_dir.is_relative_to(build_dir):
86
+ print("Error: '%s' directory contains source directory!" % self.build_dir)
85
87
  return 1
86
- print('Removing everything under %r...' % self.build_dir)
87
- for item in os.listdir(self.build_dir):
88
- rmtree(self.build_dir_join(item))
88
+ print("Removing everything under '%s'..." % self.build_dir)
89
+ for item in self.build_dir.iterdir():
90
+ rmtree(item)
89
91
  return 0
90
92
 
91
93
  def build_help(self) -> None:
92
- if not color_terminal():
93
- nocolor()
94
+ if not terminal_supports_colour():
95
+ disable_colour()
94
96
 
95
97
  print(bold('Sphinx v%s' % sphinx.__display_version__))
96
98
  print("Please use `make %s' where %s is one of" % ((blue('target'),) * 2))
@@ -110,7 +112,7 @@ class Make:
110
112
  try:
111
113
  with chdir(self.build_dir_join('latex')):
112
114
  if '-Q' in self.opts:
113
- with open('__LATEXSTDOUT__', 'w') as outfile:
115
+ with open('__LATEXSTDOUT__', 'w', encoding='utf-8') as outfile:
114
116
  returncode = subprocess.call(
115
117
  [
116
118
  makecmd,
@@ -183,7 +185,9 @@ class Make:
183
185
  return 1
184
186
  return 0
185
187
 
186
- def run_generic_build(self, builder: str, doctreedir: str | None = None) -> int:
188
+ def run_generic_build(
189
+ self, builder: str, doctreedir: str | os.PathLike[str] | None = None
190
+ ) -> int:
187
191
  # compatibility with old Makefile
188
192
  paper_size = os.getenv('PAPER', '')
189
193
  if paper_size in {'a4', 'letter'}:
@@ -195,9 +199,9 @@ class Make:
195
199
  '--builder',
196
200
  builder,
197
201
  '--doctree-dir',
198
- doctreedir,
199
- self.source_dir,
200
- self.build_dir_join(builder),
202
+ str(doctreedir),
203
+ str(self.source_dir),
204
+ str(self.build_dir_join(builder)),
201
205
  ]
202
206
  return build_main(args + self.opts)
203
207
 
sphinx/cmd/quickstart.py CHANGED
@@ -5,39 +5,48 @@ from __future__ import annotations
5
5
  import argparse
6
6
  import locale
7
7
  import os
8
+ import os.path
8
9
  import sys
9
10
  import time
10
- from os import path
11
- from typing import TYPE_CHECKING, Any
12
-
13
- # try to import readline, unix specific enhancement
14
- try:
15
- import readline
16
-
17
- if TYPE_CHECKING and sys.platform == 'win32': # always false, for type checking
18
- raise ImportError
19
- READLINE_AVAILABLE = True
20
- if readline.__doc__ and 'libedit' in readline.__doc__:
21
- readline.parse_and_bind('bind ^I rl_complete')
22
- USE_LIBEDIT = True
23
- else:
24
- readline.parse_and_bind('tab: complete')
25
- USE_LIBEDIT = False
26
- except ImportError:
27
- READLINE_AVAILABLE = False
28
- USE_LIBEDIT = False
11
+ from typing import TYPE_CHECKING
29
12
 
30
13
  from docutils.utils import column_width
31
14
 
32
15
  import sphinx.locale
33
16
  from sphinx import __display_version__, package_dir
17
+ from sphinx._cli.util.colour import (
18
+ _create_input_mode_colour_func,
19
+ bold,
20
+ disable_colour,
21
+ red,
22
+ terminal_supports_colour,
23
+ )
34
24
  from sphinx.locale import __
35
- from sphinx.util.console import bold, color_terminal, colorize, nocolor, red
36
25
  from sphinx.util.osutil import ensuredir
37
26
  from sphinx.util.template import SphinxRenderer
38
27
 
39
28
  if TYPE_CHECKING:
40
29
  from collections.abc import Callable, Sequence
30
+ from typing import Any
31
+
32
+ # try to import readline, unix specific enhancement
33
+ try:
34
+ import readline
35
+
36
+ if TYPE_CHECKING and sys.platform == 'win32':
37
+ # MyPy doesn't realise that this raises a ModuleNotFoundError
38
+ # on Windows, and complains that 'parse_and_bind' is not defined.
39
+ # This condition is always False at runtime, but tricks type checkers.
40
+ raise ImportError # NoQA: TRY301
41
+ except ImportError:
42
+ READLINE_AVAILABLE = USE_LIBEDIT = False
43
+ else:
44
+ READLINE_AVAILABLE = True
45
+ USE_LIBEDIT = 'libedit' in getattr(readline, '__doc__', '')
46
+ if USE_LIBEDIT:
47
+ readline.parse_and_bind('bind ^I rl_complete')
48
+ else:
49
+ readline.parse_and_bind('tab: complete')
41
50
 
42
51
  EXTENSIONS = {
43
52
  'autodoc': __('automatically insert docstrings from modules'),
@@ -66,10 +75,17 @@ DEFAULTS = {
66
75
  PROMPT_PREFIX = '> '
67
76
 
68
77
  if sys.platform == 'win32':
69
- # On Windows, show questions as bold because of color scheme of PowerShell (refs: #5294).
70
- COLOR_QUESTION = 'bold'
78
+ # On Windows, show questions as bold because of PowerShell's colour scheme
79
+ # See: https://github.com/sphinx-doc/sphinx/issues/5294
80
+ from sphinx._cli.util.colour import bold as _question_colour
71
81
  else:
72
- COLOR_QUESTION = 'purple'
82
+ from sphinx._cli.util.colour import purple as _question_colour
83
+
84
+ if READLINE_AVAILABLE:
85
+ # Use an input-mode colour function if readline is available
86
+ if escape_code := getattr(_question_colour, '__escape_code', ''):
87
+ _question_colour = _create_input_mode_colour_func(escape_code)
88
+ del escape_code
73
89
 
74
90
 
75
91
  # function to get input from terminal -- overridden by the test suite
@@ -89,15 +105,15 @@ class ValidationError(Exception):
89
105
 
90
106
 
91
107
  def is_path(x: str) -> str:
92
- x = path.expanduser(x)
93
- if not path.isdir(x):
108
+ x = os.path.expanduser(x)
109
+ if not os.path.isdir(x):
94
110
  raise ValidationError(__('Please enter a valid path name.'))
95
111
  return x
96
112
 
97
113
 
98
114
  def is_path_or_empty(x: str) -> str:
99
- if x == '':
100
- return x
115
+ if not x:
116
+ return ''
101
117
  return is_path(x)
102
118
 
103
119
 
@@ -121,9 +137,9 @@ def choice(*l: str) -> Callable[[str], str]:
121
137
 
122
138
 
123
139
  def boolean(x: str) -> bool:
124
- if x.upper() not in ('Y', 'YES', 'N', 'NO'):
140
+ if x.upper() not in {'Y', 'YES', 'N', 'NO'}:
125
141
  raise ValidationError(__("Please enter either 'y' or 'n'."))
126
- return x.upper() in ('Y', 'YES')
142
+ return x.upper() in {'Y', 'YES'}
127
143
 
128
144
 
129
145
  def suffix(x: str) -> str:
@@ -147,15 +163,13 @@ def do_prompt(
147
163
  else:
148
164
  prompt = PROMPT_PREFIX + text + ': '
149
165
  if USE_LIBEDIT:
150
- # Note: libedit has a problem for combination of ``input()`` and escape
151
- # sequence (see #5335). To avoid the problem, all prompts are not colored
152
- # on libedit.
166
+ # Note: libedit has a problem for combination of ``input()``
167
+ # and escape sequences.
168
+ # To avoid the problem, all prompts are not colored on libedit.
169
+ # See https://github.com/sphinx-doc/sphinx/issues/5335
153
170
  pass
154
- elif READLINE_AVAILABLE:
155
- # pass input_mode=True if readline available
156
- prompt = colorize(COLOR_QUESTION, prompt, input_mode=True)
157
171
  else:
158
- prompt = colorize(COLOR_QUESTION, prompt, input_mode=False)
172
+ prompt = _question_colour(prompt)
159
173
  x = term_input(prompt).strip()
160
174
  if default and not x:
161
175
  x = default
@@ -179,12 +193,14 @@ class QuickstartRenderer(SphinxRenderer):
179
193
  Note: Please don't use this function from extensions.
180
194
  It will be removed in the future without deprecation period.
181
195
  """
182
- template = path.join(self.templatedir, path.basename(template_name))
183
- return bool(self.templatedir) and path.exists(template)
196
+ template = os.path.join(self.templatedir, os.path.basename(template_name))
197
+ return bool(self.templatedir) and os.path.exists(template)
184
198
 
185
199
  def render(self, template_name: str, context: dict[str, Any]) -> str:
186
200
  if self._has_custom_template(template_name):
187
- custom_template = path.join(self.templatedir, path.basename(template_name))
201
+ custom_template = os.path.join(
202
+ self.templatedir, os.path.basename(template_name)
203
+ )
188
204
  return self.render_from_file(custom_template, context)
189
205
  else:
190
206
  return super().render(template_name, context)
@@ -228,8 +244,8 @@ def ask_user(d: dict[str, Any]) -> None:
228
244
  print(__('Enter the root path for documentation.'))
229
245
  d['path'] = do_prompt(__('Root path for the documentation'), '.', is_path)
230
246
 
231
- while path.isfile(path.join(d['path'], 'conf.py')) or path.isfile(
232
- path.join(d['path'], 'source', 'conf.py')
247
+ while os.path.isfile(os.path.join(d['path'], 'conf.py')) or os.path.isfile(
248
+ os.path.join(d['path'], 'source', 'conf.py')
233
249
  ):
234
250
  print()
235
251
  print(
@@ -271,7 +287,7 @@ def ask_user(d: dict[str, Any]) -> None:
271
287
  'for custom HTML templates and "_static" for custom stylesheets and other static\n' # NoQA: E501
272
288
  'files. You can enter another prefix (such as ".") to replace the underscore.'
273
289
  )
274
- ) # NoQA: E501
290
+ )
275
291
  d['dot'] = do_prompt(__('Name prefix for templates and static dir'), '_', ok)
276
292
 
277
293
  if 'project' not in d:
@@ -341,8 +357,8 @@ def ask_user(d: dict[str, Any]) -> None:
341
357
  )
342
358
 
343
359
  while (
344
- path.isfile(path.join(d['path'], d['master'] + d['suffix']))
345
- or path.isfile(path.join(d['path'], 'source', d['master'] + d['suffix']))
360
+ os.path.isfile(os.path.join(d['path'], d['master'] + d['suffix']))
361
+ or os.path.isfile(os.path.join(d['path'], 'source', d['master'] + d['suffix']))
346
362
  ): # fmt: skip
347
363
  print()
348
364
  print(
@@ -424,14 +440,14 @@ def generate(
424
440
  d['path'] = os.path.abspath(d['path'])
425
441
  ensuredir(d['path'])
426
442
 
427
- srcdir = path.join(d['path'], 'source') if d['sep'] else d['path']
443
+ srcdir = os.path.join(d['path'], 'source') if d['sep'] else d['path']
428
444
 
429
445
  ensuredir(srcdir)
430
446
  if d['sep']:
431
- builddir = path.join(d['path'], 'build')
447
+ builddir = os.path.join(d['path'], 'build')
432
448
  d['exclude_patterns'] = ''
433
449
  else:
434
- builddir = path.join(srcdir, d['dot'] + 'build')
450
+ builddir = os.path.join(srcdir, d['dot'] + 'build')
435
451
  exclude_patterns = map(
436
452
  repr,
437
453
  [
@@ -442,11 +458,11 @@ def generate(
442
458
  )
443
459
  d['exclude_patterns'] = ', '.join(exclude_patterns)
444
460
  ensuredir(builddir)
445
- ensuredir(path.join(srcdir, d['dot'] + 'templates'))
446
- ensuredir(path.join(srcdir, d['dot'] + 'static'))
461
+ ensuredir(os.path.join(srcdir, d['dot'] + 'templates'))
462
+ ensuredir(os.path.join(srcdir, d['dot'] + 'static'))
447
463
 
448
464
  def write_file(fpath: str, content: str, newline: str | None = None) -> None:
449
- if overwrite or not path.isfile(fpath):
465
+ if overwrite or not os.path.isfile(fpath):
450
466
  if 'quiet' not in d:
451
467
  print(__('Creating file %s.') % fpath)
452
468
  with open(fpath, 'w', encoding='utf-8', newline=newline) as f:
@@ -456,16 +472,16 @@ def generate(
456
472
  print(__('File %s already exists, skipping.') % fpath)
457
473
 
458
474
  conf_path = os.path.join(templatedir, 'conf.py.jinja') if templatedir else None
459
- if not conf_path or not path.isfile(conf_path):
475
+ if not conf_path or not os.path.isfile(conf_path):
460
476
  conf_path = os.path.join(
461
477
  package_dir, 'templates', 'quickstart', 'conf.py.jinja'
462
478
  )
463
479
  with open(conf_path, encoding='utf-8') as f:
464
480
  conf_text = f.read()
465
481
 
466
- write_file(path.join(srcdir, 'conf.py'), template.render_string(conf_text, d))
482
+ write_file(os.path.join(srcdir, 'conf.py'), template.render_string(conf_text, d))
467
483
 
468
- masterfile = path.join(srcdir, d['master'] + d['suffix'])
484
+ masterfile = os.path.join(srcdir, d['master'] + d['suffix'])
469
485
  if template._has_custom_template('quickstart/master_doc.rst.jinja'):
470
486
  write_file(masterfile, template.render('quickstart/master_doc.rst.jinja', d))
471
487
  else:
@@ -479,7 +495,7 @@ def generate(
479
495
  d['rbuilddir'] = 'build' if d['sep'] else d['dot'] + 'build'
480
496
  # use binary mode, to avoid writing \r\n on Windows
481
497
  write_file(
482
- path.join(d['path'], 'Makefile'),
498
+ os.path.join(d['path'], 'Makefile'),
483
499
  template.render(makefile_template, d),
484
500
  '\n',
485
501
  )
@@ -488,7 +504,7 @@ def generate(
488
504
  d['rsrcdir'] = 'source' if d['sep'] else '.'
489
505
  d['rbuilddir'] = 'build' if d['sep'] else d['dot'] + 'build'
490
506
  write_file(
491
- path.join(d['path'], 'make.bat'),
507
+ os.path.join(d['path'], 'make.bat'),
492
508
  template.render(batchfile_template, d),
493
509
  '\r\n',
494
510
  )
@@ -507,7 +523,7 @@ def generate(
507
523
  end='',
508
524
  )
509
525
  if d['makefile'] or d['batchfile']:
510
- print(__('Use the Makefile to build the docs, like so:\n' ' make builder'))
526
+ print(__('Use the Makefile to build the docs, like so:\n make builder'))
511
527
  else:
512
528
  print(
513
529
  __(
@@ -527,9 +543,9 @@ def generate(
527
543
 
528
544
  def valid_dir(d: dict[str, Any]) -> bool:
529
545
  dir = d['path']
530
- if not path.exists(dir):
546
+ if not os.path.exists(dir):
531
547
  return True
532
- if not path.isdir(dir):
548
+ if not os.path.isdir(dir):
533
549
  return False
534
550
 
535
551
  if {'Makefile', 'make.bat'} & set(os.listdir(dir)):
@@ -537,9 +553,9 @@ def valid_dir(d: dict[str, Any]) -> bool:
537
553
 
538
554
  if d['sep']:
539
555
  dir = os.path.join('source', dir)
540
- if not path.exists(dir):
556
+ if not os.path.exists(dir):
541
557
  return True
542
- if not path.isdir(dir):
558
+ if not os.path.isdir(dir):
543
559
  return False
544
560
 
545
561
  reserved_names = [
@@ -721,8 +737,8 @@ def main(argv: Sequence[str] = (), /) -> int:
721
737
  locale.setlocale(locale.LC_ALL, '')
722
738
  sphinx.locale.init_console()
723
739
 
724
- if not color_terminal():
725
- nocolor()
740
+ if not terminal_supports_colour():
741
+ disable_colour()
726
742
 
727
743
  # parse options
728
744
  parser = get_parser()