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
sphinx/util/docutils.py CHANGED
@@ -24,7 +24,9 @@ from sphinx.util import logging
24
24
  from sphinx.util.parsing import nested_parse_to_nodes
25
25
 
26
26
  logger = logging.getLogger(__name__)
27
- report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(\\d+)?\\) ')
27
+ report_re = re.compile(
28
+ '^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(\\d+)?\\) '
29
+ )
28
30
 
29
31
  if TYPE_CHECKING:
30
32
  from collections.abc import Callable, Iterator # NoQA: TCH003
@@ -114,8 +116,8 @@ def unregister_node(node: type[Element]) -> None:
114
116
  This is inverse of ``nodes._add_nodes_class_names()``.
115
117
  """
116
118
  if hasattr(nodes.GenericNodeVisitor, 'visit_' + node.__name__):
117
- delattr(nodes.GenericNodeVisitor, "visit_" + node.__name__)
118
- delattr(nodes.GenericNodeVisitor, "depart_" + node.__name__)
119
+ delattr(nodes.GenericNodeVisitor, 'visit_' + node.__name__)
120
+ delattr(nodes.GenericNodeVisitor, 'depart_' + node.__name__)
119
121
  delattr(nodes.SparseNodeVisitor, 'visit_' + node.__name__)
120
122
  delattr(nodes.SparseNodeVisitor, 'depart_' + node.__name__)
121
123
 
@@ -129,7 +131,9 @@ def patched_get_language() -> Iterator[None]:
129
131
  """
130
132
  from docutils.languages import get_language
131
133
 
132
- def patched_get_language(language_code: str, reporter: Reporter | None = None) -> Any:
134
+ def patched_get_language(
135
+ language_code: str, reporter: Reporter | None = None
136
+ ) -> Any:
133
137
  return get_language(language_code)
134
138
 
135
139
  try:
