Sphinx 7.3.6__py3-none-any.whl → 7.4.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (357) hide show
  1. sphinx/__init__.py +5 -6
  2. sphinx/_cli/__init__.py +296 -0
  3. sphinx/_cli/util/__init__.py +0 -0
  4. sphinx/_cli/util/colour.py +103 -0
  5. sphinx/_cli/util/errors.py +165 -0
  6. sphinx/application.py +78 -43
  7. sphinx/builders/__init__.py +59 -15
  8. sphinx/builders/_epub_base.py +11 -5
  9. sphinx/builders/changes.py +2 -2
  10. sphinx/builders/epub3.py +2 -2
  11. sphinx/builders/gettext.py +10 -10
  12. sphinx/builders/html/__init__.py +56 -54
  13. sphinx/builders/latex/__init__.py +5 -5
  14. sphinx/builders/latex/constants.py +5 -0
  15. sphinx/builders/linkcheck.py +73 -38
  16. sphinx/builders/texinfo.py +1 -1
  17. sphinx/cmd/build.py +1 -1
  18. sphinx/cmd/quickstart.py +11 -11
  19. sphinx/config.py +57 -38
  20. sphinx/directives/__init__.py +7 -9
  21. sphinx/directives/code.py +12 -15
  22. sphinx/directives/other.py +12 -15
  23. sphinx/directives/patches.py +26 -0
  24. sphinx/domains/__init__.py +1 -1
  25. sphinx/domains/c/__init__.py +5 -5
  26. sphinx/domains/c/_ast.py +436 -12
  27. sphinx/domains/c/_symbol.py +89 -134
  28. sphinx/domains/changeset.py +3 -4
  29. sphinx/domains/cpp/__init__.py +5 -6
  30. sphinx/domains/cpp/_ast.py +822 -25
  31. sphinx/domains/cpp/_symbol.py +3 -0
  32. sphinx/domains/javascript.py +3 -6
  33. sphinx/domains/math.py +3 -2
  34. sphinx/domains/python/__init__.py +45 -6
  35. sphinx/domains/python/_object.py +7 -5
  36. sphinx/domains/rst.py +2 -2
  37. sphinx/domains/std/__init__.py +95 -14
  38. sphinx/environment/__init__.py +35 -15
  39. sphinx/environment/adapters/indexentries.py +71 -24
  40. sphinx/environment/adapters/toctree.py +1 -1
  41. sphinx/environment/collectors/__init__.py +18 -4
  42. sphinx/environment/collectors/asset.py +4 -4
  43. sphinx/environment/collectors/toctree.py +27 -14
  44. sphinx/events.py +7 -6
  45. sphinx/ext/apidoc.py +377 -170
  46. sphinx/ext/autodoc/__init__.py +13 -13
  47. sphinx/ext/autodoc/directive.py +10 -13
  48. sphinx/ext/autodoc/mock.py +10 -7
  49. sphinx/ext/autodoc/preserve_defaults.py +1 -1
  50. sphinx/ext/autodoc/typehints.py +2 -2
  51. sphinx/ext/autosummary/__init__.py +15 -9
  52. sphinx/ext/autosummary/generate.py +270 -154
  53. sphinx/ext/coverage.py +108 -18
  54. sphinx/ext/duration.py +10 -3
  55. sphinx/ext/extlinks.py +3 -2
  56. sphinx/ext/graphviz.py +3 -3
  57. sphinx/ext/ifconfig.py +1 -2
  58. sphinx/ext/imgconverter.py +1 -0
  59. sphinx/ext/imgmath.py +7 -6
  60. sphinx/ext/inheritance_diagram.py +3 -3
  61. sphinx/ext/intersphinx/__init__.py +81 -0
  62. sphinx/ext/intersphinx/__main__.py +10 -0
  63. sphinx/ext/intersphinx/_cli.py +44 -0
  64. sphinx/ext/intersphinx/_load.py +253 -0
  65. sphinx/ext/{intersphinx.py → intersphinx/_resolve.py} +17 -368
  66. sphinx/ext/intersphinx/_shared.py +53 -0
  67. sphinx/ext/mathjax.py +1 -1
  68. sphinx/ext/todo.py +2 -2
  69. sphinx/io.py +2 -6
  70. sphinx/locale/__init__.py +1 -5
  71. sphinx/locale/ar/LC_MESSAGES/sphinx.js +1 -1
  72. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  73. sphinx/locale/ar/LC_MESSAGES/sphinx.po +678 -471
  74. sphinx/locale/bg/LC_MESSAGES/sphinx.js +1 -1
  75. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  76. sphinx/locale/bg/LC_MESSAGES/sphinx.po +684 -476
  77. sphinx/locale/bn/LC_MESSAGES/sphinx.js +1 -1
  78. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  79. sphinx/locale/bn/LC_MESSAGES/sphinx.po +679 -472
  80. sphinx/locale/ca/LC_MESSAGES/sphinx.js +1 -1
  81. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  82. sphinx/locale/ca/LC_MESSAGES/sphinx.po +681 -474
  83. sphinx/locale/cak/LC_MESSAGES/sphinx.js +1 -1
  84. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  85. sphinx/locale/cak/LC_MESSAGES/sphinx.po +678 -471
  86. sphinx/locale/cs/LC_MESSAGES/sphinx.js +1 -1
  87. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  88. sphinx/locale/cs/LC_MESSAGES/sphinx.po +679 -472
  89. sphinx/locale/cy/LC_MESSAGES/sphinx.js +1 -1
  90. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  91. sphinx/locale/cy/LC_MESSAGES/sphinx.po +679 -472
  92. sphinx/locale/da/LC_MESSAGES/sphinx.js +1 -1
  93. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  94. sphinx/locale/da/LC_MESSAGES/sphinx.po +679 -472
  95. sphinx/locale/de/LC_MESSAGES/sphinx.js +1 -1
  96. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  97. sphinx/locale/de/LC_MESSAGES/sphinx.po +679 -472
  98. sphinx/locale/de_DE/LC_MESSAGES/sphinx.js +1 -1
  99. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  100. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +678 -471
  101. sphinx/locale/el/LC_MESSAGES/sphinx.js +1 -1
  102. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  103. sphinx/locale/el/LC_MESSAGES/sphinx.po +701 -494
  104. sphinx/locale/en_DE/LC_MESSAGES/sphinx.js +1 -1
  105. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  106. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +700 -493
  107. sphinx/locale/en_FR/LC_MESSAGES/sphinx.js +1 -1
  108. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  109. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +700 -493
  110. sphinx/locale/en_GB/LC_MESSAGES/sphinx.js +1 -1
  111. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  112. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +701 -494
  113. sphinx/locale/en_HK/LC_MESSAGES/sphinx.js +1 -1
  114. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  115. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +700 -493
  116. sphinx/locale/eo/LC_MESSAGES/sphinx.js +1 -1
  117. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  118. sphinx/locale/eo/LC_MESSAGES/sphinx.po +701 -494
  119. sphinx/locale/es/LC_MESSAGES/sphinx.js +1 -1
  120. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  121. sphinx/locale/es/LC_MESSAGES/sphinx.po +701 -494
  122. sphinx/locale/es_CO/LC_MESSAGES/sphinx.js +1 -1
  123. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  124. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +700 -493
  125. sphinx/locale/et/LC_MESSAGES/sphinx.js +1 -1
  126. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  127. sphinx/locale/et/LC_MESSAGES/sphinx.po +701 -494
  128. sphinx/locale/eu/LC_MESSAGES/sphinx.js +1 -1
  129. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  130. sphinx/locale/eu/LC_MESSAGES/sphinx.po +701 -494
  131. sphinx/locale/fa/LC_MESSAGES/sphinx.js +1 -1
  132. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  133. sphinx/locale/fa/LC_MESSAGES/sphinx.po +701 -494
  134. sphinx/locale/fi/LC_MESSAGES/sphinx.js +1 -1
  135. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  136. sphinx/locale/fi/LC_MESSAGES/sphinx.po +700 -493
  137. sphinx/locale/fr/LC_MESSAGES/sphinx.js +1 -1
  138. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  139. sphinx/locale/fr/LC_MESSAGES/sphinx.po +725 -518
  140. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.js +1 -1
  141. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  142. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +700 -493
  143. sphinx/locale/gl/LC_MESSAGES/sphinx.js +1 -1
  144. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  145. sphinx/locale/gl/LC_MESSAGES/sphinx.po +701 -494
  146. sphinx/locale/he/LC_MESSAGES/sphinx.js +1 -1
  147. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  148. sphinx/locale/he/LC_MESSAGES/sphinx.po +700 -493
  149. sphinx/locale/hi/LC_MESSAGES/sphinx.js +1 -1
  150. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  151. sphinx/locale/hi/LC_MESSAGES/sphinx.po +701 -494
  152. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.js +1 -1
  153. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +700 -493
  155. sphinx/locale/hr/LC_MESSAGES/sphinx.js +1 -1
  156. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  157. sphinx/locale/hr/LC_MESSAGES/sphinx.po +701 -494
  158. sphinx/locale/hu/LC_MESSAGES/sphinx.js +1 -1
  159. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/hu/LC_MESSAGES/sphinx.po +701 -494
  161. sphinx/locale/id/LC_MESSAGES/sphinx.js +1 -1
  162. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  163. sphinx/locale/id/LC_MESSAGES/sphinx.po +701 -494
  164. sphinx/locale/is/LC_MESSAGES/sphinx.js +1 -1
  165. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/is/LC_MESSAGES/sphinx.po +700 -493
  167. sphinx/locale/it/LC_MESSAGES/sphinx.js +1 -1
  168. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  169. sphinx/locale/it/LC_MESSAGES/sphinx.po +708 -500
  170. sphinx/locale/ja/LC_MESSAGES/sphinx.js +1 -1
  171. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/ja/LC_MESSAGES/sphinx.po +701 -494
  173. sphinx/locale/ka/LC_MESSAGES/sphinx.js +1 -1
  174. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  175. sphinx/locale/ka/LC_MESSAGES/sphinx.po +700 -493
  176. sphinx/locale/ko/LC_MESSAGES/sphinx.js +1 -1
  177. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/ko/LC_MESSAGES/sphinx.po +701 -494
  179. sphinx/locale/lt/LC_MESSAGES/sphinx.js +1 -1
  180. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  181. sphinx/locale/lt/LC_MESSAGES/sphinx.po +701 -494
  182. sphinx/locale/lv/LC_MESSAGES/sphinx.js +1 -1
  183. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  184. sphinx/locale/lv/LC_MESSAGES/sphinx.po +701 -494
  185. sphinx/locale/mk/LC_MESSAGES/sphinx.js +1 -1
  186. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  187. sphinx/locale/mk/LC_MESSAGES/sphinx.po +700 -493
  188. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.js +1 -1
  189. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  190. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +701 -494
  191. sphinx/locale/ne/LC_MESSAGES/sphinx.js +1 -1
  192. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  193. sphinx/locale/ne/LC_MESSAGES/sphinx.po +701 -494
  194. sphinx/locale/nl/LC_MESSAGES/sphinx.js +1 -1
  195. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  196. sphinx/locale/nl/LC_MESSAGES/sphinx.po +701 -494
  197. sphinx/locale/pl/LC_MESSAGES/sphinx.js +1 -1
  198. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  199. sphinx/locale/pl/LC_MESSAGES/sphinx.po +701 -494
  200. sphinx/locale/pt/LC_MESSAGES/sphinx.js +1 -1
  201. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  202. sphinx/locale/pt/LC_MESSAGES/sphinx.po +700 -493
  203. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js +1 -1
  204. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  205. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +705 -498
  206. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.js +1 -1
  207. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  208. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +701 -494
  209. sphinx/locale/ro/LC_MESSAGES/sphinx.js +1 -1
  210. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/ro/LC_MESSAGES/sphinx.po +701 -494
  212. sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
  213. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  214. sphinx/locale/ru/LC_MESSAGES/sphinx.po +890 -680
  215. sphinx/locale/si/LC_MESSAGES/sphinx.js +1 -1
  216. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  217. sphinx/locale/si/LC_MESSAGES/sphinx.po +700 -493
  218. sphinx/locale/sk/LC_MESSAGES/sphinx.js +1 -1
  219. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  220. sphinx/locale/sk/LC_MESSAGES/sphinx.po +701 -494
  221. sphinx/locale/sl/LC_MESSAGES/sphinx.js +1 -1
  222. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  223. sphinx/locale/sl/LC_MESSAGES/sphinx.po +701 -494
  224. sphinx/locale/sphinx.pot +702 -494
  225. sphinx/locale/sq/LC_MESSAGES/sphinx.js +1 -1
  226. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  227. sphinx/locale/sq/LC_MESSAGES/sphinx.po +704 -497
  228. sphinx/locale/sr/LC_MESSAGES/sphinx.js +1 -1
  229. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  230. sphinx/locale/sr/LC_MESSAGES/sphinx.po +700 -493
  231. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
  232. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
  233. sphinx/locale/sv/LC_MESSAGES/sphinx.js +1 -1
  234. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  235. sphinx/locale/sv/LC_MESSAGES/sphinx.po +701 -494
  236. sphinx/locale/ta/LC_MESSAGES/sphinx.po +1016 -808
  237. sphinx/locale/te/LC_MESSAGES/sphinx.js +1 -1
  238. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  239. sphinx/locale/te/LC_MESSAGES/sphinx.po +700 -493
  240. sphinx/locale/tr/LC_MESSAGES/sphinx.js +1 -1
  241. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  242. sphinx/locale/tr/LC_MESSAGES/sphinx.po +701 -494
  243. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.js +1 -1
  244. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  245. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +701 -494
  246. sphinx/locale/ur/LC_MESSAGES/sphinx.js +1 -1
  247. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  248. sphinx/locale/ur/LC_MESSAGES/sphinx.po +700 -493
  249. sphinx/locale/vi/LC_MESSAGES/sphinx.js +1 -1
  250. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  251. sphinx/locale/vi/LC_MESSAGES/sphinx.po +701 -494
  252. sphinx/locale/yue/LC_MESSAGES/sphinx.js +1 -1
  253. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  254. sphinx/locale/yue/LC_MESSAGES/sphinx.po +700 -493
  255. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +704 -496
  256. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.js +1 -1
  257. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  258. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +700 -493
  259. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.js +1 -1
  260. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  261. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +729 -522
  262. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.js +1 -1
  263. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  264. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +700 -493
  265. sphinx/roles.py +1 -1
  266. sphinx/search/__init__.py +17 -9
  267. sphinx/templates/quickstart/{root_doc.rst_t → root_doc.rst.jinja} +7 -10
  268. sphinx/testing/fixtures.py +22 -20
  269. sphinx/testing/path.py +6 -2
  270. sphinx/testing/util.py +8 -13
  271. sphinx/texinputs/sphinx.sty +449 -332
  272. sphinx/texinputs/sphinxlatexadmonitions.sty +209 -66
  273. sphinx/texinputs/sphinxlatexliterals.sty +9 -16
  274. sphinx/texinputs/sphinxlatexstyletext.sty +4 -38
  275. sphinx/texinputs/sphinxlatextables.sty +6 -14
  276. sphinx/texinputs/sphinxpackageboxes.sty +15 -42
  277. sphinx/texinputs/sphinxpackagefootnote.sty +4 -3
  278. sphinx/themes/agogo/layout.html +3 -3
  279. sphinx/themes/basic/genindex-single.html +2 -1
  280. sphinx/themes/basic/layout.html +3 -6
  281. sphinx/themes/basic/static/searchtools.js +4 -3
  282. sphinx/themes/haiku/layout.html +4 -4
  283. sphinx/themes/pyramid/layout.html +1 -1
  284. sphinx/themes/scrolls/layout.html +2 -2
  285. sphinx/theming.py +42 -7
  286. sphinx/transforms/__init__.py +34 -20
  287. sphinx/transforms/i18n.py +8 -7
  288. sphinx/transforms/post_transforms/__init__.py +1 -1
  289. sphinx/transforms/post_transforms/images.py +7 -10
  290. sphinx/util/_pathlib.py +2 -2
  291. sphinx/util/cfamily.py +52 -30
  292. sphinx/util/console.py +1 -1
  293. sphinx/util/display.py +16 -11
  294. sphinx/util/docutils.py +88 -40
  295. sphinx/util/fileutil.py +15 -3
  296. sphinx/util/images.py +1 -0
  297. sphinx/util/inspect.py +66 -22
  298. sphinx/util/inventory.py +15 -0
  299. sphinx/util/logging.py +14 -21
  300. sphinx/util/math.py +3 -1
  301. sphinx/util/nodes.py +9 -12
  302. sphinx/util/osutil.py +5 -5
  303. sphinx/util/parsing.py +93 -0
  304. sphinx/util/tags.py +71 -47
  305. sphinx/util/typing.py +261 -143
  306. sphinx/versioning.py +17 -17
  307. sphinx/writers/html5.py +26 -19
  308. sphinx/writers/latex.py +58 -28
  309. sphinx/writers/manpage.py +4 -3
  310. sphinx/writers/texinfo.py +19 -14
  311. {sphinx-7.3.6.dist-info → sphinx-7.4.0.dist-info}/METADATA +21 -20
  312. sphinx-7.4.0.dist-info/RECORD +591 -0
  313. sphinx-7.3.6.dist-info/RECORD +0 -581
  314. /sphinx/templates/apidoc/{module.rst_t → module.rst.jinja} +0 -0
  315. /sphinx/templates/apidoc/{package.rst_t → package.rst.jinja} +0 -0
  316. /sphinx/templates/apidoc/{toc.rst_t → toc.rst.jinja} +0 -0
  317. /sphinx/templates/epub3/{content.opf_t → content.opf.jinja} +0 -0
  318. /sphinx/templates/epub3/{nav.xhtml_t → nav.xhtml.jinja} +0 -0
  319. /sphinx/templates/epub3/{toc.ncx_t → toc.ncx.jinja} +0 -0
  320. /sphinx/templates/gettext/{message.pot_t → message.pot.jinja} +0 -0
  321. /sphinx/templates/imgmath/{preview.tex_t → preview.tex.jinja} +0 -0
  322. /sphinx/templates/imgmath/{template.tex_t → template.tex.jinja} +0 -0
  323. /sphinx/templates/latex/{latex.tex_t → latex.tex.jinja} +0 -0
  324. /sphinx/templates/latex/{longtable.tex_t → longtable.tex.jinja} +0 -0
  325. /sphinx/templates/latex/{sphinxmessages.sty_t → sphinxmessages.sty.jinja} +0 -0
  326. /sphinx/templates/latex/{tabular.tex_t → tabular.tex.jinja} +0 -0
  327. /sphinx/templates/latex/{tabulary.tex_t → tabulary.tex.jinja} +0 -0
  328. /sphinx/templates/quickstart/{Makefile_t → Makefile.jinja} +0 -0
  329. /sphinx/templates/quickstart/{Makefile.new_t → Makefile.new.jinja} +0 -0
  330. /sphinx/templates/quickstart/{conf.py_t → conf.py.jinja} +0 -0
  331. /sphinx/templates/quickstart/{make.bat_t → make.bat.jinja} +0 -0
  332. /sphinx/templates/quickstart/{make.bat.new_t → make.bat.new.jinja} +0 -0
  333. /sphinx/texinputs/{Makefile_t → Makefile.jinja} +0 -0
  334. /sphinx/texinputs/{latexmkjarc_t → latexmkjarc.jinja} +0 -0
  335. /sphinx/texinputs/{latexmkrc_t → latexmkrc.jinja} +0 -0
  336. /sphinx/texinputs/{make.bat_t → make.bat.jinja} +0 -0
  337. /sphinx/texinputs_win/{Makefile_t → Makefile.jinja} +0 -0
  338. /sphinx/themes/agogo/static/{agogo.css_t → agogo.css.jinja} +0 -0
  339. /sphinx/themes/basic/static/{basic.css_t → basic.css.jinja} +0 -0
  340. /sphinx/themes/basic/static/{documentation_options.js_t → documentation_options.js.jinja} +0 -0
  341. /sphinx/themes/basic/static/{language_data.js_t → language_data.js.jinja} +0 -0
  342. /sphinx/themes/bizstyle/static/{bizstyle.css_t → bizstyle.css.jinja} +0 -0
  343. /sphinx/themes/bizstyle/static/{bizstyle.js_t → bizstyle.js.jinja} +0 -0
  344. /sphinx/themes/classic/static/{classic.css_t → classic.css.jinja} +0 -0
  345. /sphinx/themes/classic/static/{sidebar.js_t → sidebar.js.jinja} +0 -0
  346. /sphinx/themes/epub/static/{epub.css_t → epub.css.jinja} +0 -0
  347. /sphinx/themes/haiku/static/{haiku.css_t → haiku.css.jinja} +0 -0
  348. /sphinx/themes/nature/static/{nature.css_t → nature.css.jinja} +0 -0
  349. /sphinx/themes/nonav/static/{nonav.css_t → nonav.css.jinja} +0 -0
  350. /sphinx/themes/pyramid/static/{epub.css_t → epub.css.jinja} +0 -0
  351. /sphinx/themes/pyramid/static/{pyramid.css_t → pyramid.css.jinja} +0 -0
  352. /sphinx/themes/scrolls/static/{scrolls.css_t → scrolls.css.jinja} +0 -0
  353. /sphinx/themes/sphinxdoc/static/{sphinxdoc.css_t → sphinxdoc.css.jinja} +0 -0
  354. /sphinx/themes/traditional/static/{traditional.css_t → traditional.css.jinja} +0 -0
  355. {sphinx-7.3.6.dist-info → sphinx-7.4.0.dist-info}/LICENSE.rst +0 -0
  356. {sphinx-7.3.6.dist-info → sphinx-7.4.0.dist-info}/WHEEL +0 -0
  357. {sphinx-7.3.6.dist-info → sphinx-7.4.0.dist-info}/entry_points.txt +0 -0
