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
@@ -8,14 +8,22 @@ import pickle
8
8
  from collections import defaultdict
9
9
  from copy import copy
10
10
  from os import path
11
- from typing import TYPE_CHECKING, Any, NoReturn
11
+ from typing import TYPE_CHECKING
12
12
 
13
13
  from sphinx import addnodes
14
+ from sphinx.domains._domains_container import _DomainsContainer
14
15
  from sphinx.environment.adapters import toctree as toctree_adapters
15
- from sphinx.errors import BuildEnvironmentError, DocumentError, ExtensionError, SphinxError
16
+ from sphinx.errors import (
17
+ BuildEnvironmentError,
18
+ DocumentError,
19
+ ExtensionError,
20
+ SphinxError,
21
+ )
16
22
  from sphinx.locale import __
17
23
  from sphinx.transforms import SphinxTransformer
18
- from sphinx.util import DownloadFiles, FilenameUniqDict, logging
24
+ from sphinx.util import logging
25
+ from sphinx.util._files import DownloadFiles, FilenameUniqDict
26
+ from sphinx.util._serialise import stable_str
19
27
  from sphinx.util._timestamps import _format_rfc3339_microseconds
20
28
  from sphinx.util.docutils import LoggingReporter
21
29
  from sphinx.util.i18n import CatalogRepository, docname_to_domain
@@ -23,8 +31,9 @@ from sphinx.util.nodes import is_translatable
23
31
  from sphinx.util.osutil import _last_modified_time, canon_path, os_path
24
32
 
25
33
  if TYPE_CHECKING:
26
- from collections.abc import Callable, Iterator
34
+ from collections.abc import Callable, Iterable, Iterator
27
35
  from pathlib import Path
36
+ from typing import Any, Literal
28
37
 
29
38
  from docutils import nodes
30
39
  from docutils.nodes import Node