@@ -153,7 +157,9 @@ def patched_rst_get_language() -> Iterator[None]:
153
157
  """
154
158
  from docutils.parsers.rst.languages import get_language
155
159
 
156
- def patched_get_language(language_code: str, reporter: Reporter | None = None) -> Any:
160
+ def patched_get_language(
161
+ language_code: str, reporter: Reporter | None = None
162
+ ) -> Any:
157
163
  return get_language(language_code)
158
164
 
159
165
  try:
@@ -170,7 +176,9 @@ def using_user_docutils_conf(confdir: str | None) -> Iterator[None]:
170
176
  try:
171
177
  docutilsconfig = os.environ.get('DOCUTILSCONFIG', None)
172
178
  if confdir:
173
- os.environ['DOCUTILSCONFIG'] = path.join(path.abspath(confdir), 'docutils.conf')
179
+ os.environ['DOCUTILSCONFIG'] = path.join(
180
+ path.abspath(confdir), 'docutils.conf'
181
+ )
174
182
 
175
183
  yield
176
184
  finally:
@@ -183,9 +191,11 @@ def using_user_docutils_conf(confdir: str | None) -> Iterator[None]:
183
191
  @contextmanager
184
192
  def patch_docutils(confdir: str | None = None) -> Iterator[None]:
185
193
  """Patch to docutils temporarily."""
186
- with patched_get_language(), \
187
- patched_rst_get_language(), \
188
- using_user_docutils_conf(confdir):
194
+ with (
195
+ patched_get_language(),
196
+ patched_rst_get_language(),
197
+ using_user_docutils_conf(confdir),
198
+ ):
189
199
  yield
190
200
 
191
201
 
@@ -204,7 +214,7 @@ class CustomReSTDispatcher:
204
214
  self.enable()
205
215
 
206
216
  def __exit__(
207
- self, exc_type: type[Exception], exc_value: Exception, traceback: Any,
217
+ self, exc_type: type[Exception], exc_value: Exception, traceback: Any
208
218
  ) -> None:
209
219
  self.disable()
210
220
 
@@ -219,16 +229,27 @@ class CustomReSTDispatcher:
219
229
  directives.directive = self.directive_func
220
230
  roles.role = self.role_func
221
231
 
222
- def directive(self,
223
- directive_name: str, language_module: ModuleType, document: nodes.document,
224
- ) -> tuple[type[Directive] | None, list[system_message]]:
232
+ def directive(
233
+ self,
234
+ directive_name: str,
235
+ language_module: ModuleType,
236
+ document: nodes.document,
237
+ ) -> tuple[type[Directive] | None, list[system_message]]:
225
238
  return self.directive_func(directive_name, language_module, document)
226
239
 
227
240
  def role(
228
- self, role_name: str, language_module: ModuleType, lineno: int, reporter: Reporter,
241
+ self,
242
+ role_name: str,
243
+ language_module: ModuleType,
244
+ lineno: int,
245
+ reporter: Reporter,
229
246
  ) -> tuple[RoleFunction, list[system_message]]:
230
- return self.role_func(role_name, language_module, # type: ignore[return-value]
231
- lineno, reporter)
247
+ return self.role_func(
248
+ role_name,
249
+ language_module, # type: ignore[return-value]
250
+ lineno,
251
+ reporter,
252
+ )
232
253
 
233
254
 
234
255
  class ElementLookupError(Exception):
@@ -258,7 +279,9 @@ class sphinx_domains(CustomReSTDispatcher):
258
279
  if element is not None:
259
280
  return element, []
260
281
  else:
261
- logger.warning(_('unknown directive or role name: %s:%s'), domain_name, name)
282
+ logger.warning(
283
+ _('unknown directive or role name: %s:%s'), domain_name, name
284
+ )
262
285
  # else look in the default domain
263
286
  else:
264
287
  def_domain = self.env.temp_data.get('default_domain')
@@ -268,22 +291,29 @@ class sphinx_domains(CustomReSTDispatcher):
268
291
  return element, []
269
292
 
270
293
  # always look in the std domain
271
- element = getattr(self.env.get_domain('std'), type)(name)
294
+ element = getattr(self.env.domains.standard_domain, type)(name)
272
295
  if element is not None:
273
296
  return element, []
274
297
 
275
298
  raise ElementLookupError
276
299
 
277
- def directive(self,
278
- directive_name: str, language_module: ModuleType, document: nodes.document,
279
- ) -> tuple[type[Directive] | None, list[system_message]]:
300
+ def directive(
301
+ self,
302
+ directive_name: str,
303
+ language_module: ModuleType,
304
+ document: nodes.document,
305
+ ) -> tuple[type[Directive] | None, list[system_message]]:
280
306
  try:
281
307
  return self.lookup_domain_element('directive', directive_name)
282
308
  except ElementLookupError:
283
309
  return super().directive(directive_name, language_module, document)
284
310
 
285
311
  def role(
286
- self, role_name: str, language_module: ModuleType, lineno: int, reporter: Reporter,
312
+ self,
313
+ role_name: str,
314
+ language_module: ModuleType,
315
+ lineno: int,
316
+ reporter: Reporter,
287
317
  ) -> tuple[RoleFunction, list[system_message]]:
288
318
  try:
289
319
  return self.lookup_domain_element('role', role_name)
@@ -295,26 +325,39 @@ class WarningStream:
295
325
  def write(self, text: str) -> None:
296
326
  matched = report_re.search(text)
297
327
  if not matched:
298
- logger.warning(text.rstrip("\r\n"), type="docutils")
328
+ logger.warning(text.rstrip('\r\n'), type='docutils')
299
329
  else:
300
330
  location, type, level = matched.groups()
301
331
  message = report_re.sub('', text).rstrip()
302
- logger.log(type, message, location=location, type="docutils")
332
+ logger.log(type, message, location=location, type='docutils')
303
333
 
304
334
 
305
335
  class LoggingReporter(Reporter):
306
336
  @classmethod
307
- def from_reporter(cls: type[LoggingReporter], reporter: Reporter) -> LoggingReporter:
337
+ def from_reporter(
338
+ cls: type[LoggingReporter], reporter: Reporter
339
+ ) -> LoggingReporter:
308
340
  """Create an instance of LoggingReporter from other reporter object."""
309
- return cls(reporter.source, reporter.report_level, reporter.halt_level,
310
- reporter.debug_flag, reporter.error_handler)
341
+ return cls(
342
+ reporter.source,
343
+ reporter.report_level,
344
+ reporter.halt_level,
345
+ reporter.debug_flag,
346
+ reporter.error_handler,
347
+ )
311
348
 
312
- def __init__(self, source: str, report_level: int = Reporter.WARNING_LEVEL,
313
- halt_level: int = Reporter.SEVERE_LEVEL, debug: bool = False,
314
- error_handler: str = 'backslashreplace') -> None:
349
+ def __init__(
350
+ self,
351
+ source: str,
352
+ report_level: int = Reporter.WARNING_LEVEL,
353
+ halt_level: int = Reporter.SEVERE_LEVEL,
354
+ debug: bool = False,
355
+ error_handler: str = 'backslashreplace',
356
+ ) -> None:
315
357
  stream = cast(IO, WarningStream())
316
- super().__init__(source, report_level, halt_level,
317
- stream, debug, error_handler=error_handler)
358
+ super().__init__(
359
+ source, report_level, halt_level, stream, debug, error_handler=error_handler
360
+ )
318
361
 
319
362
 
320
363
  class NullReporter(Reporter):
@@ -351,8 +394,13 @@ class SphinxFileOutput(FileOutput):
351
394
  super().__init__(**kwargs)
352
395
 
353
396
  def write(self, data: str) -> str:
354
- if (self.destination_path and self.autoclose and 'b' not in self.mode and
355
- self.overwrite_if_changed and os.path.exists(self.destination_path)):
397
+ if (
398
+ self.destination_path
399
+ and self.autoclose
400
+ and 'b' not in self.mode
401
+ and self.overwrite_if_changed
402
+ and os.path.exists(self.destination_path)
403
+ ):
356
404
  with open(self.destination_path, encoding=self.encoding) as f:
357
405
  # skip writing: content not changed
358
406
  if f.read() == data:
@@ -416,7 +464,9 @@ class SphinxDirective(Directive):
416
464
  return f'<unknown>:{line}'
417
465
  return ''
418
466
 
419
- def parse_content_to_nodes(self, allow_section_headings: bool = False) -> list[Node]:
467
+ def parse_content_to_nodes(
468
+ self, allow_section_headings: bool = False
469
+ ) -> list[Node]:
420
470
  """Parse the directive's content into nodes.