@@ -15,14 +15,14 @@
15
15
  <div class="header">
16
16
  {%- if logo_url %}
17
17
  <p class="logo"><a href="{{ pathto(root_doc)|e }}">
18
- <img class="logo" src="{{ logo_url|e }}" alt="Logo"/>
18
+ <img class="logo" src="{{ logo_url|e }}" alt="{{ logo_alt|e }}"/>
19
19
  </a></p>
20
20
  {%- endif %}
21
21
  {%- block headertitle %}
22
22
  <div class="headertitle"><a
23
23
  href="{{ pathto(root_doc)|e }}">{{ shorttitle|e }}</a></div>
24
24
  {%- endblock %}
25
- <div class="rel" role="navigation" aria-label="related navigation">
25
+ <div class="rel" role="navigation" aria-label="Related">
26
26
  {%- for rellink in rellinks|reverse %}
27
27
  <a href="{{ pathto(rellink[0])|e }}" title="{{ rellink[1]|striptags|e }}"
28
28
  {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
@@ -76,7 +76,7 @@
76
76
  <div class="footer-wrapper">
77
77
  <div class="footer">
78
78
  <div class="left">
79
- <div role="navigation" aria-label="related navigation">
79
+ <div role="navigation" aria-label="Related">
80
80
  {%- for rellink in rellinks|reverse %}
81
81
  <a href="{{ pathto(rellink[0])|e }}" title="{{ rellink[1]|striptags|e }}"
82
82
  {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
@@ -30,7 +30,8 @@
30
30
  {% set title = _('Index') %}
31
31
  {% block body %}
32
32
 
33
- <h1 id="index">{% trans key=key %}Index &ndash; {{ key }}{% endtrans %}</h1>
33
+ {# We use ``&#x2013;`` instead of ``&ndash;`` for XHTML compatibility #}
34
+ <h1 id="index">{% trans key=key %}Index &#x2013; {{ key }}{% endtrans %}</h1>
34
35
 
35
36
  <table style="width: 100%" class="indextable"><tr>
36
37
  {%- for column in entries|slice(2) if column %}
@@ -22,7 +22,7 @@
22
22
  {%- endif %}
23
23
 
24
24
  {%- macro relbar() %}
25
- <div class="related" role="navigation" aria-label="related navigation">
25
+ <div class="related" role="navigation" aria-label="Related">
26
26
  <h3>{{ _('Navigation') }}</h3>
27
27
  <ul>
28
28
  {%- for rellink in rellinks %}
@@ -45,12 +45,12 @@
45
45
 
46
46
  {%- macro sidebar() %}
47
47
  {%- if render_sidebar %}
48
- <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
48
+ <div class="sphinxsidebar" role="navigation" aria-label="Main">
49
49
  <div class="sphinxsidebarwrapper">
50
50
  {%- block sidebarlogo %}
51
51
  {%- if logo_url %}
52
52
  <p class="logo"><a href="{{ pathto(root_doc)|e }}">
53
- <img class="logo" src="{{ logo_url|e }}" alt="Logo"/>
53
+ <img class="logo" src="{{ logo_url|e }}" alt="{{ logo_alt|e }}"/>
54
54
  </a></p>
55
55
  {%- endif %}
56
56
  {%- endblock %}
@@ -70,9 +70,6 @@
70
70
  {%- block sidebarsourcelink %}
71
71
  {%- include "sourcelink.html" %}
72
72
  {%- endblock %}
73
- {%- if customsidebar %}
74
- {%- include customsidebar %}
75
- {%- endif %}
76
73
  {%- block sidebarsearch %}
77
74
  {%- include "searchbox.html" %}
78
75
  {%- endblock %}
@@ -178,7 +178,7 @@ const Search = {
178
178
 
179
179
  htmlToText: (htmlString, anchor) => {
180
180
  const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
181
- for (const removalQuery of [".headerlinks", "script", "style"]) {
181
+ for (const removalQuery of [".headerlink", "script", "style"]) {
182
182
  htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() });
183
183
  }
184
184
  if (anchor) {
@@ -328,13 +328,14 @@ const Search = {
328
328
  for (const [title, foundTitles] of Object.entries(allTitles)) {
329
329
  if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) {
330
330
  for (const [file, id] of foundTitles) {
331
- let score = Math.round(100 * queryLower.length / title.length)
331
+ const score = Math.round(Scorer.title * queryLower.length / title.length);
332
+ const boost = titles[file] === title ? 1 : 0; // add a boost for document titles
332
333
  normalResults.push([
333
334
  docNames[file],
334
335
  titles[file] !== title ? `${titles[file]} > ${title}` : title,
335
336
  id !== null ? "#" + id : "",
336
337
  null,
337
- score,
338
+ score + boost,
338
339
  filenames[file],
339
340
  ]);
340
341
  }
@@ -36,11 +36,11 @@
36
36
  {%- block haikuheader %}
37
37
  {%- if theme_full_logo != "false" %}
38
38
  <a href="{{ pathto(root_doc)|e }}">
39
- <img class="logo" src="{{ logo_url|e }}" alt="Logo"/>
39
+ <img class="logo" src="{{ logo_url|e }}" alt="{{ logo_alt|e }}"/>
40
40
  </a>
41
41
  {%- else %}
42
42
  {%- if logo -%}
43
- <img class="rightlogo" src="{{ logo_url|e }}" alt="Logo"/>
43
+ <img class="rightlogo" src="{{ logo_url|e }}" alt="{{ logo_alt|e }}"/>
44
44
  {%- endif -%}
45
45
  <h1 class="heading"><a href="{{ pathto(root_doc)|e }}">
46
46
  <span>{{ shorttitle|e }}</span></a></h1>
@@ -48,7 +48,7 @@
48
48
  {%- endif %}
49
49
  {%- endblock %}
50
50
  </div>
51
- <div class="topnav" role="navigation" aria-label="top navigation">
51
+ <div class="topnav" role="navigation" aria-label="Top">
52
52
  {{ nav() }}
53
53
  </div>
54
54
  <div class="content" role="main">
@@ -60,7 +60,7 @@
60
60
  {%- endif %}#}
61
61
  {% block body %}{% endblock %}
62
62
  </div>
63
- <div class="bottomnav" role="navigation" aria-label="bottom navigation">
63
+ <div class="bottomnav" role="navigation" aria-label="Bottom">
64
64
  {{ nav() }}
65
65
  </div>
66
66
  {% endblock %}
@@ -13,7 +13,7 @@
13
13
  <div class="header" role="banner">
14
14
  <div class="logo">
15
15
  <a href="{{ pathto(root_doc)|e }}">
16
- <img class="logo" src="{{ logo_url|e }}" alt="Logo"/>
16
+ <img class="logo" src="{{ logo_url|e }}" alt="{{ logo_alt|e }}"/>
17
17
  </a>
18
18
  </div>
19
19
  </div>
@@ -26,7 +26,7 @@
26
26
  <h1 class="heading"><a href="{{ pathto(root_doc)|e }}"
27
27
  title="back to the documentation overview"><span>{{ title|striptags|e }}</span></a></h1>
28
28
  </div>
29
- <div class="relnav" role="navigation" aria-label="related navigation">
29
+ <div class="relnav" role="navigation" aria-label="Related">
30
30
  {%- if prev %}
31
31
  <a href="{{ prev.link|e }}">&laquo; {{ prev.title }}</a> |
32
32
  {%- endif %}
@@ -37,7 +37,7 @@
37
37
  </div>
38
38
  <div id="contentwrapper">
39
39
  {%- if display_toc %}
40
- <div id="toc" role="navigation" aria-label="table of contents navigation">
40
+ <div id="toc" role="navigation" aria-label="Table of contents">
41
41
  <h3>{{ _('Table of Contents') }}</h3>
42
42
  {{ toc }}
43
43
  </div>
sphinx/theming.py CHANGED
@@ -32,6 +32,7 @@ else:
32
32
  from importlib_metadata import entry_points
33
33
 
34
34
  if TYPE_CHECKING:
35
+ from collections.abc import Callable
35
36
  from typing import TypedDict
36
37
 
37
38
  from typing_extensions import Required
@@ -120,7 +121,17 @@ class Theme:
120
121
  elif section == 'options':
121
122
  value = self._options.get(name, default)
122
123
  else:
123
- value = _NO_DEFAULT
124
+ # https://github.com/sphinx-doc/sphinx/issues/12305
125
+ # For backwards compatibility when attempting to read a value
126
+ # from an unsupported configuration section.
127
+ # xref: RemovedInSphinx80Warning
128
+ msg = __(
129
+ 'Theme configuration sections other than [theme] and [options] '
130
+ 'are not supported, returning the default value instead '
131
+ '(tried to get a value from %r)'
132
+ )
133
+ logger.info(msg, section)
134
+ value = default
124
135
  if value is _NO_DEFAULT:
125
136
  msg = __('setting %s.%s occurs in none of the searched theme configs') % (
126
137
  section,
@@ -137,7 +148,7 @@ class Theme:
137
148
  options = self._options.copy()
138
149
  for option, value in overrides.items():
139
150
  if option not in options:
140
- logger.warning(__('unsupported theme option %r given') % option)
151
+ logger.warning(__('unsupported theme option %r given'), option)
141
152
  else:
142
153
  options[option] = value
143
154
 
@@ -156,6 +167,7 @@ class HTMLThemeFactory:
156
167
  def __init__(self, app: Sphinx) -> None:
157
168
  self._app = app
158
169
  self._themes = app.registry.html_themes
170
+ self._entry_point_themes: dict[str, Callable[[], None]] = {}
159
171
  self._load_builtin_themes()
160
172
  if getattr(app.config, 'html_theme_path', None):
161
173
  self._load_additional_themes(app.config.html_theme_path)
@@ -167,7 +179,7 @@ class HTMLThemeFactory:
167
179
  for name, theme in themes.items():
168
180
  self._themes[name] = theme
169
181
 
170
- def _load_additional_themes(self, theme_paths: str) -> None:
182
+ def _load_additional_themes(self, theme_paths: list[str]) -> None:
171
183
  """Load additional themes placed at specified directories."""
172
184
  for theme_path in theme_paths:
173
185
  abs_theme_path = path.abspath(path.join(self._app.confdir, theme_path))
@@ -183,8 +195,16 @@ class HTMLThemeFactory:
183
195
  for entry_point in entry_points(group='sphinx.html_themes'):
184
196
  if entry_point.name in self._themes:
185
197
  continue # don't overwrite loaded themes
186
- self._app.registry.load_extension(self._app, entry_point.module)
187
- _config_post_init(self._app, self._app.config)
198
+
199
+ def _load_theme_closure(
200
+ # bind variables in the function definition
201
+ app: Sphinx = self._app,
202
+ theme_module: str = entry_point.module,
203
+ ) -> None:
204
+ app.setup_extension(theme_module)
205
+ _config_post_init(app, app.config)
206
+
207
+ self._entry_point_themes[entry_point.name] = _load_theme_closure
188
208
 
189
209
  @staticmethod
190
210
  def _find_themes(theme_path: str) -> dict[str, str]:
@@ -217,10 +237,18 @@ class HTMLThemeFactory:
217
237
 
218
238
  def create(self, name: str) -> Theme:
219
239
  """Create an instance of theme."""
240
+ if name in self._entry_point_themes:
241
+ # Load a deferred theme from an entry point
242
+ entry_point_loader = self._entry_point_themes[name]
243
+ entry_point_loader()
220
244
  if name not in self._themes:
221
245
  raise ThemeError(__('no theme named %r found (missing theme.toml?)') % name)
222
246
 
223
- themes, theme_dirs, tmp_dirs = _load_theme_with_ancestors(self._themes, name)
247
+ themes, theme_dirs, tmp_dirs = _load_theme_with_ancestors(
248
+ name,
249
+ self._themes,
250
+ self._entry_point_themes,
251
+ )
224
252
  return Theme(name, configs=themes, paths=theme_dirs, tmp_dirs=tmp_dirs)
225
253
 
226
254
 
@@ -235,7 +263,10 @@ def _is_archived_theme(filename: str, /) -> bool:
235
263
 
236
264
 
237
265
  def _load_theme_with_ancestors(
238
- theme_paths: dict[str, str], name: str, /
266
+ name: str,
267
+ theme_paths: dict[str, str],
268
+ entry_point_themes: dict[str, Callable[[], None]],
269
+ /,
239
270
  ) -> tuple[dict[str, _ConfigFile], list[str], list[str]]:
240
271
  themes: dict[str, _ConfigFile] = {}
241
272
  theme_dirs: list[str] = []
@@ -253,6 +284,10 @@ def _load_theme_with_ancestors(
253
284
  if inherit in themes:
254
285
  msg = __('The %r theme has circular inheritance') % name
255
286
  raise ThemeError(msg)
287
+ if inherit in entry_point_themes and inherit not in theme_paths:
288
+ # Load a deferred theme from an entry point
289
+ entry_point_loader = entry_point_themes[inherit]
290
+ entry_point_loader()
256
291
  if inherit not in theme_paths:
257
292
  msg = __(
258
293
  'The %r theme inherits from %r, which is not a loaded theme. '
@@ -22,8 +22,10 @@ from sphinx.util.nodes import apply_source_workaround, is_smartquotable
22
22
 
23
23
  if TYPE_CHECKING:
24
24
  from collections.abc import Iterator
25
+ from typing import Literal
25
26
 
26
27
  from docutils.nodes import Node, Text
28
+ from typing_extensions import TypeAlias, TypeIs
27
29
 
28
30
  from sphinx.application import Sphinx
29
31
  from sphinx.config import Config
@@ -31,15 +33,22 @@ if TYPE_CHECKING:
31
33
  from sphinx.environment import BuildEnvironment
32
34
  from sphinx.util.typing import ExtensionMetadata
33
35
 
36
+ _DEFAULT_SUBSTITUTION_NAMES: TypeAlias = Literal[
37
+ 'version',
38
+ 'release',
39
+ 'today',
40
+ 'translation progress',
41
+ ]
42
+
34
43
 
35
44
  logger = logging.getLogger(__name__)
36
45
 
37
- default_substitutions = {
46
+ _DEFAULT_SUBSTITUTIONS = frozenset({
38
47
  'version',
39
48
  'release',
40
49
  'today',
41
50
  'translation progress',
42
- }
51
+ })
43
52
 
44
53
 
45
54
  class SphinxTransform(Transform):
@@ -105,20 +114,25 @@ class DefaultSubstitutions(SphinxTransform):
105
114
 
106
115
  def apply(self, **kwargs: Any) -> None:
107
116
  # only handle those not otherwise defined in the document
108
- to_handle = default_substitutions - set(self.document.substitution_defs)
117
+ to_handle = _DEFAULT_SUBSTITUTIONS - set(self.document.substitution_defs)
109
118
  for ref in self.document.findall(nodes.substitution_reference):
110
- refname = ref['refname']
111
- if refname in to_handle:
112
- if refname == 'translation progress':
113
- # special handling: calculate translation progress
114
- text = _calculate_translation_progress(self.document)
115
- else:
116
- text = self.config[refname]
117
- if refname == 'today' and not text:
118
- # special handling: can also specify a strftime format
119
- text = format_date(self.config.today_fmt or _('%b %d, %Y'),
120
- language=self.config.language)
121
- ref.replace_self(nodes.Text(text))
119
+ if (name := ref['refname']) in to_handle:
120
+ ref.replace_self(self._handle_default_substitution(name))
121
+
122
+ def _handle_default_substitution(self, name: _DEFAULT_SUBSTITUTION_NAMES) -> nodes.Text:
123
+ if name == 'translation progress':
124
+ # special handling: calculate translation progress
125
+ return nodes.Text(_calculate_translation_progress(self.document))
126
+ if name == 'today':
127
+ if text := self.config.today:
128
+ return nodes.Text(text)
129
+ # special handling: can also specify a strftime format
130
+ return nodes.Text(format_date(
131
+ self.config.today_fmt or _('%b %d, %Y'),
132
+ language=self.config.language,
133
+ ))
134
+ # config.version and config.release
135
+ return nodes.Text(getattr(self.config, name))
122
136
 
123
137
 
124
138
  def _calculate_translation_progress(document: nodes.document) -> str:
@@ -263,15 +277,15 @@ class ExtraTranslatableNodes(SphinxTransform):
263
277
  default_priority = 10
264
278
 
265
279
  def apply(self, **kwargs: Any) -> None:
266
- targets = self.config.gettext_additional_targets
267
- target_nodes = [v for k, v in TRANSLATABLE_NODES.items() if k in targets]
280
+ targets = frozenset(self.config.gettext_additional_targets)
281
+ target_nodes = tuple(v for k, v in TRANSLATABLE_NODES.items() if k in targets)
268
282
  if not target_nodes:
269
283
  return
270
284
 
271
- def is_translatable_node(node: Node) -> bool:
272
- return isinstance(node, tuple(target_nodes))
285
+ def is_translatable_node(node: Node) -> TypeIs[nodes.Element]:
286
+ return isinstance(node, target_nodes)
273
287
 
274
- for node in self.document.findall(is_translatable_node): # type: nodes.Element
288
+ for node in self.document.findall(is_translatable_node):
275
289
  node['translatable'] = True
276
290
 
277
291
 
sphinx/transforms/i18n.py CHANGED
@@ -64,7 +64,7 @@ def publish_msgstr(app: Sphinx, source: str, source_path: str, source_line: int,
64
64
  try:
65
65
  # clear rst_prolog temporarily
66
66
  rst_prolog = config.rst_prolog
67
- config.rst_prolog = None # type: ignore[attr-defined]
67
+ config.rst_prolog = None
68
68
 
69
69
  from sphinx.io import SphinxI18nReader
70
70
  reader = SphinxI18nReader()
@@ -81,7 +81,7 @@ def publish_msgstr(app: Sphinx, source: str, source_path: str, source_line: int,
81
81
  return doc[0]
82
82
  return doc
83
83
  finally:
84
- config.rst_prolog = rst_prolog # type: ignore[attr-defined]
84
+ config.rst_prolog = rst_prolog
85
85
 
86
86
 
87
87
  def parse_noqa(source: str) -> tuple[str, bool]:
@@ -364,9 +364,9 @@ class Locale(SphinxTransform):
364
364
  for node, msg in extract_messages(self.document):
365
365
  msgstr = merged.get(msg, '')
366
366
 
367
- # There is no point in having #noqa on literal blocks because
367
+ # There is no point in having noqa on literal blocks because
368
368
  # they cannot contain references. Recognizing it would just
369
- # completely prevent escaping the #noqa. Outside of literal
369
+ # completely prevent escaping the noqa. Outside of literal
370
370
  # blocks, one can always write \#noqa.
371
371
  if not isinstance(node, LITERAL_TYPE_NODES):
372
372
  msgstr, _ = parse_noqa(msgstr)
@@ -406,12 +406,13 @@ class Locale(SphinxTransform):
406
406
  # glossary terms update refid
407
407
  if isinstance(node, nodes.term):
408
408
  for _id in node['ids']:
409
- parts = split_term_classifiers(msgstr)
409
+ term, first_classifier = split_term_classifiers(msgstr)
410
410
  patch = publish_msgstr(
411
- self.app, parts[0] or '', source, node.line, self.config, settings, # type: ignore[arg-type]
411
+ self.app, term or '', source, node.line, self.config, settings, # type: ignore[arg-type]
412
412
  )
413
413
  updater.patch = make_glossary_term(
414
- self.env, patch, parts[1] or '', source, node.line, _id, self.document, # type: ignore[arg-type]
414
+ self.env, patch, first_classifier,
415
+ source, node.line, _id, self.document, # type: ignore[arg-type]
415
416
  )
416
417
  processed = True
417
418
 
@@ -98,7 +98,7 @@ class ReferencesResolver(SphinxPostTransform):
98
98
  node, contnode,
99
99
  allowed_exceptions=(NoUri,))
100
100
  # still not found? warn if node wishes to be warned about or
101
- # we are in nit-picky mode
101
+ # we are in nitpicky mode
102
102
  if newnode is None:
103
103
  self.warn_missing_reference(refdoc, typ, target, node, domain)
104
104
  except NoUri:
@@ -80,8 +80,8 @@ class ImageDownloader(BaseImageConverter):
80
80
  _tls_info=(config.tls_verify, config.tls_cacerts),
81
81
  )
82
82
  if r.status_code >= 400:
83
- logger.warning(__('Could not fetch remote image: %s [%d]') %
84
- (node['uri'], r.status_code))
83
+ logger.warning(__('Could not fetch remote image: %s [%d]'),
84
+ node['uri'], r.status_code)
85
85
  else:
86
86
  self.app.env.original_image_uri[path] = node['uri']
87
87
 
@@ -108,17 +108,15 @@ class ImageDownloader(BaseImageConverter):
108
108
  node['uri'] = path
109
109
  self.app.env.images.add_file(self.env.docname, path)
110
110
  except Exception as exc:
111
- logger.warning(__('Could not fetch remote image: %s [%s]') % (node['uri'], exc))
111
+ logger.warning(__('Could not fetch remote image: %s [%s]'), node['uri'], exc)
112
112
 
113
113
 
114
114
  class DataURIExtractor(BaseImageConverter):
115
115
  default_priority = 150
116
116
 
117
117
  def match(self, node: nodes.image) -> bool:
118
- if not self.app.builder.supported_remote_images:
119
- return False
120
118
  if self.app.builder.supported_data_uri_images is True:
121
- return False
119
+ return False # do not transform the image; data URIs are valid in the build output
122
120
  return node['uri'].startswith('data:')
123
121
 
124
122
  def handle(self, node: nodes.image) -> None:
@@ -192,9 +190,6 @@ class ImageConverter(BaseImageConverter):
192
190
  #: ]
193
191
  conversion_rules: list[tuple[str, str]] = []
194
192
 
195
- def __init__(self, *args: Any, **kwargs: Any) -> None:
196
- super().__init__(*args, **kwargs)
197
-
198
193
  def match(self, node: nodes.image) -> bool:
199
194
  if not self.app.builder.supported_image_types:
200
195
  return False
@@ -232,10 +227,12 @@ class ImageConverter(BaseImageConverter):
232
227
  raise NotImplementedError
233
228
 
234
229
  def guess_mimetypes(self, node: nodes.image) -> list[str]:
230
+ # The special key ? is set for nonlocal URIs.
235
231
  if '?' in node['candidates']:
236
232
  return []
237
233
  elif '*' in node['candidates']:
238
- guessed = guess_mimetype(node['uri'])
234
+ path = os.path.join(self.app.srcdir, node['uri'])
235
+ guessed = guess_mimetype(path)
239
236
  return [guessed] if guessed is not None else []
240
237
  else:
241
238
  return node['candidates'].keys()
sphinx/util/_pathlib.py CHANGED
@@ -28,7 +28,7 @@ if sys.platform == 'win32':
28
28
  # replace exists in both Path and str;
29
29
  # in Path it makes filesystem changes, so we use the safer str version
30
30
  warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
31
- return self.__str__().replace(old, new, count)
31
+ return self.__str__().replace(old, new, count) # NoQA: PLC2801
32
32
 
33
33
  def __getattr__(self, item: str) -> Any:
34
34
  if item in _STR_METHODS:
@@ -77,7 +77,7 @@ else:
77
77
  # replace exists in both Path and str;
78
78
  # in Path it makes filesystem changes, so we use the safer str version
79
79
  warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2)
80
- return self.__str__().replace(old, new, count)
80
+ return self.__str__().replace(old, new, count) # NoQA: PLC2801
81
81
 
82
82
  def __getattr__(self, item: str) -> Any:
83
83
  if item in _STR_METHODS:
sphinx/util/cfamily.py CHANGED
@@ -90,17 +90,11 @@ class NoOldIdError(Exception):
90
90
  class ASTBaseBase:
91
91
  def __eq__(self, other: object) -> bool:
92
92
  if type(self) is not type(other):
93
- return False
93
+ return NotImplemented
94
94
  try:
95
- for key, value in self.__dict__.items():
96
- if value != getattr(other, key):
97
- return False
95
+ return self.__dict__ == other.__dict__
98
96
  except AttributeError:
99
97
  return False
100
- return True
101
-
102
- # Defining __hash__ = None is not strictly needed when __eq__ is defined.
103
- __hash__ = None # type: ignore[assignment]
104
98
 
105
99
  def clone(self) -> Any:
106
100
  return deepcopy(self)
@@ -115,7 +109,7 @@ class ASTBaseBase:
115
109
  return self._stringify(lambda ast: ast.get_display_string())
116
110
 
117
111
  def __repr__(self) -> str:
118
- return '<%s>' % self.__class__.__name__
112
+ return f'<{self.__class__.__name__}: {self._stringify(repr)}>'
119
113
 
120
114
 
121
115
  ################################################################################
@@ -131,8 +125,16 @@ class ASTCPPAttribute(ASTAttribute):
131
125
  def __init__(self, arg: str) -> None:
132
126
  self.arg = arg
133
127
 
128
+ def __eq__(self, other: object) -> bool:
129
+ if not isinstance(other, ASTCPPAttribute):
130
+ return NotImplemented
131
+ return self.arg == other.arg
132
+
133
+ def __hash__(self) -> int:
134
+ return hash(self.arg)
135
+
134
136
  def _stringify(self, transform: StringifyTransform) -> str:
135
- return "[[" + self.arg + "]]"
137
+ return f"[[{self.arg}]]"
136
138
 
137
139
  def describe_signature(self, signode: TextElement) -> None:
138
140
  signode.append(addnodes.desc_sig_punctuation('[[', '[['))
@@ -146,35 +148,37 @@ class ASTGnuAttribute(ASTBaseBase):
146
148
  self.args = args
147
149
 
148
150
  def __eq__(self, other: object) -> bool:
149
- if type(other) is not ASTGnuAttribute:
151
+ if not isinstance(other, ASTGnuAttribute):
150
152
  return NotImplemented
151
153
  return self.name == other.name and self.args == other.args
152
154
 
155
+ def __hash__(self) -> int:
156
+ return hash((self.name, self.args))
157
+
153
158
  def _stringify(self, transform: StringifyTransform) -> str:
154
- res = [self.name]
155
159
  if self.args:
156
- res.append(transform(self.args))
157
- return ''.join(res)
160
+ return self.name + transform(self.args)
161
+ return self.name
158
162
 
159
163
 
160
164
  class ASTGnuAttributeList(ASTAttribute):
161
165
  def __init__(self, attrs: list[ASTGnuAttribute]) -> None:
162
166
  self.attrs = attrs
163
167
 
168
+ def __eq__(self, other: object) -> bool:
169
+ if not isinstance(other, ASTGnuAttributeList):
170
+ return NotImplemented
171
+ return self.attrs == other.attrs
172
+
173
+ def __hash__(self) -> int:
174
+ return hash(self.attrs)
175
+
164
176
  def _stringify(self, transform: StringifyTransform) -> str:
165
- res = ['__attribute__((']
166
- first = True
167
- for attr in self.attrs:
168
- if not first:
169
- res.append(', ')
170
- first = False
171
- res.append(transform(attr))
172
- res.append('))')
173
- return ''.join(res)
177
+ attrs = ', '.join(map(transform, self.attrs))
178
+ return f'__attribute__(({attrs}))'
174
179
 
175
180
  def describe_signature(self, signode: TextElement) -> None:
176
- txt = str(self)
177
- signode.append(nodes.Text(txt))
181
+ signode.append(nodes.Text(str(self)))
178
182
 
179
183
 
180
184
  class ASTIdAttribute(ASTAttribute):
@@ -183,6 +187,14 @@ class ASTIdAttribute(ASTAttribute):
183
187
  def __init__(self, id: str) -> None:
184
188
  self.id = id
185
189
 
190
+ def __eq__(self, other: object) -> bool:
191
+ if not isinstance(other, ASTIdAttribute):
192
+ return NotImplemented
193
+ return self.id == other.id
194
+
195
+ def __hash__(self) -> int:
196
+ return hash(self.id)
197
+
186
198
  def _stringify(self, transform: StringifyTransform) -> str:
187
199
  return self.id
188
200
 
@@ -197,12 +209,19 @@ class ASTParenAttribute(ASTAttribute):
197
209
  self.id = id
198
210
  self.arg = arg
199
211
 
212
+ def __eq__(self, other: object) -> bool:
213
+ if not isinstance(other, ASTParenAttribute):
214
+ return NotImplemented
215
+ return self.id == other.id and self.arg == other.arg
216
+
217
+ def __hash__(self) -> int:
218
+ return hash((self.id, self.arg))
219
+
200
220
  def _stringify(self, transform: StringifyTransform) -> str:
201
- return self.id + '(' + self.arg + ')'
221
+ return f'{self.id}({self.arg})'
202
222
 
203
223
  def describe_signature(self, signode: TextElement) -> None:
204
- txt = str(self)
205
- signode.append(nodes.Text(txt))
224
+ signode.append(nodes.Text(str(self)))
206
225
 
207
226
 
208
227
  class ASTAttributeList(ASTBaseBase):
@@ -210,10 +229,13 @@ class ASTAttributeList(ASTBaseBase):
210
229
  self.attrs = attrs
211
230
 
212
231
  def __eq__(self, other: object) -> bool:
213
- if type(other) is not ASTAttributeList:
232
+ if not isinstance(other, ASTAttributeList):
214
233
  return NotImplemented
215
234
  return self.attrs == other.attrs
216
235
 
236
+ def __hash__(self) -> int:
237
+ return hash(self.attrs)
238
+
217
239
  def __len__(self) -> int:
218
240
  return len(self.attrs)
219
241
 
@@ -221,7 +243,7 @@ class ASTAttributeList(ASTBaseBase):
221
243
  return ASTAttributeList(self.attrs + other.attrs)
222
244
 
223
245
  def _stringify(self, transform: StringifyTransform) -> str:
224
- return ' '.join(transform(attr) for attr in self.attrs)
246
+ return ' '.join(map(transform, self.attrs))
225
247
 
226
248
  def describe_signature(self, signode: TextElement) -> None:
227
249
  if len(self.attrs) == 0: