Sphinx 8.0.2__py3-none-any.whl → 8.1.1__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 (424) hide show
  1. sphinx/__init__.py +6 -3
  2. sphinx/_cli/__init__.py +40 -20
  3. sphinx/_cli/util/colour.py +5 -4
  4. sphinx/_cli/util/errors.py +28 -11
  5. sphinx/application.py +361 -38
  6. sphinx/builders/__init__.py +229 -83
  7. sphinx/builders/_epub_base.py +118 -71
  8. sphinx/builders/changes.py +39 -21
  9. sphinx/builders/dirhtml.py +4 -4
  10. sphinx/builders/dummy.py +2 -5
  11. sphinx/builders/epub3.py +43 -22
  12. sphinx/builders/gettext.py +43 -25
  13. sphinx/builders/html/__init__.py +284 -218
  14. sphinx/builders/html/_assets.py +62 -26
  15. sphinx/builders/html/_build_info.py +76 -0
  16. sphinx/builders/html/transforms.py +11 -9
  17. sphinx/builders/latex/__init__.py +139 -81
  18. sphinx/builders/latex/constants.py +7 -7
  19. sphinx/builders/latex/nodes.py +3 -2
  20. sphinx/builders/latex/theming.py +7 -5
  21. sphinx/builders/latex/transforms.py +27 -19
  22. sphinx/builders/linkcheck.py +146 -72
  23. sphinx/builders/manpage.py +30 -13
  24. sphinx/builders/singlehtml.py +22 -14
  25. sphinx/builders/texinfo.py +67 -37
  26. sphinx/builders/text.py +5 -5
  27. sphinx/builders/xml.py +6 -9
  28. sphinx/cmd/build.py +282 -103
  29. sphinx/cmd/make_mode.py +106 -63
  30. sphinx/cmd/quickstart.py +341 -145
  31. sphinx/config.py +45 -12
  32. sphinx/deprecation.py +8 -2
  33. sphinx/directives/__init__.py +28 -19
  34. sphinx/directives/code.py +86 -56
  35. sphinx/directives/other.py +50 -36
  36. sphinx/directives/patches.py +29 -19
  37. sphinx/domains/__init__.py +20 -120
  38. sphinx/domains/_domains_container.py +281 -0
  39. sphinx/domains/_index.py +110 -0
  40. sphinx/domains/c/__init__.py +3 -3
  41. sphinx/domains/c/_parser.py +10 -6
  42. sphinx/domains/changeset.py +5 -3
  43. sphinx/domains/citation.py +5 -3
  44. sphinx/domains/cpp/__init__.py +9 -11
  45. sphinx/domains/cpp/_parser.py +8 -7
  46. sphinx/domains/index.py +3 -3
  47. sphinx/domains/javascript.py +12 -7
  48. sphinx/domains/math.py +2 -2
  49. sphinx/domains/python/__init__.py +10 -5
  50. sphinx/domains/python/_object.py +1 -1
  51. sphinx/domains/rst.py +5 -5
  52. sphinx/domains/std/__init__.py +16 -11
  53. sphinx/environment/__init__.py +202 -146
  54. sphinx/environment/adapters/asset.py +3 -2
  55. sphinx/environment/adapters/indexentries.py +74 -33
  56. sphinx/environment/adapters/toctree.py +100 -43
  57. sphinx/environment/collectors/__init__.py +19 -8
  58. sphinx/environment/collectors/asset.py +47 -15
  59. sphinx/environment/collectors/dependencies.py +8 -4
  60. sphinx/environment/collectors/metadata.py +7 -2
  61. sphinx/environment/collectors/title.py +7 -2
  62. sphinx/environment/collectors/toctree.py +54 -22
  63. sphinx/errors.py +4 -1
  64. sphinx/events.py +314 -7
  65. sphinx/ext/apidoc.py +42 -18
  66. sphinx/ext/autodoc/__init__.py +52 -24
  67. sphinx/ext/autodoc/importer.py +6 -9
  68. sphinx/ext/autosectionlabel.py +1 -2
  69. sphinx/ext/autosummary/__init__.py +3 -1
  70. sphinx/ext/autosummary/generate.py +28 -14
  71. sphinx/ext/coverage.py +7 -7
  72. sphinx/ext/doctest.py +4 -8
  73. sphinx/ext/duration.py +6 -5
  74. sphinx/ext/inheritance_diagram.py +1 -1
  75. sphinx/ext/intersphinx/_cli.py +6 -4
  76. sphinx/ext/intersphinx/_load.py +77 -32
  77. sphinx/ext/intersphinx/_resolve.py +173 -79
  78. sphinx/ext/intersphinx/_shared.py +7 -5
  79. sphinx/ext/linkcode.py +7 -1
  80. sphinx/ext/mathjax.py +1 -2
  81. sphinx/ext/napoleon/__init__.py +37 -24
  82. sphinx/ext/napoleon/docstring.py +202 -134
  83. sphinx/ext/todo.py +5 -3
  84. sphinx/highlighting.py +9 -2
  85. sphinx/io.py +1 -1
  86. sphinx/jinja2glue.py +27 -6
  87. sphinx/locale/__init__.py +6 -2
  88. sphinx/locale/ar/LC_MESSAGES/sphinx.js +8 -1
  89. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  90. sphinx/locale/ar/LC_MESSAGES/sphinx.po +2246 -2288
  91. sphinx/locale/bg/LC_MESSAGES/sphinx.js +4 -1
  92. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  93. sphinx/locale/bg/LC_MESSAGES/sphinx.po +2113 -2159
  94. sphinx/locale/bn/LC_MESSAGES/sphinx.js +4 -1
  95. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  96. sphinx/locale/bn/LC_MESSAGES/sphinx.po +2349 -2395
  97. sphinx/locale/ca/LC_MESSAGES/sphinx.js +4 -1
  98. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  99. sphinx/locale/ca/LC_MESSAGES/sphinx.po +2846 -2892
  100. sphinx/locale/cak/LC_MESSAGES/sphinx.js +4 -1
  101. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  102. sphinx/locale/cak/LC_MESSAGES/sphinx.po +2213 -2259
  103. sphinx/locale/cs/LC_MESSAGES/sphinx.js +6 -1
  104. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  105. sphinx/locale/cs/LC_MESSAGES/sphinx.po +2225 -2269
  106. sphinx/locale/cy/LC_MESSAGES/sphinx.js +6 -1
  107. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  108. sphinx/locale/cy/LC_MESSAGES/sphinx.po +2403 -2447
  109. sphinx/locale/da/LC_MESSAGES/sphinx.js +4 -1
  110. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  111. sphinx/locale/da/LC_MESSAGES/sphinx.po +2214 -2260
  112. sphinx/locale/de/LC_MESSAGES/sphinx.js +4 -1
  113. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  114. sphinx/locale/de/LC_MESSAGES/sphinx.po +2230 -2276
  115. sphinx/locale/de_DE/LC_MESSAGES/sphinx.js +4 -1
  116. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  117. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2113 -2159
  118. sphinx/locale/el/LC_MESSAGES/sphinx.js +4 -1
  119. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  120. sphinx/locale/el/LC_MESSAGES/sphinx.po +2619 -2665
  121. sphinx/locale/en_DE/LC_MESSAGES/sphinx.js +4 -1
  122. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2113 -2159
  124. sphinx/locale/en_FR/LC_MESSAGES/sphinx.js +4 -1
  125. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  126. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2113 -2159
  127. sphinx/locale/en_GB/LC_MESSAGES/sphinx.js +4 -1
  128. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  129. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2519 -2565
  130. sphinx/locale/en_HK/LC_MESSAGES/sphinx.js +4 -1
  131. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  132. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2113 -2159
  133. sphinx/locale/eo/LC_MESSAGES/sphinx.js +4 -1
  134. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  135. sphinx/locale/eo/LC_MESSAGES/sphinx.po +2232 -2278
  136. sphinx/locale/es/LC_MESSAGES/sphinx.js +5 -1
  137. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  138. sphinx/locale/es/LC_MESSAGES/sphinx.po +2516 -2561
  139. sphinx/locale/es_CO/LC_MESSAGES/sphinx.js +5 -1
  140. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  141. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2114 -2159
  142. sphinx/locale/et/LC_MESSAGES/sphinx.js +4 -1
  143. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/et/LC_MESSAGES/sphinx.po +2317 -2363
  145. sphinx/locale/eu/LC_MESSAGES/sphinx.js +4 -1
  146. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  147. sphinx/locale/eu/LC_MESSAGES/sphinx.po +2218 -2264
  148. sphinx/locale/fa/LC_MESSAGES/sphinx.js +4 -1
  149. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  150. sphinx/locale/fa/LC_MESSAGES/sphinx.po +2505 -2551
  151. sphinx/locale/fi/LC_MESSAGES/sphinx.js +4 -1
  152. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  153. sphinx/locale/fi/LC_MESSAGES/sphinx.po +2303 -2349
  154. sphinx/locale/fr/LC_MESSAGES/sphinx.js +6 -2
  155. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/fr/LC_MESSAGES/sphinx.po +2863 -2908
  157. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.js +5 -1
  158. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  159. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +2114 -2159
  160. sphinx/locale/gl/LC_MESSAGES/sphinx.js +4 -1
  161. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/gl/LC_MESSAGES/sphinx.po +2571 -2617
  163. sphinx/locale/he/LC_MESSAGES/sphinx.js +5 -1
  164. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  165. sphinx/locale/he/LC_MESSAGES/sphinx.po +2307 -2352
  166. sphinx/locale/hi/LC_MESSAGES/sphinx.js +4 -1
  167. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/hi/LC_MESSAGES/sphinx.po +2580 -2626
  169. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.js +4 -1
  170. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  171. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +2113 -2159
  172. sphinx/locale/hr/LC_MESSAGES/sphinx.js +5 -1
  173. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/hr/LC_MESSAGES/sphinx.po +2238 -2283
  175. sphinx/locale/hu/LC_MESSAGES/sphinx.js +4 -1
  176. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  177. sphinx/locale/hu/LC_MESSAGES/sphinx.po +2228 -2274
  178. sphinx/locale/id/LC_MESSAGES/sphinx.js +3 -1
  179. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  180. sphinx/locale/id/LC_MESSAGES/sphinx.po +2787 -2834
  181. sphinx/locale/is/LC_MESSAGES/sphinx.js +4 -1
  182. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  183. sphinx/locale/is/LC_MESSAGES/sphinx.po +2224 -2270
  184. sphinx/locale/it/LC_MESSAGES/sphinx.js +5 -1
  185. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  186. sphinx/locale/it/LC_MESSAGES/sphinx.po +2231 -2276
  187. sphinx/locale/ja/LC_MESSAGES/sphinx.js +3 -1
  188. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  189. sphinx/locale/ja/LC_MESSAGES/sphinx.po +2507 -2554
  190. sphinx/locale/ka/LC_MESSAGES/sphinx.js +4 -1
  191. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  192. sphinx/locale/ka/LC_MESSAGES/sphinx.po +2428 -2474
  193. sphinx/locale/ko/LC_MESSAGES/sphinx.js +3 -1
  194. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  195. sphinx/locale/ko/LC_MESSAGES/sphinx.po +2516 -2563
  196. sphinx/locale/lt/LC_MESSAGES/sphinx.js +6 -1
  197. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  198. sphinx/locale/lt/LC_MESSAGES/sphinx.po +2425 -2469
  199. sphinx/locale/lv/LC_MESSAGES/sphinx.js +5 -1
  200. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  201. sphinx/locale/lv/LC_MESSAGES/sphinx.po +2362 -2407
  202. sphinx/locale/mk/LC_MESSAGES/sphinx.js +4 -1
  203. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  204. sphinx/locale/mk/LC_MESSAGES/sphinx.po +2121 -2167
  205. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.js +4 -1
  206. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2220 -2266
  208. sphinx/locale/ne/LC_MESSAGES/sphinx.js +4 -1
  209. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  210. sphinx/locale/ne/LC_MESSAGES/sphinx.po +2221 -2267
  211. sphinx/locale/nl/LC_MESSAGES/sphinx.js +4 -1
  212. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/nl/LC_MESSAGES/sphinx.po +2240 -2286
  214. sphinx/locale/pl/LC_MESSAGES/sphinx.js +6 -1
  215. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  216. sphinx/locale/pl/LC_MESSAGES/sphinx.po +2319 -2363
  217. sphinx/locale/pt/LC_MESSAGES/sphinx.js +5 -1
  218. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  219. sphinx/locale/pt/LC_MESSAGES/sphinx.po +2114 -2159
  220. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js +5 -1
  221. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  222. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2854 -2899
  223. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.js +5 -1
  224. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  225. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2224 -2269
  226. sphinx/locale/ro/LC_MESSAGES/sphinx.js +5 -1
  227. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  228. sphinx/locale/ro/LC_MESSAGES/sphinx.po +2226 -2271
  229. sphinx/locale/ru/LC_MESSAGES/sphinx.js +8 -3
  230. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  231. sphinx/locale/ru/LC_MESSAGES/sphinx.po +2841 -2885
  232. sphinx/locale/si/LC_MESSAGES/sphinx.js +4 -1
  233. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  234. sphinx/locale/si/LC_MESSAGES/sphinx.po +2294 -2340
  235. sphinx/locale/sk/LC_MESSAGES/sphinx.js +6 -1
  236. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  237. sphinx/locale/sk/LC_MESSAGES/sphinx.po +2497 -2541
  238. sphinx/locale/sl/LC_MESSAGES/sphinx.js +6 -1
  239. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  240. sphinx/locale/sl/LC_MESSAGES/sphinx.po +2331 -2375
  241. sphinx/locale/sphinx.pot +2121 -2167
  242. sphinx/locale/sq/LC_MESSAGES/sphinx.js +4 -1
  243. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  244. sphinx/locale/sq/LC_MESSAGES/sphinx.po +2855 -2901
  245. sphinx/locale/sr/LC_MESSAGES/sphinx.js +5 -1
  246. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  247. sphinx/locale/sr/LC_MESSAGES/sphinx.po +2203 -2248
  248. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
  249. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
  250. sphinx/locale/sv/LC_MESSAGES/sphinx.js +4 -1
  251. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  252. sphinx/locale/sv/LC_MESSAGES/sphinx.po +2423 -2469
  253. sphinx/locale/te/LC_MESSAGES/sphinx.js +4 -1
  254. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  255. sphinx/locale/te/LC_MESSAGES/sphinx.po +2113 -2159
  256. sphinx/locale/tr/LC_MESSAGES/sphinx.js +4 -1
  257. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  258. sphinx/locale/tr/LC_MESSAGES/sphinx.po +2443 -2489
  259. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.js +6 -1
  260. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  261. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2329 -2373
  262. sphinx/locale/ur/LC_MESSAGES/sphinx.js +4 -1
  263. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  264. sphinx/locale/ur/LC_MESSAGES/sphinx.po +2113 -2159
  265. sphinx/locale/vi/LC_MESSAGES/sphinx.js +3 -1
  266. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  267. sphinx/locale/vi/LC_MESSAGES/sphinx.po +2199 -2246
  268. sphinx/locale/yue/LC_MESSAGES/sphinx.js +3 -1
  269. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  270. sphinx/locale/yue/LC_MESSAGES/sphinx.po +2112 -2159
  271. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.js +3 -1
  272. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  273. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2112 -2159
  274. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.js +3 -1
  275. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  276. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2845 -2892
  277. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.js +3 -1
  278. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  279. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2112 -2159
  280. sphinx/parsers.py +3 -1
  281. sphinx/project.py +6 -2
  282. sphinx/pycode/__init__.py +11 -4
  283. sphinx/pycode/ast.py +58 -58
  284. sphinx/pycode/parser.py +49 -28
  285. sphinx/pygments_styles.py +49 -49
  286. sphinx/registry.py +8 -3
  287. sphinx/roles.py +136 -13
  288. sphinx/search/__init__.py +146 -87
  289. sphinx/search/da.py +2 -4
  290. sphinx/search/de.py +2 -4
  291. sphinx/search/en.py +4 -4
  292. sphinx/search/es.py +2 -4
  293. sphinx/search/fi.py +2 -4
  294. sphinx/search/fr.py +2 -4
  295. sphinx/search/hu.py +2 -4
  296. sphinx/search/it.py +2 -4
  297. sphinx/search/ja.py +55 -32
  298. sphinx/search/nl.py +2 -4
  299. sphinx/search/no.py +2 -4
  300. sphinx/search/pt.py +2 -4
  301. sphinx/search/ro.py +0 -2
  302. sphinx/search/ru.py +2 -4
  303. sphinx/search/sv.py +2 -4
  304. sphinx/search/tr.py +0 -2
  305. sphinx/search/zh.py +18 -13
  306. sphinx/templates/graphviz/graphviz.css +0 -7
  307. sphinx/testing/fixtures.py +6 -5
  308. sphinx/testing/path.py +7 -5
  309. sphinx/testing/util.py +63 -29
  310. sphinx/texinputs/sphinx.sty +115 -50
  311. sphinx/texinputs/sphinxlatexadmonitions.sty +56 -38
  312. sphinx/texinputs/sphinxlatexcontainers.sty +1 -1
  313. sphinx/texinputs/sphinxlatexgraphics.sty +3 -2
  314. sphinx/texinputs/sphinxlatexindbibtoc.sty +1 -1
  315. sphinx/texinputs/sphinxlatexlists.sty +1 -1
  316. sphinx/texinputs/sphinxlatexliterals.sty +4 -1
  317. sphinx/texinputs/sphinxlatexnumfig.sty +22 -9
  318. sphinx/texinputs/sphinxlatexobjects.sty +1 -1
  319. sphinx/texinputs/sphinxlatexshadowbox.sty +72 -10
  320. sphinx/texinputs/sphinxlatexstyleheadings.sty +7 -2
  321. sphinx/texinputs/sphinxlatexstylepage.sty +2 -8
  322. sphinx/texinputs/sphinxlatexstyletext.sty +2 -4
  323. sphinx/texinputs/sphinxlatextables.sty +1 -1
  324. sphinx/texinputs/sphinxoptionsgeometry.sty +1 -1
  325. sphinx/texinputs/sphinxoptionshyperref.sty +1 -1
  326. sphinx/themes/agogo/layout.html +1 -10
  327. sphinx/themes/agogo/static/agogo.css.jinja +0 -7
  328. sphinx/themes/basic/defindex.html +1 -8
  329. sphinx/themes/basic/domainindex.html +1 -9
  330. sphinx/themes/basic/genindex-single.html +1 -9
  331. sphinx/themes/basic/genindex-split.html +1 -9
  332. sphinx/themes/basic/genindex.html +1 -9
  333. sphinx/themes/basic/globaltoc.html +1 -9
  334. sphinx/themes/basic/layout.html +1 -9
  335. sphinx/themes/basic/localtoc.html +1 -9
  336. sphinx/themes/basic/page.html +1 -9
  337. sphinx/themes/basic/relations.html +1 -9
  338. sphinx/themes/basic/search.html +1 -9
  339. sphinx/themes/basic/searchbox.html +1 -9
  340. sphinx/themes/basic/searchfield.html +4 -10
  341. sphinx/themes/basic/sourcelink.html +1 -9
  342. sphinx/themes/basic/static/basic.css.jinja +2 -13
  343. sphinx/themes/basic/static/doctools.js +0 -7
  344. sphinx/themes/basic/static/language_data.js.jinja +0 -7
  345. sphinx/themes/basic/static/searchtools.js +25 -13
  346. sphinx/themes/bizstyle/layout.html +1 -9
  347. sphinx/themes/bizstyle/static/bizstyle.css.jinja +0 -7
  348. sphinx/themes/bizstyle/static/bizstyle.js.jinja +5 -11
  349. sphinx/themes/classic/layout.html +1 -9
  350. sphinx/themes/classic/static/classic.css.jinja +0 -7
  351. sphinx/themes/classic/static/sidebar.js.jinja +0 -6
  352. sphinx/themes/epub/epub-cover.html +1 -9
  353. sphinx/themes/epub/layout.html +1 -9
  354. sphinx/themes/epub/static/epub.css.jinja +0 -7
  355. sphinx/themes/haiku/layout.html +1 -9
  356. sphinx/themes/haiku/static/haiku.css.jinja +0 -6
  357. sphinx/themes/nature/static/nature.css.jinja +0 -7
  358. sphinx/themes/nonav/layout.html +1 -9
  359. sphinx/themes/nonav/static/nonav.css.jinja +0 -7
  360. sphinx/themes/pyramid/static/epub.css.jinja +0 -7
  361. sphinx/themes/pyramid/static/pyramid.css.jinja +0 -7
  362. sphinx/themes/scrolls/layout.html +1 -10
  363. sphinx/themes/scrolls/static/scrolls.css.jinja +0 -7
  364. sphinx/themes/sphinxdoc/static/sphinxdoc.css.jinja +2 -7
  365. sphinx/themes/traditional/static/traditional.css.jinja +0 -7
  366. sphinx/theming.py +18 -6
  367. sphinx/transforms/__init__.py +56 -35
  368. sphinx/transforms/compact_bullet_list.py +3 -2
  369. sphinx/transforms/i18n.py +132 -50
  370. sphinx/transforms/post_transforms/__init__.py +94 -43
  371. sphinx/transforms/post_transforms/code.py +7 -6
  372. sphinx/transforms/post_transforms/images.py +71 -54
  373. sphinx/transforms/references.py +1 -2
  374. sphinx/util/__init__.py +23 -194
  375. sphinx/util/_files.py +80 -0
  376. sphinx/util/_importer.py +27 -0
  377. sphinx/util/_io.py +1 -2
  378. sphinx/util/_lines.py +26 -0
  379. sphinx/util/_pathlib.py +5 -2
  380. sphinx/util/_serialise.py +53 -0
  381. sphinx/util/_timestamps.py +2 -1
  382. sphinx/util/_uri.py +16 -0
  383. sphinx/util/cfamily.py +48 -25
  384. sphinx/util/console.py +1 -0
  385. sphinx/util/display.py +1 -1
  386. sphinx/util/docfields.py +125 -45
  387. sphinx/util/docstrings.py +1 -1
  388. sphinx/util/docutils.py +118 -44
  389. sphinx/util/exceptions.py +11 -5
  390. sphinx/util/fileutil.py +53 -32
  391. sphinx/util/http_date.py +9 -7
  392. sphinx/util/i18n.py +49 -16
  393. sphinx/util/images.py +7 -6
  394. sphinx/util/inspect.py +29 -12
  395. sphinx/util/inventory.py +47 -29
  396. sphinx/util/logging.py +58 -85
  397. sphinx/util/matching.py +3 -3
  398. sphinx/util/math.py +1 -1
  399. sphinx/util/nodes.py +176 -108
  400. sphinx/util/osutil.py +13 -10
  401. sphinx/util/parallel.py +5 -4
  402. sphinx/util/parsing.py +5 -3
  403. sphinx/util/png.py +3 -3
  404. sphinx/util/requests.py +8 -4
  405. sphinx/util/rst.py +5 -3
  406. sphinx/util/tags.py +5 -2
  407. sphinx/util/template.py +26 -11
  408. sphinx/util/texescape.py +2 -2
  409. sphinx/util/typing.py +89 -38
  410. sphinx/versioning.py +3 -1
  411. sphinx/writers/html.py +22 -7
  412. sphinx/writers/html5.py +113 -64
  413. sphinx/writers/latex.py +408 -221
  414. sphinx/writers/manpage.py +25 -15
  415. sphinx/writers/texinfo.py +94 -82
  416. sphinx/writers/text.py +87 -53
  417. sphinx/writers/xml.py +5 -4
  418. sphinx-8.1.1.dist-info/LICENSE.rst +31 -0
  419. {sphinx-8.0.2.dist-info → sphinx-8.1.1.dist-info}/METADATA +13 -11
  420. sphinx-8.1.1.dist-info/RECORD +598 -0
  421. sphinx-8.0.2.dist-info/LICENSE.rst +0 -67
  422. sphinx-8.0.2.dist-info/RECORD +0 -590
  423. {sphinx-8.0.2.dist-info → sphinx-8.1.1.dist-info}/WHEEL +0 -0
  424. {sphinx-8.0.2.dist-info → sphinx-8.1.1.dist-info}/entry_points.txt +0 -0
@@ -3,17 +3,16 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import contextlib
6
- import hashlib
7
6
  import html
8
7
  import os
9
8
  import posixpath
10
9
  import re
10
+ import shutil
11
11
  import sys
12
- import types
13
12
  import warnings
14
13
  from os import path
15
14
  from pathlib import Path
16
- from typing import IO, TYPE_CHECKING, Any
15
+ from typing import TYPE_CHECKING, Any
17
16
  from urllib.parse import quote
18
17
 
19
18
  import docutils.readers.doctree
@@ -26,10 +25,15 @@ from docutils.utils import relative_path
26
25
  from sphinx import __display_version__, package_dir
27
26
  from sphinx import version_info as sphinx_version
28
27
  from sphinx.builders import Builder
29
- from sphinx.builders.html._assets import _CascadingStyleSheet, _file_checksum, _JavaScript
28
+ from sphinx.builders.html._assets import (
29
+ _CascadingStyleSheet,
30
+ _file_checksum,
31
+ _JavaScript,
32
+ )
33
+ from sphinx.builders.html._build_info import BuildInfo
30
34
  from sphinx.config import ENUM, Config
31
35
  from sphinx.deprecation import _deprecation_warning
32
- from sphinx.domains import Domain, Index, IndexEntry
36
+ from sphinx.domains import Index, IndexEntry
33
37
  from sphinx.environment.adapters.asset import ImageAdapter
34
38
  from sphinx.environment.adapters.indexentries import IndexEntries
35
39
  from sphinx.environment.adapters.toctree import document_toc, global_toctree_for_doc
@@ -38,8 +42,10 @@ from sphinx.highlighting import PygmentsBridge
38
42
  from sphinx.locale import _, __
39
43
  from sphinx.search import js_index
40
44
  from sphinx.theming import HTMLThemeFactory
41
- from sphinx.util import isurl, logging
45
+ from sphinx.util import logging
42
46
  from sphinx.util._timestamps import _format_rfc3339_microseconds
47
+ from sphinx.util._uri import is_url
48
+ from sphinx.util.console import bold
43
49
  from sphinx.util.display import progress_message, status_iterator
44
50
  from sphinx.util.docutils import new_document
45
51
  from sphinx.util.fileutil import copy_asset
@@ -58,16 +64,14 @@ from sphinx.writers.html import HTMLWriter
58
64
  from sphinx.writers.html5 import HTML5Translator
59
65
 
60
66
  if TYPE_CHECKING:
61
- from collections.abc import Iterable, Iterator, Set
67
+ from collections.abc import Iterator, Set
62
68
  from typing import TypeAlias
63
69
 
64
70
  from docutils.nodes import Node
65
71
  from docutils.readers import Reader
66
72
 
67
73
  from sphinx.application import Sphinx
68
- from sphinx.config import _ConfigRebuild
69
74
  from sphinx.environment import BuildEnvironment
70
- from sphinx.util.tags import Tags
71
75
  from sphinx.util.typing import ExtensionMetadata
72
76
 
73
77
  #: the filename for the inventory of objects
@@ -88,23 +92,6 @@ DOMAIN_INDEX_TYPE: TypeAlias = tuple[
88
92
  ]
89
93
 
90
94
 
91
- def _stable_hash(obj: Any) -> str:
92
- """Return a stable hash for a Python data structure.
93
-
94
- We can't just use the md5 of str(obj) as the order of collections
95
- may be random.
96
- """
97
- if isinstance(obj, dict):
98
- obj = sorted(map(_stable_hash, obj.items()))
99
- if isinstance(obj, list | tuple | set | frozenset):
100
- obj = sorted(map(_stable_hash, obj))
101
- elif isinstance(obj, type | types.FunctionType):
102
- # The default repr() of functions includes the ID, which is not ideal.
103
- # We use the fully qualified name instead.
104
- obj = f'{obj.__module__}.{obj.__qualname__}'
105
- return hashlib.md5(str(obj).encode(), usedforsecurity=False).hexdigest()
106
-
107
-
108
95
  def convert_locale_to_language_tag(locale: str | None) -> str | None:
109
96
  """Convert a locale string to a language tag (ex. en_US -> en-US).
110
97
 
@@ -116,57 +103,6 @@ def convert_locale_to_language_tag(locale: str | None) -> str | None:
116
103
  return None
117
104
 
118
105
 
119
- class BuildInfo:
120
- """buildinfo file manipulator.
121
-
122
- HTMLBuilder and its family are storing their own envdata to ``.buildinfo``.
123
- This class is a manipulator for the file.
124
- """
125
-
126
- @classmethod
127
- def load(cls: type[BuildInfo], f: IO[str]) -> BuildInfo:
128
- try:
129
- lines = f.readlines()
130
- assert lines[0].rstrip() == '# Sphinx build info version 1'
131
- assert lines[2].startswith('config: ')
132
- assert lines[3].startswith('tags: ')
133
-
134
- build_info = BuildInfo()
135
- build_info.config_hash = lines[2].split()[1].strip()
136
- build_info.tags_hash = lines[3].split()[1].strip()
137
- return build_info
138
- except Exception as exc:
139
- raise ValueError(__('build info file is broken: %r') % exc) from exc
140
-
141
- def __init__(
142
- self,
143
- config: Config | None = None,
144
- tags: Tags | None = None,
145
- config_categories: Set[_ConfigRebuild] = frozenset(),
146
- ) -> None:
147
- self.config_hash = ''
148
- self.tags_hash = ''
149
-
150
- if config:
151
- values = {c.name: c.value for c in config.filter(config_categories)}
152
- self.config_hash = _stable_hash(values)
153
-
154
- if tags:
155
- self.tags_hash = _stable_hash(sorted(tags))
156
-
157
- def __eq__(self, other: BuildInfo) -> bool: # type: ignore[override]
158
- return (self.config_hash == other.config_hash and
159
- self.tags_hash == other.tags_hash)
160
-
161
- def dump(self, f: IO[str]) -> None:
162
- f.write('# Sphinx build info version 1\n'
163
- '# This file hashes the configuration used when building these files.'
164
- ' When it is not found, a full rebuild will be done.\n'
165
- 'config: %s\n'
166
- 'tags: %s\n' %
167
- (self.config_hash, self.tags_hash))
168
-
169
-
170
106
  class StandaloneHTMLBuilder(Builder):
171
107
  """
172
108
  Builds standalone HTML docs.
@@ -185,8 +121,7 @@ class StandaloneHTMLBuilder(Builder):
185
121
  indexer_dumps_unicode = True
186
122
  # create links to original images from images [True/False]
187
123
  html_scaled_image_link = True
188
- supported_image_types = ['image/svg+xml', 'image/png',
189
- 'image/gif', 'image/jpeg']
124
+ supported_image_types = ['image/svg+xml', 'image/png', 'image/gif', 'image/jpeg']
190
125
  supported_remote_images = True
191
126
  supported_data_uri_images = True
192
127
  searchindex_filename = 'searchindex.js'
@@ -253,13 +188,17 @@ class StandaloneHTMLBuilder(Builder):
253
188
  return BuildInfo(self.config, self.tags, frozenset({'html'}))
254
189
 
255
190
  def _get_translations_js(self) -> str:
256
- candidates = [path.join(dir, self.config.language,
257
- 'LC_MESSAGES', 'sphinx.js')
258
- for dir in self.config.locale_dirs] + \
259
- [path.join(package_dir, 'locale', self.config.language,
260
- 'LC_MESSAGES', 'sphinx.js'),
261
- path.join(sys.prefix, 'share/sphinx/locale',
262
- self.config.language, 'sphinx.js')]
191
+ candidates = [
192
+ path.join(dir, self.config.language, 'LC_MESSAGES', 'sphinx.js')
193
+ for dir in self.config.locale_dirs
194
+ ] + [
195
+ path.join(
196
+ package_dir, 'locale', self.config.language, 'LC_MESSAGES', 'sphinx.js'
197
+ ),
198
+ path.join(
199
+ sys.prefix, 'share/sphinx/locale', self.config.language, 'sphinx.js'
200
+ ),
201
+ ]
263
202
 
264
203
  for jsfile in candidates:
265
204
  if path.isfile(jsfile):
@@ -307,15 +246,19 @@ class StandaloneHTMLBuilder(Builder):
307
246
  self.dark_highlighter: PygmentsBridge | None
308
247
  if dark_style is not None:
309
248
  self.dark_highlighter = PygmentsBridge('html', dark_style)
310
- self.app.add_css_file('pygments_dark.css',
311
- media='(prefers-color-scheme: dark)',
312
- id='pygments_dark_css')
249
+ self.app.add_css_file(
250
+ 'pygments_dark.css',
251
+ media='(prefers-color-scheme: dark)',
252
+ id='pygments_dark_css',
253
+ )
313
254
  else:
314
255
  self.dark_highlighter = None
315
256
 
316
257
  @property
317
258
  def css_files(self) -> list[_CascadingStyleSheet]:
318
- _deprecation_warning(__name__, f'{self.__class__.__name__}.css_files', remove=(9, 0))
259
+ _deprecation_warning(
260
+ __name__, f'{self.__class__.__name__}.css_files', remove=(9, 0)
261
+ )
319
262
  return self._css_files
320
263
 
321
264
  def init_css_files(self) -> None:
@@ -329,7 +272,8 @@ class StandaloneHTMLBuilder(Builder):
329
272
  self.add_css_file(filename, **attrs)
330
273
 
331
274
  for filename, attrs in self.get_builder_config('css_files', 'html'):
332
- attrs.setdefault('priority', 800) # User's CSSs are loaded after extensions'
275
+ # User's CSSs are loaded after extensions'
276
+ attrs.setdefault('priority', 800)
333
277
  self.add_css_file(filename, **attrs)
334
278
 
335
279
  def add_css_file(self, filename: str, **kwargs: Any) -> None:
@@ -389,22 +333,50 @@ class StandaloneHTMLBuilder(Builder):
389
333
  return None
390
334
 
391
335
  def get_outdated_docs(self) -> Iterator[str]:
336
+ build_info_fname = self.outdir / '.buildinfo'
392
337
  try:
393
- with open(path.join(self.outdir, '.buildinfo'), encoding="utf-8") as fp:
394
- buildinfo = BuildInfo.load(fp)
395
-
396
- if self.build_info != buildinfo:
397
- logger.debug('[build target] did not match: build_info ')
398
- yield from self.env.found_docs
399
- return
338
+ build_info = BuildInfo.load(build_info_fname)
400
339
  except ValueError as exc:
401
340
  logger.warning(__('Failed to read build info file: %r'), exc)
402
341
  except OSError:
403
342
  # ignore errors on reading
404
343
  pass
344
+ else:
345
+ if self.build_info != build_info:
346
+ # log the mismatch and backup the old build info
347
+ build_info_backup = build_info_fname.with_name('.buildinfo.bak')
348
+ try:
349
+ shutil.move(build_info_fname, build_info_backup)
350
+ self.build_info.dump(build_info_fname)
351
+ except OSError:
352
+ pass # ignore errors
353
+ else:
354
+ # only log on success
355
+ msg = __(
356
+ 'build_info mismatch, copying .buildinfo to .buildinfo.bak'
357
+ )
358
+ logger.info(bold(__('building [html]: ')) + msg)
359
+
360
+ yield from self.env.found_docs
361
+ return
405
362
 
406
363
  if self.templates:
407
364
  template_mtime = int(self.templates.newest_template_mtime() * 10**6)
365
+ try:
366
+ old_mtime = _last_modified_time(build_info_fname)
367
+ except Exception:
368
+ pass
369
+ else:
370
+ # Let users know they have a newer template
371
+ if template_mtime > old_mtime:
372
+ logger.info(
373
+ bold('building [html]: ')
374
+ + __(
375
+ 'template %s has been changed since the previous build, '
376
+ 'all docs will be rebuilt'
377
+ ),
378
+ self.templates.newest_template_name(),
379
+ )
408
380
  else:
409
381
  template_mtime = 0
410
382
  for docname in self.env.found_docs:
@@ -418,7 +390,8 @@ class StandaloneHTMLBuilder(Builder):
418
390
  except Exception:
419
391
  targetmtime = 0
420
392
  try:
421
- srcmtime = max(_last_modified_time(self.env.doc2path(docname)), template_mtime)
393
+ doc_mtime = _last_modified_time(self.env.doc2path(docname))
394
+ srcmtime = max(doc_mtime, template_mtime)
422
395
  if srcmtime > targetmtime:
423
396
  logger.debug(
424
397
  '[build target] targetname %r(%s), template(%s), docname %r(%s)',
@@ -426,7 +399,7 @@ class StandaloneHTMLBuilder(Builder):
426
399
  _format_rfc3339_microseconds(targetmtime),
427
400
  _format_rfc3339_microseconds(template_mtime),
428
401
  docname,
429
- _format_rfc3339_microseconds(_last_modified_time(self.env.doc2path(docname))),
402
+ _format_rfc3339_microseconds(doc_mtime),
430
403
  )
431
404
  yield docname
432
405
  except OSError:
@@ -447,15 +420,19 @@ class StandaloneHTMLBuilder(Builder):
447
420
  self._publisher.publish()
448
421
  return self._publisher.writer.parts
449
422
 
450
- def prepare_writing(self, docnames: set[str]) -> None:
423
+ def prepare_writing(self, docnames: Set[str]) -> None:
451
424
  # create the search indexer
452
425
  self.indexer = None
453
426
  if self.search:
454
427
  from sphinx.search import IndexBuilder
428
+
455
429
  lang = self.config.html_search_language or self.config.language
456
- self.indexer = IndexBuilder(self.env, lang,
457
- self.config.html_search_options,
458
- self.config.html_search_scorer)
430
+ self.indexer = IndexBuilder(
431
+ self.env,
432
+ lang,
433
+ self.config.html_search_options,
434
+ self.config.html_search_scorer,
435
+ )
459
436
  self.load_indexer(docnames)
460
437
 
461
438
  self.docwriter = HTMLWriter(self)
@@ -466,7 +443,8 @@ class StandaloneHTMLBuilder(Builder):
466
443
  self.docsettings: Any = OptionParser(
467
444
  defaults=self.env.settings,
468
445
  components=(self.docwriter,),
469
- read_config_files=True).get_default_values()
446
+ read_config_files=True,
447
+ ).get_default_values()
470
448
  self.docsettings.compact_lists = bool(self.config.html_compact_lists)
471
449
 
472
450
  # determine the additional indices to include
@@ -478,23 +456,29 @@ class StandaloneHTMLBuilder(Builder):
478
456
  indices_config = frozenset(indices_config)
479
457
  else:
480
458
  check_names = False
481
- for domain_name in sorted(self.env.domains):
482
- domain: Domain = self.env.domains[domain_name]
459
+ for domain in self.env.domains.sorted():
483
460
  for index_cls in domain.indices:
484
461
  index_name = f'{domain.name}-{index_cls.name}'
485
462
  if check_names and index_name not in indices_config:
486
463
  continue
487
464
  content, collapse = index_cls(domain).generate()
488
465
  if content:
489
- self.domain_indices.append(
490
- (index_name, index_cls, content, collapse))
466
+ self.domain_indices.append((
467
+ index_name,
468
+ index_cls,
469
+ content,
470
+ collapse,
471
+ ))
491
472
 
492
473
  # format the "last updated on" string, only once is enough since it
493
474
  # typically doesn't include the time of day
494
475
  last_updated: str | None
495
476
  if (lu_fmt := self.config.html_last_updated_fmt) is not None:
496
- lu_fmt = lu_fmt or _('%b %d, %Y')
497
- last_updated = format_date(lu_fmt, language=self.config.language)
477
+ last_updated = format_date(
478
+ lu_fmt or _('%b %d, %Y'),
479
+ language=self.config.language,
480
+ local_time=not self.config.html_last_updated_use_utc,
481
+ )
498
482
  else:
499
483
  last_updated = None
500
484
 
@@ -503,9 +487,9 @@ class StandaloneHTMLBuilder(Builder):
503
487
  logo = self.config.html_logo or ''
504
488
  favicon = self.config.html_favicon or ''
505
489
 
506
- if not isurl(logo):
490
+ if not is_url(logo):
507
491
  logo = path.basename(logo)
508
- if not isurl(favicon):
492
+ if not is_url(favicon):
509
493
  favicon = path.basename(favicon)
510
494
 
511
495
  self.relations = self.env.collect_relations()
@@ -516,8 +500,7 @@ class StandaloneHTMLBuilder(Builder):
516
500
  for indexname, indexcls, _content, _collapse in self.domain_indices:
517
501
  # if it has a short name
518
502
  if indexcls.shortname:
519
- rellinks.append((indexname, indexcls.localname,
520
- '', indexcls.shortname))
503
+ rellinks.append((indexname, indexcls.localname, '', indexcls.shortname))
521
504
 
522
505
  # add assets registered after ``Builder.init()``.
523
506
  for css_filename, attrs in self.app.registry.css_files:
@@ -567,8 +550,8 @@ class StandaloneHTMLBuilder(Builder):
567
550
  }
568
551
  if self.theme:
569
552
  self.globalcontext |= {
570
- f'theme_{key}': val for key, val in
571
- self.theme.get_options(self.theme_options).items()
553
+ f'theme_{key}': val
554
+ for key, val in self.theme.get_options(self.theme_options).items()
572
555
  }
573
556
  self.globalcontext |= self.config.html_context
574
557
 
@@ -602,9 +585,10 @@ class StandaloneHTMLBuilder(Builder):
602
585
  prev = None
603
586
  while related and related[0]:
604
587
  with contextlib.suppress(KeyError):
605
- parents.append(
606
- {'link': self.get_relative_uri(docname, related[0]),
607
- 'title': self.render_partial(titles[related[0]])['title']})
588
+ parents.append({
589
+ 'link': self.get_relative_uri(docname, related[0]),
590
+ 'title': self.render_partial(titles[related[0]])['title'],
591
+ })
608
592
 
609
593
  related = self.relations.get(related[0])
610
594
  if parents:
@@ -618,7 +602,7 @@ class StandaloneHTMLBuilder(Builder):
618
602
  title = self.render_partial(title_node)['title'] if title_node else ''
619
603
 
620
604
  # Suffix for the document
621
- source_suffix = str(self.env.doc2path(docname, False))[len(docname):]
605
+ source_suffix = str(self.env.doc2path(docname, False))[len(docname) :]
622
606
 
623
607
  # the name for the copied source
624
608
  if self.config.html_copy_source:
@@ -728,8 +712,10 @@ class StandaloneHTMLBuilder(Builder):
728
712
  # the total count of lines for each index letter, used to distribute
729
713
  # the entries into two columns
730
714
  genindex = IndexEntries(self.env).create_index(self)
731
- indexcounts = [sum(1 + len(subitems) for _, (_, subitems, _) in entries)
732
- for _k, entries in genindex]
715
+ indexcounts = [
716
+ sum(1 + len(subitems) for _, (_, subitems, _) in entries)
717
+ for _k, entries in genindex
718
+ ]
733
719
 