421
471
 
422
472
  :param allow_section_headings:
@@ -437,7 +487,12 @@ class SphinxDirective(Directive):
437
487
  )
438
488
 
439
489
  def parse_text_to_nodes(
440
- self, text: str = '', /, *, offset: int = -1, allow_section_headings: bool = False,
490
+ self,
491
+ text: str = '',
492
+ /,
493
+ *,
494
+ offset: int = -1,
495
+ allow_section_headings: bool = False,
441
496
  ) -> list[Node]:
442
497
  """Parse *text* into nodes.
443
498
 
@@ -465,7 +520,7 @@ class SphinxDirective(Directive):
465
520
  )
466
521
 
467
522
  def parse_inline(
468
- self, text: str, *, lineno: int = -1,
523
+ self, text: str, *, lineno: int = -1
469
524
  ) -> tuple[list[Node], list[system_message]]:
470
525
  """Parse *text* as inline elements.
471
526
 
@@ -496,6 +551,7 @@ class SphinxRole:
496
551
  This class is strongly coupled with Sphinx.
497
552
  """
498
553
 
554
+ # fmt: off
499
555
  name: str #: The role name actually used in the document.
500
556
  rawtext: str #: A string containing the entire interpreted text input.
501
557
  text: str #: The interpreted text content.
@@ -507,10 +563,18 @@ class SphinxRole:
507
563
  #: A list of strings, the directive content for customisation
508
564
  #: (from the "role" directive).
509
565
  content: Sequence[str]
510
-
511
- def __call__(self, name: str, rawtext: str, text: str, lineno: int,
512
- inliner: Inliner, options: dict | None = None, content: Sequence[str] = (),
513
- ) -> tuple[list[Node], list[system_message]]:
566
+ # fmt: on
567
+
568
+ def __call__(
569
+ self,
570
+ name: str,
571
+ rawtext: str,
572
+ text: str,
573
+ lineno: int,
574
+ inliner: Inliner,
575
+ options: dict | None = None,
576
+ content: Sequence[str] = (),
577
+ ) -> tuple[list[Node], list[system_message]]:
514
578
  self.rawtext = rawtext
515
579
  self.text = unescape(text)
516
580
  self.lineno = lineno
@@ -585,17 +649,26 @@ class ReferenceRole(SphinxRole):
585
649
  .. versionadded:: 2.0
586
650
  """
587
651
 
652
+ # fmt: off
588
653
  has_explicit_title: bool #: A boolean indicates the role has explicit title or not.
589
654
  disabled: bool #: A boolean indicates the reference is disabled.
590
655
  title: str #: The link title for the interpreted text.
591
656
  target: str #: The link target for the interpreted text.
657
+ # fmt: on
592
658
 
593
659
  # \x00 means the "<" was backslash-escaped
594
660
  explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL)
595
661
 
596
- def __call__(self, name: str, rawtext: str, text: str, lineno: int,
597
- inliner: Inliner, options: dict | None = None, content: Sequence[str] = (),
598
- ) -> tuple[list[Node], list[system_message]]:
662
+ def __call__(
663
+ self,
664
+ name: str,
665
+ rawtext: str,
666
+ text: str,
667
+ lineno: int,
668
+ inliner: Inliner,
669
+ options: dict | None = None,
670
+ content: Sequence[str] = (),
671
+ ) -> tuple[list[Node], list[system_message]]:
599
672
  if options is None:
600
673
  options = {}
601
674
 
@@ -698,6 +771,7 @@ def new_document(source_path: str, settings: Any = None) -> nodes.document:
698
771
 
699
772
  # Create a new instance of nodes.document using cached reporter
700
773
  from sphinx import addnodes
774
+
701
775
  document = addnodes.document(settings, reporter, source=source_path)
702
776
  document.note_source(source_path, -1)
703
777
  return document
sphinx/util/exceptions.py CHANGED
@@ -31,12 +31,18 @@ def save_traceback(app: Sphinx | None, exc: BaseException) -> str:
31
31
  last_msgs = exts_list = ''
32
32
  else:
33
33
  extensions = app.extensions.values()
34
- last_msgs = '\n'.join(f'# {strip_escape_sequences(s).strip()}'
35
- for s in app.messagelog)
36
- exts_list = '\n'.join(f'# {ext.name} ({ext.version})' for ext in extensions
37
- if ext.version != 'builtin')
34
+ last_msgs = '\n'.join(
35
+ f'# {strip_escape_sequences(s).strip()}' for s in app.messagelog
36
+ )
37
+ exts_list = '\n'.join(
38
+ f'# {ext.name} ({ext.version})'
39
+ for ext in extensions
40
+ if ext.version != 'builtin'
41
+ )
38
42
 