@@ -60,7 +69,7 @@ default_settings: dict[str, Any] = {
60
69
 
61
70
  # This is increased every time an environment attribute is added
62
71
  # or changed to properly invalidate pickle files.
63
- ENV_VERSION = 63
72
+ ENV_VERSION = 64
64
73
 
65
74
  # config status
66
75
  CONFIG_UNSET = -1
@@ -81,61 +90,6 @@ versioning_conditions: dict[str, Literal[False] | Callable[[Node], bool]] = {
81
90
  'text': is_translatable,
82
91
  }
83
92
 
84
- if TYPE_CHECKING:
85
- from collections.abc import MutableMapping
86
- from typing import Literal, overload
87
-
88
- from sphinx.domains.c import CDomain
89
- from sphinx.domains.changeset import ChangeSetDomain
90
- from sphinx.domains.citation import CitationDomain
91
- from sphinx.domains.cpp import CPPDomain
92
- from sphinx.domains.index import IndexDomain
93
- from sphinx.domains.javascript import JavaScriptDomain
94
- from sphinx.domains.math import MathDomain
95
- from sphinx.domains.python import PythonDomain
96
- from sphinx.domains.rst import ReSTDomain
97
- from sphinx.domains.std import StandardDomain
98
- from sphinx.ext.duration import DurationDomain
99
- from sphinx.ext.todo import TodoDomain
100
-
101
- class _DomainsType(MutableMapping[str, Domain]):
102
- @overload
103
- def __getitem__(self, key: Literal["c"]) -> CDomain: ... # NoQA: E704
104
- @overload
105
- def __getitem__(self, key: Literal["cpp"]) -> CPPDomain: ... # NoQA: E704
106
- @overload
107
- def __getitem__(self, key: Literal["changeset"]) -> ChangeSetDomain: ... # NoQA: E704
108
- @overload
109
- def __getitem__(self, key: Literal["citation"]) -> CitationDomain: ... # NoQA: E704
110
- @overload
111
- def __getitem__(self, key: Literal["index"]) -> IndexDomain: ... # NoQA: E704
112
- @overload
113
- def __getitem__(self, key: Literal["js"]) -> JavaScriptDomain: ... # NoQA: E704
114
- @overload
115
- def __getitem__(self, key: Literal["math"]) -> MathDomain: ... # NoQA: E704
116
- @overload
117
- def __getitem__(self, key: Literal["py"]) -> PythonDomain: ... # NoQA: E704
118
- @overload
119
- def __getitem__(self, key: Literal["rst"]) -> ReSTDomain: ... # NoQA: E704
120
- @overload
121
- def __getitem__(self, key: Literal["std"]) -> StandardDomain: ... # NoQA: E704
122
- @overload
123
- def __getitem__(self, key: Literal["duration"]) -> DurationDomain: ... # NoQA: E704
124
- @overload
125
- def __getitem__(self, key: Literal["todo"]) -> TodoDomain: ... # NoQA: E704
126
- @overload
127
- def __getitem__(self, key: str) -> Domain: ... # NoQA: E704
128
- def __getitem__(self, _key: str) -> Domain: raise NotImplementedError # NoQA: E704
129
- def __setitem__( # NoQA: E301,E704
130
- self, key: str, value: Domain,
131
- ) -> NoReturn: raise NotImplementedError
132
- def __delitem__(self, key: str) -> NoReturn: raise NotImplementedError # NoQA: E704
133
- def __iter__(self) -> NoReturn: raise NotImplementedError # NoQA: E704
134
- def __len__(self) -> NoReturn: raise NotImplementedError # NoQA: E704
135
-
136
- else:
137
- _DomainsType = dict
138
-
139
93
 
140
94
  class BuildEnvironment:
141
95
  """
@@ -144,8 +98,6 @@ class BuildEnvironment:
144
98
  transformations to resolve links to them.
145
99
  """
146
100
 
147
- domains: _DomainsType
148
-
149
101
  # --------- ENVIRONMENT INITIALIZATION -------------------------------------
150
102
 
151
103
  def __init__(self, app: Sphinx) -> None:
@@ -163,9 +115,6 @@ class BuildEnvironment:
163
115
  self.versioning_condition: Literal[False] | Callable[[Node], bool] | None = None
164
116
  self.versioning_compare: bool | None = None
165
117
 
166
- # all the registered domains, set by the application
167
- self.domains = _DomainsType()
168
-
169
118
  # the docutils settings for building
170
119
  self.settings: dict[str, Any] = default_settings.copy()
171
120
  self.settings['env'] = self
@@ -242,7 +191,7 @@ class BuildEnvironment:
242
191
  self.dlfiles: DownloadFiles = DownloadFiles()
243
192
 
244
193
  # the original URI for images
245
- self.original_image_uri: dict[str, str] = {}
194
+ self.original_image_uri: dict[_StrPath, str] = {}
246
195
 
247
196
  # temporary data storage while reading a document
248
197
  self.temp_data: dict[str, Any] = {}
@@ -270,6 +219,9 @@ class BuildEnvironment:
270
219
  # objtype index -> (domain, type, objname (localized))
271
220
  self._search_index_objnames: dict[int, tuple[str, str, str]] = {}
272
221
 
222
+ # all the registered domains, set by the application
223
+ self.domains: _DomainsContainer = _DomainsContainer._from_environment(self)
224
+
273
225
  # set up environment
274
226
  self.setup(app)
275
227
 
@@ -277,7 +229,7 @@ class BuildEnvironment:
277
229
  """Obtains serializable data for pickling."""
278
230
  __dict__ = self.__dict__.copy()
279
231
  # clear unpickable attributes
280
- __dict__.update(app=None, domains={}, events=None)
232
+ __dict__.update(app=None, domains=None, events=None)
281
233
  # clear in-memory doctree caches, to reduce memory consumption and
282
234
  # ensure that, upon restoring the state, the most recent pickled files
283
235
  # on the disk are used instead of those from a possibly outdated state
@@ -304,51 +256,83 @@ class BuildEnvironment:
304
256
  self.project = app.project
305
257
  self.version = app.registry.get_envversion(app)
306
258
 
307
- # initialize domains
308
- self.domains = _DomainsType()
309
- for domain in app.registry.create_domains(self):
310
- self.domains[domain.name] = domain
311
-
259
+ # initialise domains
260
+ if self.domains is None:
261
+ # if we are unpickling an environment, we need to recreate the domains
262
+ self.domains = _DomainsContainer._from_environment(self)
312
263
  # setup domains (must do after all initialization)
313
- for domain in self.domains.values():
314
- domain.setup()
264
+ self.domains._setup()
315
265
 
316
- # initialize config
317
- self._update_config(app.config)
266
+ # Initialise config.
267
+ # The old config is self.config, restored from the pickled environment.
268
+ # The new config is app.config, always recreated from ``conf.py``
269
+ self.config_status, self.config_status_extra = self._config_status(
270
+ old_config=self.config, new_config=app.config, verbosity=app.verbosity
271
+ )
272
+ self.config = app.config
318
273
 
319
274
  # initialize settings
320
275
  self._update_settings(app.config)
321
276
 
322
- def _update_config(self, config: Config) -> None:
323
- """Update configurations by new one."""
324
- self.config_status = CONFIG_OK
325
- self.config_status_extra = ''
326
- if self.config is None:
327
- self.config_status = CONFIG_NEW
328
- elif self.config.extensions != config.extensions:
329
- self.config_status = CONFIG_EXTENSIONS_CHANGED
330
- extensions = sorted(
331
- set(self.config.extensions) ^ set(config.extensions))
277
+ @staticmethod
278
+ def _config_status(
279
+ *, old_config: Config | None, new_config: Config, verbosity: int
280
+ ) -> tuple[int, str]:
281
+ """Report the differences between two Config objects.
282
+
283
+ Returns a triple of:
284
+
285
+ 1. The new configuration
286
+ 2. A status code indicating how the configuration has changed.
287
+ 3. A status message indicating what has changed.
288
+ """
289
+ if old_config is None:
290
+ return CONFIG_NEW, ''
291
+
292
+ if old_config.extensions != new_config.extensions:
293
+ old_extensions = set(old_config.extensions)
294
+ new_extensions = set(new_config.extensions)
295
+ extensions = old_extensions ^ new_extensions
332
296
  if len(extensions) == 1:
333
- extension = extensions[0]
297
+ extension = extensions.pop()
334
298
  else:
335
- extension = '%d' % (len(extensions),)
336
- self.config_status_extra = f' ({extension!r})'
337
- else:
338
- # check if a config value was changed that affects how
339
- # doctrees are read
340
- for item in config.filter(frozenset({'env'})):
341
- if self.config[item.name] != item.value:
342
- self.config_status = CONFIG_CHANGED
343
- self.config_status_extra = f' ({item.name!r})'
344
- break
299
+ extension = f'{len(extensions)}'
300
+ return CONFIG_EXTENSIONS_CHANGED, f' ({extension!r})'
301
+
302
+ # Log any changes in configuration keys
303
+ if changed_keys := _differing_config_keys(old_config, new_config):
304
+ changed_num = len(changed_keys)
305
+ if changed_num == 1:
306
+ logger.info(
307
+ __('The configuration has changed (1 option: %r)'),
308
+ next(iter(changed_keys)),
309
+ )
310
+ elif changed_num <= 5 or verbosity >= 1:
311
+ logger.info(
312
+ __('The configuration has changed (%d options: %s)'),
313
+ changed_num,
314
+ ', '.join(map(repr, sorted(changed_keys))),
315
+ )
316
+ else:
317
+ logger.info(
318
+ __('The configuration has changed (%d options: %s, ...)'),
319
+ changed_num,
320
+ ', '.join(map(repr, sorted(changed_keys)[:5])),
321
+ )
322
+
323
+ # check if a config value was changed that affects how doctrees are read
324
+ for item in new_config.filter(frozenset({'env'})):
325
+ if old_config[item.name] != item.value:
326
+ return CONFIG_CHANGED, f' ({item.name!r})'
345
327
 
346
- self.config = config
328
+ return CONFIG_OK, ''
347
329
 
348
330
  def _update_settings(self, config: Config) -> None:
349
331
  """Update settings by new config."""
350
332
  self.settings['input_encoding'] = config.source_encoding
351
- self.settings['trim_footnote_reference_space'] = config.trim_footnote_reference_space
333
+ self.settings['trim_footnote_reference_space'] = (
334
+ config.trim_footnote_reference_space
335
+ )
352
336
  self.settings['language_code'] = config.language
353
337
 
354
338
  # Allow to disable by 3rd party extension (workaround)
@@ -373,9 +357,12 @@ class BuildEnvironment:
373
357
  condition = versioning_conditions[method]
374
358
 
375
359
  if self.versioning_condition not in {None, condition}:
376
- raise SphinxError(__('This environment is incompatible with the '
377
- 'selected builder, please choose another '
378
- 'doctree directory.'))
360
+ msg = __(
361
+ 'This environment is incompatible with the '
362
+ 'selected builder, please choose another '
363
+ 'doctree directory.'
364
+ )
365
+ raise SphinxError(msg)
379
366
  self.versioning_condition = condition
380
367
  self.versioning_compare = compare
381
368
 
@@ -386,25 +373,24 @@ class BuildEnvironment:
386
373
  self.included.pop(docname, None)
387
374
  self.reread_always.discard(docname)
388
375
 
389
- for domain in self.domains.values():
390
- domain.clear_doc(docname)
376
+ self.domains._clear_doc(docname)
391
377
 
392
- def merge_info_from(self, docnames: list[str], other: BuildEnvironment,
393
- app: Sphinx) -> None:
378
+ def merge_info_from(
379
+ self, docnames: Iterable[str], other: BuildEnvironment, app: Sphinx
380
+ ) -> None:
394
381
  """Merge global information gathered about *docnames* while reading them
395
382
  from the *other* environment.
396
383
 
397
384
  This possibly comes from a parallel build process.
398
385
  """
399
- docnames = set(docnames) # type: ignore[assignment]
386
+ docnames = frozenset(docnames)
400
387
  for docname in docnames:
401
388
  self.all_docs[docname] = other.all_docs[docname]
402
389
  self.included[docname] = other.included[docname]
403
390
  if docname in other.reread_always:
404
391
  self.reread_always.add(docname)
405
392
 
406
- for domainname, domain in self.domains.items():
407
- domain.merge_domaindata(docnames, other.domaindata[domainname])
393
+ self.domains._merge_domain_data(docnames, other.domaindata)
408
394
  self.events.emit('env-merge-info', self, docnames, other)
409
395
 
410
396
  def path2doc(self, filename: str | os.PathLike[str]) -> str | None:
@@ -434,12 +420,13 @@ class BuildEnvironment:
434
420
  if filename.startswith(('/', os.sep)):
435
421
  rel_fn = filename[1:]
436
422
  else:
437
- docdir = path.dirname(self.doc2path(docname or self.docname,
438
- base=False))
423
+ docdir = path.dirname(self.doc2path(docname or self.docname, base=False))
439
424
  rel_fn = path.join(docdir, filename)
440
425
 
441
- return (canon_path(path.normpath(rel_fn)),
442
- path.normpath(path.join(self.srcdir, rel_fn)))
426
+ return (
427
+ canon_path(path.normpath(rel_fn)),
428
+ path.normpath(path.join(self.srcdir, rel_fn)),
429
+ )
443
430
 
444
431
  @property
445
432
  def found_docs(self) -> set[str]:
@@ -451,9 +438,11 @@ class BuildEnvironment:
451
438
  self.found_docs.
452
439
  """
453
440
  try:
454
- exclude_paths = (self.config.exclude_patterns +
455
- self.config.templates_path +
456
- builder.get_asset_paths())
441
+ exclude_paths = (
442
+ self.config.exclude_patterns
443
+ + self.config.templates_path
444
+ + builder.get_asset_paths()
445
+ )
457
446
  self.project.discover(exclude_paths, self.config.include_patterns)
458
447
 
459
448
  # Current implementation is applying translated messages in the reading
@@ -464,18 +453,25 @@ class BuildEnvironment:
464
453
  # move i18n process into the writing phase, and remove these lines.
465
454
  if builder.use_message_catalog:
466
455
  # add catalog mo file dependency
467
- repo = CatalogRepository(self.srcdir, self.config.locale_dirs,
468
- self.config.language, self.config.source_encoding)
456
+ repo = CatalogRepository(
457
+ self.srcdir,
458
+ self.config.locale_dirs,
459
+ self.config.language,
460
+ self.config.source_encoding,
461
+ )
469
462
  mo_paths = {c.domain: c.mo_path for c in repo.catalogs}
470
463
  for docname in self.found_docs:
471
464
  domain = docname_to_domain(docname, self.config.gettext_compact)
472
465
  if domain in mo_paths:
473
466
  self.dependencies[docname].add(mo_paths[domain])
474
467
  except OSError as exc:
475
- raise DocumentError(__('Failed to scan documents in %s: %r') %
476
- (self.srcdir, exc)) from exc
468
+ raise DocumentError(
469
+ __('Failed to scan documents in %s: %r') % (self.srcdir, exc)
470
+ ) from exc
477
471
 
478
- def get_outdated_files(self, config_changed: bool) -> tuple[set[str], set[str], set[str]]:
472
+ def get_outdated_files(
473
+ self, config_changed: bool
474
+ ) -> tuple[set[str], set[str], set[str]]:
479
475
  """Return (added, changed, removed) sets."""
480
476
  # clear all files no longer present
481
477
  removed = set(self.all_docs) - self.found_docs
@@ -507,10 +503,12 @@ class BuildEnvironment:
507
503
  mtime = self.all_docs[docname]
508
504
  newmtime = _last_modified_time(self.doc2path(docname))
509
505
  if newmtime > mtime:
510
- logger.debug('[build target] outdated %r: %s -> %s',
511
- docname,
512
- _format_rfc3339_microseconds(mtime),
513
- _format_rfc3339_microseconds(newmtime))
506
+ logger.debug(
507
+ '[build target] outdated %r: %s -> %s',
508
+ docname,
509
+ _format_rfc3339_microseconds(mtime),
510
+ _format_rfc3339_microseconds(newmtime),
511
+ )
514
512
  changed.add(docname)
515
513
  continue
516
514
  # finally, check the mtime of dependencies
@@ -521,7 +519,8 @@ class BuildEnvironment:
521
519
  if not path.isfile(deppath):
522
520
  logger.debug(
523
521
  '[build target] changed %r missing dependency %r',
524
- docname, deppath,
522
+ docname,
523
+ deppath,
525
524
  )
526
525
  changed.add(docname)
527
526
  break
@@ -529,7 +528,8 @@ class BuildEnvironment:
529
528
  if depmtime > mtime:
530
529
  logger.debug(
531
530
  '[build target] outdated %r from dependency %r: %s -> %s',
532
- docname, deppath,
531
+ docname,
532
+ deppath,
533
533
  _format_rfc3339_microseconds(mtime),
534
534
  _format_rfc3339_microseconds(depmtime),
535
535
  )
@@ -557,8 +557,7 @@ class BuildEnvironment:
557
557
  self.temp_data['docname'] = docname
558
558
  # defaults to the global default, but can be re-set in a document
559
559
  self.temp_data['default_role'] = self.config.default_role
560
- self.temp_data['default_domain'] = \
561
- self.domains.get(self.config.primary_domain)
560
+ self.temp_data['default_domain'] = self.domains.get(self.config.primary_domain)
562
561
 
563
562
  # utilities to use while reading a document
564
563
 
@@ -616,7 +615,8 @@ class BuildEnvironment:
616
615
  try:
617
616
  return self.domains[domainname]
618
617
  except KeyError as exc:
619
- raise ExtensionError(__('Domain %r is not registered') % domainname) from exc
618
+ msg = __('Domain %r is not registered') % domainname
619
+ raise ExtensionError(msg) from exc
620
620
 
621
621
  # --------- RESOLVING REFERENCES AND TOCTREES ------------------------------
622
622
 
@@ -663,7 +663,10 @@ class BuildEnvironment:
663
663
  # now, resolve all toctree nodes
664
664
  for toctreenode in doctree.findall(addnodes.toctree):
665
665
  result = toctree_adapters._resolve_toctree(
666
- self, docname, builder, toctreenode,
666
+ self,
667
+ docname,
668
+ builder,
669
+ toctreenode,
667
670
  prune=prune_toctrees,
668
671
  includehidden=includehidden,
669
672
  )
@@ -674,9 +677,17 @@ class BuildEnvironment:
674
677
 
675
678
  return doctree
676
679
 
677
- def resolve_toctree(self, docname: str, builder: Builder, toctree: addnodes.toctree,
678
- prune: bool = True, maxdepth: int = 0, titles_only: bool = False,
679
- collapse: bool = False, includehidden: bool = False) -> Node | None:
680
+ def resolve_toctree(
681
+ self,
682
+ docname: str,
683
+ builder: Builder,
684
+ toctree: addnodes.toctree,
685
+ prune: bool = True,
686
+ maxdepth: int = 0,
687
+ titles_only: bool = False,
688
+ collapse: bool = False,
689
+ includehidden: bool = False,
690
+ ) -> Node | None:
680
691
  """Resolve a *toctree* node into individual bullet lists with titles
681
692
  as items, returning None (if no containing titles are found) or
682
693
  a new node.
@@ -689,7 +700,10 @@ class BuildEnvironment:
689
700
  be collapsed.
690
701
  """
691
702
  return toctree_adapters._resolve_toctree(
692
- self, docname, builder, toctree,
703
+ self,
704
+ docname,
705
+ builder,
706
+ toctree,
693
707
  prune=prune,
694
708
  maxdepth=maxdepth,
695
709
  titles_only=titles_only,
@@ -697,8 +711,9 @@ class BuildEnvironment:
697
711
  includehidden=includehidden,
698
712
  )
699
713
 
700
- def resolve_references(self, doctree: nodes.document, fromdocname: str,
701
- builder: Builder) -> None:
714
+ def resolve_references(
715
+ self, doctree: nodes.document, fromdocname: str, builder: Builder
716
+ ) -> None:
702
717
  self.apply_post_transforms(doctree, fromdocname)
703
718
 
704
719
  def apply_post_transforms(self, doctree: nodes.document, docname: str) -> None:
@@ -723,7 +738,7 @@ class BuildEnvironment:
723
738
 
724
739
  relations = {}
725
740
  docnames = _traverse_toctree(
726
- traversed, None, self.config.root_doc, self.toctree_includes,
741
+ traversed, None, self.config.root_doc, self.toctree_includes
727
742
  )
728
743
  prev_doc = None
729
744
  parent, docname = next(docnames)
@@ -750,15 +765,32 @@ class BuildEnvironment:
750
765
  continue
751
766
  if 'orphan' in self.metadata[docname]:
752
767
  continue
753
- logger.warning(__("document isn't included in any toctree"),
754
- location=docname)
768
+ logger.warning(
769
+ __("document isn't included in any toctree"), location=docname
770
+ )
771
+ # Call _check_toc_parents here rather than in _get_toctree_ancestors()
772
+ # because that method is called multiple times per document and would
773
+ # lead to duplicate warnings.
774
+ _check_toc_parents(self.toctree_includes)
755
775
 
756
776
  # call check-consistency for all extensions
757
- for domain in self.domains.values():
758
- domain.check_consistency()
777
+ self.domains._check_consistency()
759
778
  self.events.emit('env-check-consistency', self)
760
779
 
761
780
 
781
+ def _differing_config_keys(old: Config, new: Config) -> frozenset[str]:
782
+ """Return a set of keys that differ between two config objects."""
783
+ old_vals = {c.name: c.value for c in old}
784
+ new_vals = {c.name: c.value for c in new}
785
+ not_in_both = old_vals.keys() ^ new_vals.keys()
786
+ different_values = {
787
+ key
788
+ for key in old_vals.keys() & new_vals.keys()
789
+ if stable_str(old_vals[key]) != stable_str(new_vals[key])
790
+ }
791
+ return frozenset(not_in_both | different_values)
792
+
793
+
762
794
  def _traverse_toctree(
763
795
  traversed: set[str],
764
796
  parent: str | None,
@@ -766,9 +798,12 @@ def _traverse_toctree(
766
798
  toctree_includes: dict[str, list[str]],
767
799
  ) -> Iterator[tuple[str | None, str]]:
768
800
  if parent == docname:
769
- logger.warning(__('self referenced toctree found. Ignored.'),
770
- location=docname, type='toc',
771
- subtype='circular')
801
+ logger.warning(
802
+ __('self referenced toctree found. Ignored.'),
803
+ location=docname,
804
+ type='toc',
805
+ subtype='circular',
806
+ )
772
807
  return
773
808
 
774
809
  # traverse toctree by pre-order
@@ -777,8 +812,29 @@ def _traverse_toctree(
777
812
 
778
813
  for child in toctree_includes.get(docname, ()):
779
814
  for sub_parent, sub_docname in _traverse_toctree(
780
- traversed, docname, child, toctree_includes,
815
+ traversed, docname, child, toctree_includes
781
816
  ):
782
817
  if sub_docname not in traversed:
783
818
  yield sub_parent, sub_docname
784
819
  traversed.add(sub_docname)
820
+
821
+
822
+ def _check_toc_parents(toctree_includes: dict[str, list[str]]) -> None:
823
+ toc_parents: dict[str, list[str]] = {}
824
+ for parent, children in toctree_includes.items():
825
+ for child in children:
826
+ toc_parents.setdefault(child, []).append(parent)
827
+
828
+ for doc, parents in sorted(toc_parents.items()):
829
+ if len(parents) > 1:
830
+ logger.info(
831
+ __(
832
+ 'document is referenced in multiple toctrees: %s, selecting: %s <- %s'
833
+ ),
834
+ parents,
835
+ max(parents),
836
+ doc,
837
+ location=doc,
838
+ type='toc',
839
+ subtype='multiple_toc_parents',
840
+ )
@@ -1,6 +1,7 @@
1
1
  """Assets adapter for sphinx.environment."""
2
2
 
3
3
  from sphinx.environment import BuildEnvironment
4
+ from sphinx.util._pathlib import _StrPath
4
5
 
5
6
 
6
7
  class ImageAdapter:
@@ -9,7 +10,7 @@ class ImageAdapter:
9
10
 
10
11
  def get_original_image_uri(self, name: str) -> str:
11
12
  """Get the original image URI."""
12
- while name in self.env.original_image_uri:
13
- name = self.env.original_image_uri[name]
13
+ while _StrPath(name) in self.env.original_image_uri:
14
+ name = self.env.original_image_uri[_StrPath(name)]
14
15
 
15
16
  return name