734
720
  genindexcontext = {
735
721
  'genindexentries': genindex,
@@ -739,15 +725,16 @@ class StandaloneHTMLBuilder(Builder):
739
725
  logger.info('genindex ', nonl=True)
740
726
 
741
727
  if self.config.html_split_index:
742
- self.handle_page('genindex', genindexcontext,
743
- 'genindex-split.html')
744
- self.handle_page('genindex-all', genindexcontext,
745
- 'genindex.html')
728
+ self.handle_page('genindex', genindexcontext, 'genindex-split.html')
729
+ self.handle_page('genindex-all', genindexcontext, 'genindex.html')
746
730
  for (key, entries), count in zip(genindex, indexcounts, strict=True):
747
- ctx = {'key': key, 'entries': entries, 'count': count,
748
- 'genindexentries': genindex}
749
- self.handle_page('genindex-' + key, ctx,
750
- 'genindex-single.html')
731
+ ctx = {
732
+ 'key': key,
733
+ 'entries': entries,
734
+ 'count': count,
735
+ 'genindexentries': genindex,
736
+ }
737
+ self.handle_page('genindex-' + key, ctx, 'genindex-single.html')
751
738
  else:
752
739
  self.handle_page('genindex', genindexcontext, 'genindex.html')
753
740
 
@@ -765,9 +752,14 @@ class StandaloneHTMLBuilder(Builder):
765
752
  if self.images:
766
753
  stringify_func = ImageAdapter(self.app.env).get_original_image_uri
767
754
  ensuredir(self.outdir / self.imagedir)
768
- for src in status_iterator(self.images, __('copying images... '), "brown",
769
- len(self.images), self.app.verbosity,
770
- stringify_func=stringify_func):
755
+ for src in status_iterator(
756
+ self.images,
757
+ __('copying images... '),
758
+ 'brown',
759
+ len(self.images),
760
+ self.app.verbosity,
761
+ stringify_func=stringify_func,
762
+ ):
771
763
  dest = self.images[src]
772
764
  try:
773
765
  copyfile(
@@ -776,8 +768,9 @@ class StandaloneHTMLBuilder(Builder):
776
768
  force=True,
777
769
  )
778
770
  except Exception as err:
779
- logger.warning(__("cannot copy image file '%s': %s"),
780
- self.srcdir / src, err)
771
+ logger.warning(
772
+ __("cannot copy image file '%s': %s"), self.srcdir / src, err
773
+ )
781
774
 
782
775
  def copy_download_files(self) -> None:
783
776
  def to_relpath(f: str) -> str:
@@ -786,26 +779,34 @@ class StandaloneHTMLBuilder(Builder):
786
779
  # copy downloadable files
787
780
  if self.env.dlfiles:
788
781
  ensuredir(self.outdir / '_downloads')
789
- for src in status_iterator(self.env.dlfiles, __('copying downloadable files... '),
790
- "brown", len(self.env.dlfiles), self.app.verbosity,
791
- stringify_func=to_relpath):
782
+ for src in status_iterator(
783
+ self.env.dlfiles,
784
+ __('copying downloadable files... '),
785
+ 'brown',
786
+ len(self.env.dlfiles),
787
+ self.app.verbosity,
788
+ stringify_func=to_relpath,
789
+ ):
792
790
  try:
793
791
  dest = self.outdir / '_downloads' / self.env.dlfiles[src][1]
794
792
  ensuredir(dest.parent)
795
793
  copyfile(self.srcdir / src, dest, force=True)
796
794
  except OSError as err:
797
- logger.warning(__('cannot copy downloadable file %r: %s'),
798
- self.srcdir / src, err)
795
+ logger.warning(
796
+ __('cannot copy downloadable file %r: %s'),
797
+ self.srcdir / src,
798
+ err,
799
+ )
799
800
 
800
801
  def create_pygments_style_file(self) -> None:
801
802
  """Create a style file for pygments."""
802
- with open(path.join(self.outdir, '_static', 'pygments.css'), 'w',
803
- encoding="utf-8") as f:
803
+ pyg_path = path.join(self.outdir, '_static', 'pygments.css')
804
+ with open(pyg_path, 'w', encoding='utf-8') as f:
804
805
  f.write(self.highlighter.get_stylesheet())
805
806
 
806
807
  if self.dark_highlighter:
807
- with open(path.join(self.outdir, '_static', 'pygments_dark.css'), 'w',
808
- encoding="utf-8") as f:
808
+ dark_path = path.join(self.outdir, '_static', 'pygments_dark.css')
809
+ with open(dark_path, 'w', encoding='utf-8') as f:
809
810
  f.write(self.dark_highlighter.get_stylesheet())
810
811
 
811
812
  def copy_translation_js(self) -> None:
@@ -847,28 +848,33 @@ class StandaloneHTMLBuilder(Builder):
847
848
  copy_asset(
848
849
  Path(entry) / 'static',
849
850
  self.outdir / '_static',
850
- excluded=DOTFILES, context=context,
851
- renderer=self.templates, onerror=onerror,
851
+ excluded=DOTFILES,
852
+ context=context,
853
+ renderer=self.templates,
854
+ onerror=onerror,
852
855
  force=True,
853
856
  )
854
857
 
855
858
  def copy_html_static_files(self, context: dict[str, Any]) -> None:
856
859
  def onerror(filename: str, error: Exception) -> None:
857
- logger.warning(__('Failed to copy a file in html_static_file: %s: %r'),
858
- filename, error)
860
+ logger.warning(
861
+ __('Failed to copy a file in html_static_file: %s: %r'), filename, error
862
+ )
859
863
 
860
864
  excluded = Matcher([*self.config.exclude_patterns, '**/.*'])
861
865
  for entry in self.config.html_static_path:
862
866
  copy_asset(
863
867
  self.confdir / entry,
864
868
  self.outdir / '_static',
865
- excluded=excluded, context=context,
866
- renderer=self.templates, onerror=onerror,
869
+ excluded=excluded,
870
+ context=context,
871
+ renderer=self.templates,
872
+ onerror=onerror,
867
873
  force=True,
868
874
  )
869
875
 
870
876
  def copy_html_logo(self) -> None:
871
- if self.config.html_logo and not isurl(self.config.html_logo):
877
+ if self.config.html_logo and not is_url(self.config.html_logo):
872
878
  source_path = self.confdir / self.config.html_logo
873
879
  copyfile(
874
880
  source_path,
@@ -877,7 +883,7 @@ class StandaloneHTMLBuilder(Builder):
877
883
  )
878
884
 
879
885
  def copy_html_favicon(self) -> None:
880
- if self.config.html_favicon and not isurl(self.config.html_favicon):
886
+ if self.config.html_favicon and not is_url(self.config.html_favicon):
881
887
  source_path = self.confdir / self.config.html_favicon
882
888
  copyfile(
883
889
  source_path,
@@ -887,7 +893,7 @@ class StandaloneHTMLBuilder(Builder):
887
893
 
888
894
  def copy_static_files(self) -> None:
889
895
  try:
890
- with progress_message(__('copying static files')):
896
+ with progress_message(__('copying static files'), nonl=False):
891
897
  ensuredir(self.outdir / '_static')
892
898
 
893
899
  # prepare context for templates
@@ -908,7 +914,7 @@ class StandaloneHTMLBuilder(Builder):
908
914
  def copy_extra_files(self) -> None:
909
915
  """Copy html_extra_path files."""
910
916
  try:
911
- with progress_message(__('copying extra files')):
917
+ with progress_message(__('copying extra files'), nonl=False):
912
918
  excluded = Matcher(self.config.exclude_patterns)
913
919
  for extra_path in self.config.html_extra_path:
914
920
  copy_asset(
@@ -922,8 +928,7 @@ class StandaloneHTMLBuilder(Builder):
922
928
 
923
929
  def write_buildinfo(self) -> None:
924
930
  try:
925
- with open(path.join(self.outdir, '.buildinfo'), 'w', encoding="utf-8") as fp:
926
- self.build_info.dump(fp)
931
+ self.build_info.dump(self.outdir / '.buildinfo')
927
932
  except OSError as exc:
928
933
  logger.warning(__('Failed to write build info file: %r'), exc)
929
934
 
@@ -954,16 +959,15 @@ class StandaloneHTMLBuilder(Builder):
954
959
  uri = node['uri']
955
960
  reference = nodes.reference('', '', internal=True)
956
961
  if uri in self.images:
957
- reference['refuri'] = posixpath.join(self.imgpath,
958
- self.images[uri])
962
+ reference['refuri'] = posixpath.join(self.imgpath, self.images[uri])
959
963
  else:
960
964
  reference['refuri'] = uri
961
965
  node.replace_self(reference)
962
966
  reference.append(node)
963
967
 
964
- def load_indexer(self, docnames: Iterable[str]) -> None:
968
+ def load_indexer(self, docnames: Set[str]) -> None:
965
969
  assert self.indexer is not None
966
- keep = set(self.env.all_docs) - set(docnames)
970
+ keep = set(self.env.all_docs).difference(docnames)
967
971
  try:
968
972
  searchindexfn = path.join(self.outdir, self.searchindex_filename)
969
973
  if self.indexer_dumps_unicode:
@@ -974,9 +978,13 @@ class StandaloneHTMLBuilder(Builder):
974
978
  self.indexer.load(fb, self.indexer_format)
975
979
  except (OSError, ValueError):
976
980
  if keep:
977
- logger.warning(__("search index couldn't be loaded, but not all "
978
- 'documents will be built: the index will be '
979
- 'incomplete.'))
981
+ logger.warning(
982
+ __(
983
+ "search index couldn't be loaded, but not all "
984
+ 'documents will be built: the index will be '
985
+ 'incomplete.'
986
+ )
987
+ )
980
988
  # delete all entries for files that will be rebuilt
981
989
  self.indexer.prune(keep)
982
990
 
@@ -990,12 +998,16 @@ class StandaloneHTMLBuilder(Builder):
990
998
  else:
991
999
  self.indexer.feed(pagename, filename, title, doctree)
992
1000
 
993
- def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
1001
+ def _get_local_toctree(
1002
+ self, docname: str, collapse: bool = True, **kwargs: Any
1003
+ ) -> str:
994
1004
  if 'includehidden' not in kwargs:
995
1005
  kwargs['includehidden'] = False
996
1006
  if kwargs.get('maxdepth') == '':
997
1007
  kwargs.pop('maxdepth')
998
- toctree = global_toctree_for_doc(self.env, docname, self, collapse=collapse, **kwargs)
1008
+ toctree = global_toctree_for_doc(
1009
+ self.env, docname, self, collapse=collapse, **kwargs
1010
+ )
999
1011
  return self.render_partial(toctree)['fragment']
1000
1012
 
1001
1013
  def get_outfilename(self, pagename: str) -> str:
@@ -1033,7 +1045,8 @@ class StandaloneHTMLBuilder(Builder):
1033
1045
  return quote(docname) + self.link_suffix
1034
1046
 
1035
1047
  def handle_page(
1036
- self, pagename: str,
1048
+ self,
1049
+ pagename: str,
1037
1050
  addctx: dict[str, Any],
1038
1051
  templatename: str = 'page.html',
1039
1052
  outfilename: str | None = None,
@@ -1049,13 +1062,16 @@ class StandaloneHTMLBuilder(Builder):
1049
1062
  default_baseuri = default_baseuri.rsplit('#', 1)[0]
1050
1063
 
1051
1064
  if self.config.html_baseurl:
1052
- ctx['pageurl'] = posixpath.join(self.config.html_baseurl,
1053
- pagename + self.out_suffix)
1065
+ ctx['pageurl'] = posixpath.join(
1066
+ self.config.html_baseurl, self.get_target_uri(pagename)
1067
+ )
1054
1068
  else:
1055
1069
  ctx['pageurl'] = None
1056
1070
 
1057
1071
  def pathto(
1058
- otheruri: str, resource: bool = False, baseuri: str = default_baseuri,
1072
+ otheruri: str,
1073
+ resource: bool = False,
1074
+ baseuri: str = default_baseuri,
1059
1075
  ) -> str:
1060
1076
  if resource and '://' in otheruri:
1061
1077
  # allow non-local resources given by scheme
@@ -1066,6 +1082,7 @@ class StandaloneHTMLBuilder(Builder):
1066
1082
  if uri == '#' and not self.allow_sharp_as_current_path:
1067
1083
  uri = baseuri
1068
1084
  return uri
1085
+
1069
1086
  ctx['pathto'] = pathto
1070
1087
 
1071
1088
  def hasdoc(name: str) -> bool:
@@ -1074,6 +1091,7 @@ class StandaloneHTMLBuilder(Builder):
1074
1091
  if name == 'search' and self.search:
1075
1092
  return True
1076
1093
  return name == 'genindex' and self.get_builder_config('use_index', 'html')
1094
+
1077
1095
  ctx['hasdoc'] = hasdoc
1078
1096
 
1079
1097
  ctx['toctree'] = lambda **kwargs: self._get_local_toctree(pagename, **kwargs)
@@ -1086,9 +1104,11 @@ class StandaloneHTMLBuilder(Builder):
1086
1104
  outdir = self.app.outdir
1087
1105
 
1088
1106
  def css_tag(css: _CascadingStyleSheet) -> str:
1089
- attrs = [f'{key}="{html.escape(value, quote=True)}"'
1090
- for key, value in css.attributes.items()
1091
- if value is not None]
1107
+ attrs = [
1108
+ f'{key}="{html.escape(value, quote=True)}"'
1109
+ for key, value in css.attributes.items()
1110
+ if value is not None
1111
+ ]
1092
1112
  uri = pathto(os.fspath(css.filename), resource=True)
1093
1113
  # the EPUB format does not allow the use of query components
1094
1114
  # the Windows help compiler requires that css links
@@ -1106,9 +1126,11 @@ class StandaloneHTMLBuilder(Builder):
1106
1126
  return f'<script src="{pathto(js, resource=True)}"></script>'
1107
1127
 
1108
1128
  body = js.attributes.get('body', '')
1109
- attrs = [f'{key}="{html.escape(value, quote=True)}"'
1110
- for key, value in js.attributes.items()
1111
- if key != 'body' and value is not None]
1129
+ attrs = [
1130
+ f'{key}="{html.escape(value, quote=True)}"'
1131
+ for key, value in js.attributes.items()
1132
+ if key != 'body' and value is not None
1133
+ ]
1112
1134
 
1113
1135
  if not js.filename:
1114
1136
  if attrs:
@@ -1137,15 +1159,18 @@ class StandaloneHTMLBuilder(Builder):
1137
1159
  self._js_files[:] = self._orig_js_files
1138
1160
 
1139
1161
  self.update_page_context(pagename, templatename, ctx, event_arg)
1140
- newtmpl = self.app.emit_firstresult('html-page-context', pagename,
1141
- templatename, ctx, event_arg)
1162
+ newtmpl = self.app.emit_firstresult(
1163
+ 'html-page-context', pagename, templatename, ctx, event_arg
1164
+ )
1142
1165
  if newtmpl:
1143
1166
  templatename = newtmpl
1144
1167
 
1145
1168
  # sort JS/CSS before rendering HTML
1146
1169
  try: # NoQA: SIM105
1147
1170
  # Convert script_files to list to support non-list script_files (refs: #8889)
1148
- ctx['script_files'] = sorted(ctx['script_files'], key=lambda js: js.priority)
1171
+ ctx['script_files'] = sorted(
1172
+ ctx['script_files'], key=lambda js: js.priority
1173
+ )
1149
1174
  except AttributeError:
1150
1175
  # Skip sorting if users modifies script_files directly (maybe via `html_context`).
1151
1176
  # refs: #8885
@@ -1159,33 +1184,42 @@ class StandaloneHTMLBuilder(Builder):
1159
1184
  try:
1160
1185
  output = self.templates.render(templatename, ctx)
1161
1186
  except UnicodeError:
1162
- logger.warning(__("a Unicode error occurred when rendering the page %s. "
1163
- "Please make sure all config values that contain "
1164
- "non-ASCII content are Unicode strings."), pagename)
1187
+ logger.warning(
1188
+ __(
1189
+ 'a Unicode error occurred when rendering the page %s. '
1190
+ 'Please make sure all config values that contain '
1191
+ 'non-ASCII content are Unicode strings.'
1192
+ ),
1193
+ pagename,
1194
+ )
1165
1195
  return
1166
1196
  except Exception as exc:
1167
- raise ThemeError(__("An error happened in rendering the page %s.\nReason: %r") %
1168
- (pagename, exc)) from exc
1197
+ msg = __('An error happened in rendering the page %s.\nReason: %r') % (
1198
+ pagename,
1199
+ exc,
1200
+ )
1201
+ raise ThemeError(msg) from exc
1169
1202
 
1170
1203
  if not outfilename:
1171
1204
  outfilename = self.get_outfilename(pagename)
1172
1205
  # outfilename's path is in general different from self.outdir
1173
1206
  ensuredir(path.dirname(outfilename))
1174
1207
  try:
1175
- with open(outfilename, 'w', encoding=ctx['encoding'],
1176
- errors='xmlcharrefreplace') as f:
1208
+ with open(
1209
+ outfilename, 'w', encoding=ctx['encoding'], errors='xmlcharrefreplace'
1210
+ ) as f:
1177
1211
  f.write(output)
1178
1212
  except OSError as err:
1179
- logger.warning(__("error writing file %s: %s"), outfilename, err)
1213
+ logger.warning(__('error writing file %s: %s'), outfilename, err)
1180
1214
  if self.copysource and ctx.get('sourcename'):
1181
1215
  # copy the source file for the "show source" link
1182
- source_name = path.join(self.outdir, '_sources',
1183
- os_path(ctx['sourcename']))
1216
+ source_name = path.join(self.outdir, '_sources', os_path(ctx['sourcename']))
1184
1217
  ensuredir(path.dirname(source_name))
1185
1218
  copyfile(self.env.doc2path(pagename), source_name, force=True)
1186
1219
 
1187
- def update_page_context(self, pagename: str, templatename: str,
1188
- ctx: dict[str, Any], event_arg: Any) -> None:
1220
+ def update_page_context(
1221
+ self, pagename: str, templatename: str, ctx: dict[str, Any], event_arg: Any
1222
+ ) -> None:
1189
1223
  pass
1190
1224
 
1191
1225
  def handle_finish(self) -> None:
@@ -1248,19 +1282,24 @@ def convert_html_js_files(app: Sphinx, config: Config) -> None:
1248
1282
  config.html_js_files = html_js_files
1249
1283
 
1250
1284
 
1251
- def setup_resource_paths(app: Sphinx, pagename: str, templatename: str,
1252
- context: dict[str, Any], doctree: Node) -> None:
1285
+ def setup_resource_paths(
1286
+ app: Sphinx,
1287
+ pagename: str,
1288
+ templatename: str,
1289
+ context: dict[str, Any],
1290
+ doctree: Node,
1291
+ ) -> None:
1253
1292
  """Set up relative resource paths."""
1254
1293
  pathto = context['pathto']
1255
1294
 
1256
1295
  # favicon_url
1257
1296
  favicon_url = context.get('favicon_url')
1258
- if favicon_url and not isurl(favicon_url):
1297
+ if favicon_url and not is_url(favicon_url):
1259
1298
  context['favicon_url'] = pathto('_static/' + favicon_url, resource=True)
1260
1299
 
1261
1300
  # logo_url
1262
1301
  logo_url = context.get('logo_url')
1263
- if logo_url and not isurl(logo_url):
1302
+ if logo_url and not is_url(logo_url):
1264
1303
  context['logo_url'] = pathto('_static/' + logo_url, resource=True)
1265
1304
 
1266
1305
 
@@ -1270,8 +1309,10 @@ def validate_math_renderer(app: Sphinx) -> None:
1270
1309
 
1271
1310
  name = app.builder.math_renderer_name # type: ignore[attr-defined]
1272
1311
  if name is None:
1273
- raise ConfigError(__('Many math_renderers are registered. '
1274
- 'But no math_renderer is selected.'))
1312
+ msg = __(
1313
+ 'Many math_renderers are registered. But no math_renderer is selected.'
1314
+ )
1315
+ raise ConfigError(msg)
1275
1316
  if name not in app.registry.html_inline_math_renderers:
1276
1317
  raise ConfigError(__('Unknown math_renderer %r is given.') % name)
1277
1318
 
@@ -1283,9 +1324,13 @@ def validate_html_extra_path(app: Sphinx, config: Config) -> None:
1283
1324
  if not path.exists(extra_path):
1284
1325
  logger.warning(__('html_extra_path entry %r does not exist'), entry)
1285
1326
  config.html_extra_path.remove(entry)
1286
- elif (path.splitdrive(app.outdir)[0] == path.splitdrive(extra_path)[0] and
1287
- path.commonpath((app.outdir, extra_path)) == path.normpath(app.outdir)):
1288
- logger.warning(__('html_extra_path entry %r is placed inside outdir'), entry)
1327
+ elif (
1328
+ path.splitdrive(app.outdir)[0] == path.splitdrive(extra_path)[0]
1329
+ and path.commonpath((app.outdir, extra_path)) == path.normpath(app.outdir)
1330
+ ): # fmt: skip
1331
+ logger.warning(
1332
+ __('html_extra_path entry %r is placed inside outdir'), entry
1333
+ )
1289
1334
  config.html_extra_path.remove(entry)
1290
1335
 
1291
1336
 
@@ -1296,26 +1341,34 @@ def validate_html_static_path(app: Sphinx, config: Config) -> None:
1296
1341
  if not path.exists(static_path):
1297
1342
  logger.warning(__('html_static_path entry %r does not exist'), entry)
1298
1343
  config.html_static_path.remove(entry)
1299
- elif (path.splitdrive(app.outdir)[0] == path.splitdrive(static_path)[0] and
1300
- path.commonpath((app.outdir, static_path)) == path.normpath(app.outdir)):
1301
- logger.warning(__('html_static_path entry %r is placed inside outdir'), entry)
1344
+ elif (
1345
+ path.splitdrive(app.outdir)[0] == path.splitdrive(static_path)[0]
1346
+ and path.commonpath((app.outdir, static_path)) == path.normpath(app.outdir)
1347
+ ): # fmt: skip
1348
+ logger.warning(
1349
+ __('html_static_path entry %r is placed inside outdir'), entry
1350
+ )
1302
1351
  config.html_static_path.remove(entry)
1303
1352
 
1304
1353
 
1305
1354
  def validate_html_logo(app: Sphinx, config: Config) -> None:
1306
1355
  """Check html_logo setting."""
1307
- if (config.html_logo and
1308
- not path.isfile(path.join(app.confdir, config.html_logo)) and
1309
- not isurl(config.html_logo)):
1356
+ if (
1357
+ config.html_logo
1358
+ and not path.isfile(path.join(app.confdir, config.html_logo))
1359
+ and not is_url(config.html_logo)
1360
+ ):
1310
1361
  logger.warning(__('logo file %r does not exist'), config.html_logo)
1311
1362
  config.html_logo = None
1312
1363
 
1313
1364
 
1314
1365
  def validate_html_favicon(app: Sphinx, config: Config) -> None:
1315
1366
  """Check html_favicon setting."""
1316
- if (config.html_favicon and
1317
- not path.isfile(path.join(app.confdir, config.html_favicon)) and
1318
- not isurl(config.html_favicon)):
1367
+ if (
1368
+ config.html_favicon
1369
+ and not path.isfile(path.join(app.confdir, config.html_favicon))
1370
+ and not is_url(config.html_favicon)
1371
+ ):
1319
1372
  logger.warning(__('favicon file %r does not exist'), config.html_favicon)
1320
1373
  config.html_favicon = None
1321
1374
 
@@ -1328,9 +1381,11 @@ def error_on_html_sidebars_string_values(app: Sphinx, config: Config) -> None:
1328
1381
  errors[pattern] = [pat_sidebars]
1329
1382
  if not errors:
1330
1383
  return
1331
- msg = __("Values in 'html_sidebars' must be a list of strings. "
1332
- "At least one pattern has a string value: %s. "
1333
- "Change to `html_sidebars = %r`.")
1384
+ msg = __(
1385
+ "Values in 'html_sidebars' must be a list of strings. "
1386
+ 'At least one pattern has a string value: %s. '
1387
+ 'Change to `html_sidebars = %r`.'
1388
+ )
1334
1389
  bad_patterns = ', '.join(map(repr, errors))
1335
1390
  fixed = config.html_sidebars | errors
1336
1391
  raise ConfigError(msg % (bad_patterns, fixed))
@@ -1339,10 +1394,11 @@ def error_on_html_sidebars_string_values(app: Sphinx, config: Config) -> None:
1339
1394
  def error_on_html_4(_app: Sphinx, config: Config) -> None:
1340
1395
  """Error on HTML 4."""
1341
1396
  if config.html4_writer:
1342
- raise ConfigError(_(
1397
+ msg = __(
1343
1398
  'HTML 4 is no longer supported by Sphinx. '
1344
1399
  '("html4_writer=True" detected in configuration options)',
1345
- ))
1400
+ )
1401
+ raise ConfigError(msg)
1346
1402
 
1347
1403
 
1348
1404
  def setup(app: Sphinx) -> ExtensionMetadata:
@@ -1354,7 +1410,11 @@ def setup(app: Sphinx) -> ExtensionMetadata:
1354
1410
  app.add_config_value('html_theme_path', [], 'html')
1355
1411
  app.add_config_value('html_theme_options', {}, 'html')
1356
1412
  app.add_config_value(
1357
- 'html_title', lambda c: _('%s %s documentation') % (c.project, c.release), 'html', str)
1413
+ 'html_title',
1414
+ lambda c: _('%s %s documentation') % (c.project, c.release),
1415
+ 'html',
1416
+ str,
1417
+ )
1358
1418
  app.add_config_value('html_short_title', lambda self: self.html_title, 'html')
1359
1419
  app.add_config_value('html_style', None, 'html', {list, str})
1360
1420
  app.add_config_value('html_logo', None, 'html', str)
@@ -1364,6 +1424,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
1364
1424
  app.add_config_value('html_static_path', [], 'html')
1365
1425
  app.add_config_value('html_extra_path', [], 'html')
1366
1426
  app.add_config_value('html_last_updated_fmt', None, 'html', str)
1427
+ app.add_config_value('html_last_updated_use_utc', False, 'html', types={bool})
1367
1428
  app.add_config_value('html_sidebars', {}, 'html')
1368
1429
  app.add_config_value('html_additional_pages', {}, 'html')
1369
1430
  app.add_config_value('html_domain_indices', True, 'html', types={set, list})
@@ -1390,8 +1451,9 @@ def setup(app: Sphinx) -> ExtensionMetadata:
1390
1451
  app.add_config_value('html_scaled_image_link', True, 'html')
1391
1452
  app.add_config_value('html_baseurl', '', 'html')
1392
1453
  # removal is indefinitely on hold (ref: https://github.com/sphinx-doc/sphinx/issues/10265)
1393
- app.add_config_value('html_codeblock_linenos_style', 'inline', 'html',
1394
- ENUM('table', 'inline'))
1454
+ app.add_config_value(
1455
+ 'html_codeblock_linenos_style', 'inline', 'html', ENUM('table', 'inline')
1456
+ )
1395
1457
  app.add_config_value('html_math_renderer', None, 'env')
1396
1458
  app.add_config_value('html4_writer', False, 'html')
1397
1459
 
@@ -1426,7 +1488,11 @@ def setup(app: Sphinx) -> ExtensionMetadata:
1426
1488
 
1427
1489
  # deprecated name -> (object to return, canonical path or empty string, removal version)
1428
1490
  _DEPRECATED_OBJECTS: dict[str, tuple[Any, str, tuple[int, int]]] = {
1429
- 'Stylesheet': (_CascadingStyleSheet, 'sphinx.builders.html._assets._CascadingStyleSheet', (9, 0)), # NoQA: E501
1491
+ 'Stylesheet': (
1492
+ _CascadingStyleSheet,
1493
+ 'sphinx.builders.html._assets._CascadingStyleSheet',
1494
+ (9, 0),
1495
+ ),
1430
1496
  'JavaScript': (_JavaScript, 'sphinx.builders.html._assets._JavaScript', (9, 0)),
1431
1497
  }
1432
1498