39
- with NamedTemporaryFile('w', suffix='.log', prefix='sphinx-err-', delete=False) as f:
43
+ with NamedTemporaryFile(
44
+ 'w', suffix='.log', prefix='sphinx-err-', delete=False
45
+ ) as f:
40
46
  f.write(f"""\
41
47
  # Platform: {sys.platform}; ({platform.platform()})
42
48
  # Sphinx version: {sphinx.__display_version__}
sphinx/util/fileutil.py CHANGED
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import os
6
6
  import posixpath
7
+ from pathlib import Path
7
8
  from typing import TYPE_CHECKING, Any
8
9
 
9
10
  from docutils.utils import relative_path
@@ -34,11 +35,14 @@ def _template_basename(filename: str | os.PathLike[str]) -> str | None:
34
35
  return None
35
36
 
36
37
 
37
- def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLike[str],
38
- context: dict[str, Any] | None = None,
39
- renderer: BaseRenderer | None = None,
40
- *,
41
- force: bool = False) -> None:
38
+ def copy_asset_file(
39
+ source: str | os.PathLike[str],
40
+ destination: str | os.PathLike[str],
41
+ context: dict[str, Any] | None = None,
42
+ renderer: BaseRenderer | None = None,
43
+ *,
44
+ force: bool = False,
45
+ ) -> None:
42
46
  """Copy an asset file to destination.
43
47
 
44
48
  On copying, it expands the template variables if context argument is given and
@@ -53,44 +57,59 @@ def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLi
53
57
  if not os.path.exists(source):
54
58
  return
55
59
 
56
- if os.path.isdir(destination):
60
+ destination = Path(destination)
61
+ if destination.is_dir():
57
62
  # Use source filename if destination points a directory
58
- destination = os.path.join(destination, os.path.basename(source))
59
- else:
60
- destination = str(destination)
63
+ destination /= os.path.basename(source)
61
64
 
62
65
  if _template_basename(source) and context is not None:
63
66
  if renderer is None:
64
67
  from sphinx.util.template import SphinxRenderer
68
+
65
69
  renderer = SphinxRenderer()
66
70
 
67
71
  with open(source, encoding='utf-8') as fsrc:
68
72
  template_content = fsrc.read()
69
73
  rendered_template = renderer.render_string(template_content, context)
70
74
 
71
- if (
72
- not force
73
- and os.path.exists(destination)
74
- and template_content != rendered_template
75
- ):
76
- msg = __('Aborted attempted copy from rendered template %s to %s '
77
- '(the destination path has existing data).')
78
- logger.warning(msg, os.fsdecode(source), os.fsdecode(destination),
79
- type='misc', subtype='copy_overwrite')
75
+ if not force and destination.exists() and template_content != rendered_template:
76
+ msg = __(
77
+ 'Aborted attempted copy from rendered template %s to %s '
78
+ '(the destination path has existing data).'
79
+ )
80
+ logger.warning(
81
+ msg,
82
+ os.fsdecode(source),
83
+ os.fsdecode(destination),
84
+ type='misc',
85
+ subtype='copy_overwrite',
86
+ )
80
87
  return
81
88
 
82
89
  destination = _template_basename(destination) or destination
83
90
  with open(destination, 'w', encoding='utf-8') as fdst:
91
+ msg = __('Writing evaluated template result to %s')
92
+ logger.info(
93
+ msg,
94
+ os.fsdecode(destination),
95
+ type='misc',
96
+ subtype='template_evaluation',
97
+ )
84
98
  fdst.write(rendered_template)
85
99
  else:
86
100
  copyfile(source, destination, force=force)
87
101
 
88
102
 
89
- def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[str],
90
- excluded: PathMatcher = lambda path: False,
91
- context: dict[str, Any] | None = None, renderer: BaseRenderer | None = None,
92
- onerror: Callable[[str, Exception], None] | None = None,
93
- *, force: bool = False) -> None:
103
+ def copy_asset(
104
+ source: str | os.PathLike[str],
105
+ destination: str | os.PathLike[str],
106
+ excluded: PathMatcher = lambda path: False,
107
+ context: dict[str, Any] | None = None,
108
+ renderer: BaseRenderer | None = None,
109
+ onerror: Callable[[str, Exception], None] | None = None,
110
+ *,
111
+ force: bool = False,
112
+ ) -> None:
94
113
  """Copy asset files to destination recursively.
95
114
 
96
115
  On copying, it expands the template variables if context argument is given and
@@ -111,14 +130,14 @@ def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[st
111
130
 
112
131
  if renderer is None:
113
132
  from sphinx.util.template import SphinxRenderer
133
+
114
134
  renderer = SphinxRenderer()
115
135
 
116
136
  ensuredir(destination)
117
137
  if os.path.isfile(source):
118
- copy_asset_file(source, destination,
119
- context=context,
120
- renderer=renderer,
121
- force=force)
138
+ copy_asset_file(
139
+ source, destination, context=context, renderer=renderer, force=force
140
+ )
122
141
  return
123
142
 
124
143
  for root, dirs, files in os.walk(source, followlinks=True):
@@ -132,11 +151,13 @@ def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[st
132
151
  for filename in files:
133
152
  if not excluded(posixpath.join(reldir, filename)):
134
153
  try:
135
- copy_asset_file(posixpath.join(root, filename),
136
- posixpath.join(destination, reldir),
137
- context=context,
138
- renderer=renderer,
139
- force=force)
154
+ copy_asset_file(
155
+ posixpath.join(root, filename),
156
+ posixpath.join(destination, reldir),
157
+ context=context,
158
+ renderer=renderer,
159
+ force=force,
160
+ )
140
161
  except Exception as exc:
141
162
  if onerror:
142
163
  onerror(posixpath.join(root, filename), exc)
sphinx/util/http_date.py CHANGED
@@ -12,7 +12,7 @@ from sphinx.deprecation import RemovedInSphinx90Warning
12
12
  _WEEKDAY_NAME = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
13
13
  _MONTH_NAME = ('', # Placeholder for indexing purposes
14
14
  'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
15
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
15
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec') # fmt: skip
16
16
  _GMT_OFFSET = float(time.localtime().tm_gmtoff)
17
17
 
18
18
 
@@ -29,18 +29,20 @@ def rfc1123_to_epoch(rfc1123: str) -> float:
29
29
  t = parsedate_tz(rfc1123)
30
30
  if t is None:
31
31
  raise ValueError
32
- if not rfc1123.endswith(" GMT"):
32
+ if not rfc1123.endswith(' GMT'):
33
33
  warnings.warn(
34
- "HTTP-date string does not meet RFC 7231 requirements "
34
+ 'HTTP-date string does not meet RFC 7231 requirements '
35
35
  f"(must end with 'GMT'): {rfc1123!r}",
36
- RemovedInSphinx90Warning, stacklevel=3,
36
+ RemovedInSphinx90Warning,
37
+ stacklevel=3,
37
38
  )
38
39
  epoch_secs = time.mktime(time.struct_time(t[:9])) + _GMT_OFFSET
39
40
  if (gmt_offset := t[9]) != 0:
40
41
  warnings.warn(
41
- "HTTP-date string does not meet RFC 7231 requirements "
42
- f"(must be GMT time): {rfc1123!r}",
43
- RemovedInSphinx90Warning, stacklevel=3,
42
+ 'HTTP-date string does not meet RFC 7231 requirements '
43
+ f'(must be GMT time): {rfc1123!r}',
44
+ RemovedInSphinx90Warning,
45
+ stacklevel=3,
44
46
  )
45
47
  return epoch_secs - (gmt_offset or 0)
46
48
  return epoch_secs
sphinx/util/i18n.py CHANGED
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import os
6
6
  import re
7
+ import sys
7
8
  from datetime import datetime, timezone
8
9
  from os import path
9
10
  from typing import TYPE_CHECKING, NamedTuple
@@ -59,6 +60,11 @@ if TYPE_CHECKING:
59
60
 
60
61
  Formatter: TypeAlias = DateFormatter | TimeFormatter | DatetimeFormatter
61
62
 
63
+ if sys.version_info[:2] >= (3, 11):
64
+ from datetime import UTC
65
+ else:
66
+ UTC = timezone.utc
67
+
62
68
  logger = logging.getLogger(__name__)
63
69
 
64
70
 
@@ -69,7 +75,6 @@ class LocaleFileInfoBase(NamedTuple):
69
75
 
70
76
 
71
77
  class CatalogInfo(LocaleFileInfoBase):
72
-
73
78
  @property
74
79
  def po_file(self) -> str:
75
80
  return self.domain + '.po'
@@ -88,8 +93,9 @@ class CatalogInfo(LocaleFileInfoBase):
88
93
 
89
94
  def is_outdated(self) -> bool:
90
95
  return (
91
- not path.exists(self.mo_path) or
92
- _last_modified_time(self.mo_path) < _last_modified_time(self.po_path))
96
+ not path.exists(self.mo_path)
97
+ or _last_modified_time(self.mo_path) < _last_modified_time(self.po_path)
98
+ ) # fmt: skip
93
99
 
94
100
  def write_mo(self, locale: str, use_fuzzy: bool = False) -> None:
95
101
  with open(self.po_path, encoding=self.charset) as file_po:
@@ -109,8 +115,13 @@ class CatalogInfo(LocaleFileInfoBase):
109
115
  class CatalogRepository:
110
116
  """A repository for message catalogs."""
111
117
 
112
- def __init__(self, basedir: str | os.PathLike[str], locale_dirs: list[str],
113
- language: str, encoding: str) -> None:
118
+ def __init__(
119
+ self,
120
+ basedir: str | os.PathLike[str],
121
+ locale_dirs: list[str],
122
+ language: str,
123
+ encoding: str,
124
+ ) -> None:
114
125
  self.basedir = basedir
115
126
  self._locale_dirs = locale_dirs
116
127
  self.language = language
@@ -199,13 +210,17 @@ date_format_mappings = {
199
210
  '%z': 'ZZZ', # UTC offset in the form ±HHMM[SS[.ffffff]]
200
211
  # (empty string if the object is naive).
201
212
  '%%': '%',
202
- }
213
+ } # fmt: skip
203
214
 
204
215
  date_format_re = re.compile('(%s)' % '|'.join(date_format_mappings))
205
216
 
206
217
 
207
- def babel_format_date(date: datetime, format: str, locale: str,
208
- formatter: Formatter = babel.dates.format_date) -> str:
218
+ def babel_format_date(
219
+ date: datetime,
220
+ format: str,
221
+ locale: str,
222
+ formatter: Formatter = babel.dates.format_date,
223
+ ) -> str:
209
224
  # Check if we have the tzinfo attribute. If not we cannot do any time
210
225
  # related formats.
211
226
  if not hasattr(date, 'tzinfo'):
@@ -217,22 +232,37 @@ def babel_format_date(date: datetime, format: str, locale: str,
217
232
  # fallback to English
218
233
  return formatter(date, format, locale='en')
219
234
  except AttributeError:
220
- logger.warning(__('Invalid date format. Quote the string by single quote '
221
- 'if you want to output it directly: %s'), format)
235
+ logger.warning(
236
+ __(
237
+ 'Invalid date format. Quote the string by single quote '
238
+ 'if you want to output it directly: %s'
239
+ ),
240
+ format,
241
+ )
222
242
  return format
223
243
 
224
244
 
225
245
  def format_date(
226
- format: str, *, date: datetime | None = None, language: str,
246
+ format: str,
247
+ *,
248
+ date: datetime | None = None,
249
+ language: str,
250
+ local_time: bool = False,
227
251
  ) -> str:
228
252
  if date is None:
229
253
  # If time is not specified, try to use $SOURCE_DATE_EPOCH variable
230
254
  # See https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal
231
255
  source_date_epoch = os.getenv('SOURCE_DATE_EPOCH')
232
256
  if source_date_epoch is not None:
233
- date = datetime.fromtimestamp(float(source_date_epoch), tz=timezone.utc)
257
+ date = datetime.fromtimestamp(float(source_date_epoch), tz=UTC)
234
258
  else:
235
- date = datetime.now(tz=timezone.utc).astimezone()
259
+ date = datetime.now(tz=UTC)
260
+
261
+ if local_time:
262
+ # > If called with tz=None, the system local time zone
263
+ # > is assumed for the target time zone.
264
+ # https://docs.python.org/dev/library/datetime.html#datetime.datetime.astimezone
265
+ date = date.astimezone(tz=None)
236
266
 
237
267
  result = []
238
268
  tokens = date_format_re.split(format)
@@ -251,12 +281,15 @@ def format_date(
251
281
  else:
252
282
  function = babel.dates.format_datetime
253
283
 
254
- result.append(babel_format_date(date, babel_format, locale=language,
255
- formatter=function))
284
+ result.append(
285
+ babel_format_date(
286
+ date, babel_format, locale=language, formatter=function
287
+ )
288
+ )
256
289
  else:
257
290
  result.append(token)
258
291
 
259
- return "".join(result)
292
+ return ''.join(result)
260
293
 
261
294
 
262
295
  def get_image_filename_for_language(