Sphinx 7.2.6__py3-none-any.whl → 7.3.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 (388) hide show
  1. sphinx/__init__.py +8 -9
  2. sphinx/addnodes.py +31 -28
  3. sphinx/application.py +9 -15
  4. sphinx/builders/__init__.py +5 -6
  5. sphinx/builders/_epub_base.py +17 -9
  6. sphinx/builders/changes.py +10 -5
  7. sphinx/builders/dirhtml.py +4 -2
  8. sphinx/builders/dummy.py +3 -2
  9. sphinx/builders/epub3.py +5 -3
  10. sphinx/builders/gettext.py +24 -7
  11. sphinx/builders/html/__init__.py +88 -96
  12. sphinx/builders/html/_assets.py +16 -16
  13. sphinx/builders/html/transforms.py +4 -2
  14. sphinx/builders/latex/__init__.py +40 -33
  15. sphinx/builders/latex/nodes.py +6 -2
  16. sphinx/builders/latex/transforms.py +17 -8
  17. sphinx/builders/latex/util.py +1 -1
  18. sphinx/builders/linkcheck.py +86 -27
  19. sphinx/builders/manpage.py +8 -6
  20. sphinx/builders/singlehtml.py +5 -4
  21. sphinx/builders/texinfo.py +18 -14
  22. sphinx/builders/text.py +3 -2
  23. sphinx/builders/xml.py +5 -2
  24. sphinx/cmd/build.py +119 -76
  25. sphinx/cmd/make_mode.py +4 -9
  26. sphinx/cmd/quickstart.py +13 -16
  27. sphinx/config.py +432 -250
  28. sphinx/deprecation.py +23 -13
  29. sphinx/directives/__init__.py +8 -8
  30. sphinx/directives/code.py +7 -7
  31. sphinx/directives/other.py +23 -13
  32. sphinx/directives/patches.py +7 -6
  33. sphinx/domains/__init__.py +2 -2
  34. sphinx/domains/c/__init__.py +796 -0
  35. sphinx/domains/c/_ast.py +1421 -0
  36. sphinx/domains/c/_ids.py +65 -0
  37. sphinx/domains/c/_parser.py +1048 -0
  38. sphinx/domains/c/_symbol.py +700 -0
  39. sphinx/domains/changeset.py +11 -7
  40. sphinx/domains/citation.py +5 -2
  41. sphinx/domains/cpp/__init__.py +1089 -0
  42. sphinx/domains/cpp/_ast.py +3635 -0
  43. sphinx/domains/cpp/_ids.py +537 -0
  44. sphinx/domains/cpp/_parser.py +2117 -0
  45. sphinx/domains/cpp/_symbol.py +1092 -0
  46. sphinx/domains/index.py +6 -4
  47. sphinx/domains/javascript.py +16 -13
  48. sphinx/domains/math.py +9 -4
  49. sphinx/domains/python/__init__.py +890 -0
  50. sphinx/domains/python/_annotations.py +507 -0
  51. sphinx/domains/python/_object.py +426 -0
  52. sphinx/domains/rst.py +12 -7
  53. sphinx/domains/{std.py → std/__init__.py} +19 -16
  54. sphinx/environment/__init__.py +21 -19
  55. sphinx/environment/adapters/indexentries.py +2 -2
  56. sphinx/environment/adapters/toctree.py +10 -9
  57. sphinx/environment/collectors/__init__.py +6 -3
  58. sphinx/environment/collectors/asset.py +4 -3
  59. sphinx/environment/collectors/dependencies.py +3 -2
  60. sphinx/environment/collectors/metadata.py +6 -5
  61. sphinx/environment/collectors/title.py +3 -2
  62. sphinx/environment/collectors/toctree.py +5 -4
  63. sphinx/errors.py +13 -2
  64. sphinx/events.py +14 -9
  65. sphinx/ext/apidoc.py +9 -11
  66. sphinx/ext/autodoc/__init__.py +105 -71
  67. sphinx/ext/autodoc/directive.py +7 -6
  68. sphinx/ext/autodoc/importer.py +102 -36
  69. sphinx/ext/autodoc/mock.py +7 -5
  70. sphinx/ext/autodoc/preserve_defaults.py +4 -3
  71. sphinx/ext/autodoc/type_comment.py +2 -1
  72. sphinx/ext/autodoc/typehints.py +5 -4
  73. sphinx/ext/autosectionlabel.py +3 -2
  74. sphinx/ext/autosummary/__init__.py +21 -17
  75. sphinx/ext/autosummary/generate.py +9 -9
  76. sphinx/ext/coverage.py +26 -20
  77. sphinx/ext/doctest.py +38 -33
  78. sphinx/ext/duration.py +1 -0
  79. sphinx/ext/extlinks.py +4 -3
  80. sphinx/ext/githubpages.py +3 -2
  81. sphinx/ext/graphviz.py +10 -7
  82. sphinx/ext/ifconfig.py +5 -5
  83. sphinx/ext/imgconverter.py +6 -5
  84. sphinx/ext/imgmath.py +9 -8
  85. sphinx/ext/inheritance_diagram.py +31 -31
  86. sphinx/ext/intersphinx.py +140 -23
  87. sphinx/ext/linkcode.py +3 -2
  88. sphinx/ext/mathjax.py +2 -1
  89. sphinx/ext/napoleon/__init__.py +12 -7
  90. sphinx/ext/napoleon/docstring.py +34 -32
  91. sphinx/ext/todo.py +10 -7
  92. sphinx/ext/viewcode.py +12 -11
  93. sphinx/extension.py +18 -8
  94. sphinx/highlighting.py +39 -20
  95. sphinx/io.py +17 -8
  96. sphinx/jinja2glue.py +16 -15
  97. sphinx/locale/__init__.py +30 -23
  98. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  99. sphinx/locale/ar/LC_MESSAGES/sphinx.po +818 -761
  100. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  101. sphinx/locale/bg/LC_MESSAGES/sphinx.po +811 -754
  102. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  103. sphinx/locale/bn/LC_MESSAGES/sphinx.po +835 -778
  104. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  105. sphinx/locale/ca/LC_MESSAGES/sphinx.po +864 -807
  106. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  107. sphinx/locale/cak/LC_MESSAGES/sphinx.po +816 -759
  108. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  109. sphinx/locale/cs/LC_MESSAGES/sphinx.po +837 -780
  110. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  111. sphinx/locale/cy/LC_MESSAGES/sphinx.po +819 -762
  112. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  113. sphinx/locale/da/LC_MESSAGES/sphinx.po +838 -781
  114. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  115. sphinx/locale/de/LC_MESSAGES/sphinx.po +838 -781
  116. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  117. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +811 -754
  118. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  119. sphinx/locale/el/LC_MESSAGES/sphinx.po +853 -796
  120. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  121. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +811 -754
  122. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +811 -754
  124. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  125. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +856 -799
  126. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  127. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +811 -754
  128. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  129. sphinx/locale/eo/LC_MESSAGES/sphinx.po +820 -763
  130. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  131. sphinx/locale/es/LC_MESSAGES/sphinx.po +856 -799
  132. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  133. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +811 -754
  134. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  135. sphinx/locale/et/LC_MESSAGES/sphinx.po +845 -788
  136. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  137. sphinx/locale/eu/LC_MESSAGES/sphinx.po +837 -780
  138. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  139. sphinx/locale/fa/LC_MESSAGES/sphinx.po +854 -797
  140. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  141. sphinx/locale/fi/LC_MESSAGES/sphinx.po +816 -759
  142. sphinx/locale/fr/LC_MESSAGES/sphinx.js +1 -1
  143. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/fr/LC_MESSAGES/sphinx.po +904 -847
  145. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +811 -754
  147. sphinx/locale/gl/LC_MESSAGES/sphinx.js +54 -54
  148. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  149. sphinx/locale/gl/LC_MESSAGES/sphinx.po +1506 -1449
  150. sphinx/locale/he/LC_MESSAGES/sphinx.js +1 -1
  151. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/he/LC_MESSAGES/sphinx.po +823 -766
  153. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/hi/LC_MESSAGES/sphinx.po +853 -796
  155. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +811 -754
  157. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/hr/LC_MESSAGES/sphinx.po +844 -787
  159. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/hu/LC_MESSAGES/sphinx.po +837 -780
  161. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/id/LC_MESSAGES/sphinx.po +854 -797
  163. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/is/LC_MESSAGES/sphinx.po +811 -754
  165. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/it/LC_MESSAGES/sphinx.po +837 -780
  167. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/ja/LC_MESSAGES/sphinx.po +853 -796
  169. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/ka/LC_MESSAGES/sphinx.po +848 -791
  171. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/ko/LC_MESSAGES/sphinx.po +855 -798
  173. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/lt/LC_MESSAGES/sphinx.po +837 -780
  175. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/lv/LC_MESSAGES/sphinx.po +837 -780
  177. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/mk/LC_MESSAGES/sphinx.po +825 -768
  179. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.js +27 -27
  180. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  181. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +876 -818
  182. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  183. sphinx/locale/ne/LC_MESSAGES/sphinx.po +837 -780
  184. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  185. sphinx/locale/nl/LC_MESSAGES/sphinx.po +844 -787
  186. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  187. sphinx/locale/pl/LC_MESSAGES/sphinx.po +845 -788
  188. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  189. sphinx/locale/pt/LC_MESSAGES/sphinx.po +811 -754
  190. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  191. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +908 -851
  192. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  193. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +837 -780
  194. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  195. sphinx/locale/ro/LC_MESSAGES/sphinx.po +837 -780
  196. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  197. sphinx/locale/ru/LC_MESSAGES/sphinx.po +838 -781
  198. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  199. sphinx/locale/si/LC_MESSAGES/sphinx.po +823 -766
  200. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  201. sphinx/locale/sk/LC_MESSAGES/sphinx.po +854 -797
  202. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  203. sphinx/locale/sl/LC_MESSAGES/sphinx.po +832 -775
  204. sphinx/locale/sphinx.pot +813 -755
  205. sphinx/locale/sq/LC_MESSAGES/sphinx.js +1 -1
  206. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/sq/LC_MESSAGES/sphinx.po +865 -808
  208. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/sr/LC_MESSAGES/sphinx.po +835 -778
  210. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
  212. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/sv/LC_MESSAGES/sphinx.po +837 -780
  214. sphinx/locale/ta/LC_MESSAGES/sphinx.js +54 -54
  215. sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
  216. sphinx/locale/ta/LC_MESSAGES/sphinx.po +1530 -1473
  217. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  218. sphinx/locale/te/LC_MESSAGES/sphinx.po +811 -754
  219. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  220. sphinx/locale/tr/LC_MESSAGES/sphinx.po +853 -796
  221. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  222. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +833 -776
  223. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  224. sphinx/locale/ur/LC_MESSAGES/sphinx.po +811 -754
  225. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  226. sphinx/locale/vi/LC_MESSAGES/sphinx.po +837 -780
  227. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  228. sphinx/locale/yue/LC_MESSAGES/sphinx.po +811 -754
  229. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo +0 -0
  230. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +855 -798
  231. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  232. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +811 -754
  233. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.js +1 -1
  234. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  235. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +879 -822
  236. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  237. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +811 -754
  238. sphinx/parsers.py +7 -5
  239. sphinx/project.py +18 -11
  240. sphinx/pycode/__init__.py +6 -5
  241. sphinx/pycode/ast.py +23 -8
  242. sphinx/pycode/parser.py +6 -5
  243. sphinx/registry.py +12 -6
  244. sphinx/roles.py +103 -57
  245. sphinx/search/__init__.py +17 -18
  246. sphinx/search/da.py +2 -2
  247. sphinx/search/de.py +2 -2
  248. sphinx/search/en.py +1 -1
  249. sphinx/search/es.py +2 -2
  250. sphinx/search/fi.py +2 -2
  251. sphinx/search/fr.py +2 -2
  252. sphinx/search/hu.py +2 -2
  253. sphinx/search/it.py +2 -2
  254. sphinx/search/ja.py +13 -22
  255. sphinx/search/nl.py +2 -2
  256. sphinx/search/no.py +2 -2
  257. sphinx/search/pt.py +2 -2
  258. sphinx/search/ro.py +1 -1
  259. sphinx/search/ru.py +2 -2
  260. sphinx/search/sv.py +2 -2
  261. sphinx/search/tr.py +1 -1
  262. sphinx/search/zh.py +2 -3
  263. sphinx/templates/graphviz/graphviz.css +1 -1
  264. sphinx/testing/fixtures.py +41 -24
  265. sphinx/testing/path.py +1 -1
  266. sphinx/testing/util.py +142 -53
  267. sphinx/texinputs/sphinx.xdy +1 -1
  268. sphinx/texinputs/sphinxlatextables.sty +1 -1
  269. sphinx/texinputs/sphinxpackagesubstitutefont.sty +21 -0
  270. sphinx/themes/agogo/layout.html +4 -4
  271. sphinx/themes/agogo/static/agogo.css_t +1 -1
  272. sphinx/themes/agogo/theme.toml +22 -0
  273. sphinx/themes/basic/defindex.html +1 -1
  274. sphinx/themes/basic/domainindex.html +1 -1
  275. sphinx/themes/basic/genindex-single.html +1 -1
  276. sphinx/themes/basic/genindex-split.html +1 -1
  277. sphinx/themes/basic/genindex.html +1 -1
  278. sphinx/themes/basic/globaltoc.html +1 -1
  279. sphinx/themes/basic/layout.html +1 -1
  280. sphinx/themes/basic/localtoc.html +1 -1
  281. sphinx/themes/basic/page.html +1 -1
  282. sphinx/themes/basic/relations.html +1 -1
  283. sphinx/themes/basic/search.html +5 -20
  284. sphinx/themes/basic/searchbox.html +3 -3
  285. sphinx/themes/basic/searchfield.html +3 -3
  286. sphinx/themes/basic/sourcelink.html +1 -1
  287. sphinx/themes/basic/static/basic.css_t +1 -1
  288. sphinx/themes/basic/static/doctools.js +1 -1
  289. sphinx/themes/basic/static/language_data.js_t +2 -2
  290. sphinx/themes/basic/static/searchtools.js +105 -60
  291. sphinx/themes/basic/theme.toml +23 -0
  292. sphinx/themes/bizstyle/layout.html +1 -6
  293. sphinx/themes/bizstyle/static/bizstyle.css_t +1 -1
  294. sphinx/themes/bizstyle/static/bizstyle.js_t +1 -1
  295. sphinx/themes/bizstyle/static/css3-mediaqueries_src.js +3 -3
  296. sphinx/themes/bizstyle/theme.toml +12 -0
  297. sphinx/themes/classic/layout.html +1 -1
  298. sphinx/themes/classic/static/classic.css_t +1 -1
  299. sphinx/themes/classic/static/sidebar.js_t +1 -1
  300. sphinx/themes/classic/theme.toml +34 -0
  301. sphinx/themes/default/theme.toml +2 -0
  302. sphinx/themes/epub/epub-cover.html +1 -1
  303. sphinx/themes/epub/layout.html +1 -1
  304. sphinx/themes/epub/static/epub.css_t +1 -1
  305. sphinx/themes/epub/theme.toml +10 -0
  306. sphinx/themes/haiku/layout.html +3 -3
  307. sphinx/themes/haiku/static/haiku.css_t +2 -2
  308. sphinx/themes/haiku/theme.toml +16 -0
  309. sphinx/themes/nature/static/nature.css_t +1 -1
  310. sphinx/themes/nature/theme.toml +6 -0
  311. sphinx/themes/nonav/layout.html +1 -1
  312. sphinx/themes/nonav/static/nonav.css_t +1 -1
  313. sphinx/themes/nonav/theme.toml +10 -0
  314. sphinx/themes/pyramid/static/epub.css_t +1 -1
  315. sphinx/themes/pyramid/static/pyramid.css_t +1 -1
  316. sphinx/themes/pyramid/theme.toml +6 -0
  317. sphinx/themes/scrolls/artwork/logo.svg +1 -1
  318. sphinx/themes/scrolls/layout.html +2 -2
  319. sphinx/themes/scrolls/static/scrolls.css_t +1 -1
  320. sphinx/themes/scrolls/theme.toml +15 -0
  321. sphinx/themes/sphinxdoc/static/sphinxdoc.css_t +1 -1
  322. sphinx/themes/sphinxdoc/theme.toml +6 -0
  323. sphinx/themes/traditional/static/traditional.css_t +1 -1
  324. sphinx/themes/traditional/theme.toml +9 -0
  325. sphinx/theming.py +427 -131
  326. sphinx/transforms/__init__.py +21 -24
  327. sphinx/transforms/compact_bullet_list.py +5 -5
  328. sphinx/transforms/i18n.py +30 -28
  329. sphinx/transforms/post_transforms/__init__.py +9 -7
  330. sphinx/transforms/post_transforms/code.py +4 -1
  331. sphinx/transforms/post_transforms/images.py +17 -13
  332. sphinx/transforms/references.py +3 -1
  333. sphinx/util/__init__.py +15 -11
  334. sphinx/util/_io.py +34 -0
  335. sphinx/util/_pathlib.py +23 -18
  336. sphinx/util/build_phase.py +1 -0
  337. sphinx/util/cfamily.py +19 -11
  338. sphinx/util/console.py +101 -21
  339. sphinx/util/display.py +3 -2
  340. sphinx/util/docfields.py +12 -8
  341. sphinx/util/docutils.py +21 -35
  342. sphinx/util/exceptions.py +3 -2
  343. sphinx/util/fileutil.py +5 -5
  344. sphinx/util/http_date.py +9 -2
  345. sphinx/util/i18n.py +40 -9
  346. sphinx/util/inspect.py +317 -245
  347. sphinx/util/inventory.py +22 -5
  348. sphinx/util/logging.py +81 -7
  349. sphinx/util/matching.py +2 -1
  350. sphinx/util/math.py +1 -2
  351. sphinx/util/nodes.py +39 -29
  352. sphinx/util/osutil.py +25 -6
  353. sphinx/util/parallel.py +6 -1
  354. sphinx/util/requests.py +8 -5
  355. sphinx/util/rst.py +8 -6
  356. sphinx/util/tags.py +3 -3
  357. sphinx/util/template.py +8 -3
  358. sphinx/util/typing.py +76 -42
  359. sphinx/versioning.py +6 -2
  360. sphinx/writers/html.py +1 -1
  361. sphinx/writers/html5.py +17 -13
  362. sphinx/writers/latex.py +12 -12
  363. sphinx/writers/manpage.py +13 -7
  364. sphinx/writers/texinfo.py +13 -10
  365. sphinx/writers/text.py +13 -23
  366. sphinx/writers/xml.py +1 -1
  367. sphinx-7.2.6.dist-info/LICENSE → sphinx-7.3.1.dist-info/LICENSE.rst +1 -1
  368. {sphinx-7.2.6.dist-info → sphinx-7.3.1.dist-info}/METADATA +14 -12
  369. sphinx-7.3.1.dist-info/RECORD +581 -0
  370. sphinx/domains/c.py +0 -3906
  371. sphinx/domains/cpp.py +0 -8233
  372. sphinx/domains/python.py +0 -1769
  373. sphinx/themes/agogo/theme.conf +0 -20
  374. sphinx/themes/basic/theme.conf +0 -16
  375. sphinx/themes/bizstyle/theme.conf +0 -10
  376. sphinx/themes/classic/theme.conf +0 -32
  377. sphinx/themes/default/theme.conf +0 -2
  378. sphinx/themes/epub/theme.conf +0 -8
  379. sphinx/themes/haiku/theme.conf +0 -14
  380. sphinx/themes/nature/theme.conf +0 -4
  381. sphinx/themes/nonav/theme.conf +0 -8
  382. sphinx/themes/pyramid/theme.conf +0 -4
  383. sphinx/themes/scrolls/theme.conf +0 -13
  384. sphinx/themes/sphinxdoc/theme.conf +0 -4
  385. sphinx/themes/traditional/theme.conf +0 -7
  386. sphinx-7.2.6.dist-info/RECORD +0 -569
  387. {sphinx-7.2.6.dist-info → sphinx-7.3.1.dist-info}/WHEEL +0 -0
  388. {sphinx-7.2.6.dist-info → sphinx-7.3.1.dist-info}/entry_points.txt +0 -0
sphinx/util/template.py CHANGED
@@ -26,7 +26,8 @@ class BaseRenderer:
26
26
  def __init__(self, loader: BaseLoader | None = None) -> None:
27
27
  self.env = SandboxedEnvironment(loader=loader, extensions=['jinja2.ext.i18n'])
28
28
  self.env.filters['repr'] = repr
29
- self.env.install_gettext_translations(get_translator())
29
+ # ``install_gettext_translations`` is injected by the ``jinja2.ext.i18n`` extension
30
+ self.env.install_gettext_translations(get_translator()) # type: ignore[attr-defined]
30
31
 
31
32
  def render(self, template_name: str, context: dict[str, Any]) -> str:
32
33
  return self.env.get_template(template_name).render(context)
@@ -47,7 +48,9 @@ class FileRenderer(BaseRenderer):
47
48
  super().__init__(loader)
48
49
 
49
50
  @classmethod
50
- def render_from_file(cls, filename: str, context: dict[str, Any]) -> str:
51
+ def render_from_file(
52
+ cls: type[FileRenderer], filename: str, context: dict[str, Any],
53
+ ) -> str:
51
54
  dirname = os.path.dirname(filename)
52
55
  basename = os.path.basename(filename)
53
56
  return cls(dirname).render(basename, context)
@@ -60,7 +63,9 @@ class SphinxRenderer(FileRenderer):
60
63
  super().__init__(template_path)
61
64
 
62
65
  @classmethod
63
- def render_from_file(cls, filename: str, context: dict[str, Any]) -> str:
66
+ def render_from_file(
67
+ cls: type[FileRenderer], filename: str, context: dict[str, Any],
68
+ ) -> str:
64
69
  return FileRenderer.render_from_file(filename, context)
65
70
 
66
71
 
sphinx/util/typing.py CHANGED
@@ -3,11 +3,12 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import sys
6
+ import types
6
7
  import typing
7
8
  from collections.abc import Sequence
9
+ from contextvars import Context, ContextVar, Token
8
10
  from struct import Struct
9
- from types import TracebackType
10
- from typing import TYPE_CHECKING, Any, Callable, ForwardRef, TypeVar, Union
11
+ from typing import TYPE_CHECKING, Any, Callable, ForwardRef, TypedDict, TypeVar, Union
11
12
 
12
13
  from docutils import nodes
13
14
  from docutils.parsers.rst.states import Inliner
@@ -15,22 +16,47 @@ from docutils.parsers.rst.states import Inliner
15
16
  if TYPE_CHECKING:
16
17
  import enum
17
18
 
18
- try:
19
- from types import UnionType # type: ignore[attr-defined] # python 3.10 or above
20
- except ImportError:
19
+ from sphinx.application import Sphinx
20
+
21
+ if sys.version_info >= (3, 10):
22
+ from types import UnionType
23
+ else:
21
24
  UnionType = None
22
25
 
23
- # classes that have incorrect __module__
24
- INVALID_BUILTIN_CLASSES = {
26
+ # classes that have an incorrect .__module__ attribute
27
+ _INVALID_BUILTIN_CLASSES = {
28
+ Context: 'contextvars.Context', # Context.__module__ == '_contextvars'
29
+ ContextVar: 'contextvars.ContextVar', # ContextVar.__module__ == '_contextvars'
30
+ Token: 'contextvars.Token', # Token.__module__ == '_contextvars'
25
31
  Struct: 'struct.Struct', # Struct.__module__ == '_struct'
26
- TracebackType: 'types.TracebackType', # TracebackType.__module__ == 'builtins'
32
+ # types in 'types' with <type>.__module__ == 'builtins':
33
+ types.AsyncGeneratorType: 'types.AsyncGeneratorType',
34
+ types.BuiltinFunctionType: 'types.BuiltinFunctionType',
35
+ types.BuiltinMethodType: 'types.BuiltinMethodType',
36
+ types.CellType: 'types.CellType',
37
+ types.ClassMethodDescriptorType: 'types.ClassMethodDescriptorType',
38
+ types.CodeType: 'types.CodeType',
39
+ types.CoroutineType: 'types.CoroutineType',
40
+ types.FrameType: 'types.FrameType',
41
+ types.FunctionType: 'types.FunctionType',
42
+ types.GeneratorType: 'types.GeneratorType',
43
+ types.GetSetDescriptorType: 'types.GetSetDescriptorType',
44
+ types.LambdaType: 'types.LambdaType',
45
+ types.MappingProxyType: 'types.MappingProxyType',
46
+ types.MemberDescriptorType: 'types.MemberDescriptorType',
47
+ types.MethodDescriptorType: 'types.MethodDescriptorType',
48
+ types.MethodType: 'types.MethodType',
49
+ types.MethodWrapperType: 'types.MethodWrapperType',
50
+ types.ModuleType: 'types.ModuleType',
51
+ types.TracebackType: 'types.TracebackType',
52
+ types.WrapperDescriptorType: 'types.WrapperDescriptorType',
27
53
  }
28
54
 
29
55
 
30
56
  def is_invalid_builtin_class(obj: Any) -> bool:
31
57
  """Check *obj* is an invalid built-in class."""
32
58
  try:
33
- return obj in INVALID_BUILTIN_CLASSES
59
+ return obj in _INVALID_BUILTIN_CLASSES
34
60
  except TypeError: # unhashable type
35
61
  return False
36
62
 
@@ -64,6 +90,30 @@ InventoryItem = tuple[
64
90
  Inventory = dict[str, dict[str, InventoryItem]]
65
91
 
66
92
 
93
+ class ExtensionMetadata(TypedDict, total=False):
94
+ """The metadata returned by an extension's ``setup()`` function.
95
+
96
+ See :ref:`ext-metadata`.
97
+ """
98
+
99
+ version: str
100
+ """The extension version (default: ``'unknown version'``)."""
101
+ env_version: int
102
+ """An integer that identifies the version of env data added by the extension."""
103
+ parallel_read_safe: bool
104
+ """Indicate whether parallel reading of source files is supported
105
+ by the extension.
106
+ """
107
+ parallel_write_safe: bool
108
+ """Indicate whether parallel writing of output files is supported
109
+ by the extension (default: ``True``).
110
+ """
111
+
112
+
113
+ if TYPE_CHECKING:
114
+ _ExtensionSetupFunc = Callable[[Sphinx], ExtensionMetadata]
115
+
116
+
67
117
  def get_type_hints(
68
118
  obj: Any, globalns: dict[str, Any] | None = None, localns: dict[str, Any] | None = None,
69
119
  ) -> dict[str, Any]:
@@ -128,19 +178,15 @@ def restify(cls: type | None, mode: str = 'fully-qualified-except-typing') -> st
128
178
  elif ismock(cls):
129
179
  return f':py:class:`{modprefix}{cls.__module__}.{cls.__name__}`'
130
180
  elif is_invalid_builtin_class(cls):
131
- return f':py:class:`{modprefix}{INVALID_BUILTIN_CLASSES[cls]}`'
181
+ return f':py:class:`{modprefix}{_INVALID_BUILTIN_CLASSES[cls]}`'
132
182
  elif inspect.isNewType(cls):
133
183
  if sys.version_info[:2] >= (3, 10):
134
184
  # newtypes have correct module info since Python 3.10+
135
185
  return f':py:class:`{modprefix}{cls.__module__}.{cls.__name__}`'
136
186
  else:
137
- return ':py:class:`%s`' % cls.__name__
187
+ return f':py:class:`{cls.__name__}`'
138
188
  elif UnionType and isinstance(cls, UnionType):
139
- if len(cls.__args__) > 1 and None in cls.__args__:
140
- args = ' | '.join(restify(a, mode) for a in cls.__args__ if a)
141
- return 'Optional[%s]' % args
142
- else:
143
- return ' | '.join(restify(a, mode) for a in cls.__args__)
189
+ return ' | '.join(restify(a, mode) for a in cls.__args__)
144
190
  elif cls.__module__ in ('__builtin__', 'builtins'):
145
191
  if hasattr(cls, '__args__'):
146
192
  if not cls.__args__: # Empty tuple, list, ...
@@ -149,23 +195,11 @@ def restify(cls: type | None, mode: str = 'fully-qualified-except-typing') -> st
149
195
  concatenated_args = ', '.join(restify(arg, mode) for arg in cls.__args__)
150
196
  return fr':py:class:`{cls.__name__}`\ [{concatenated_args}]'
151
197
  else:
152
- return ':py:class:`%s`' % cls.__name__
198
+ return f':py:class:`{cls.__name__}`'
153
199
  elif (inspect.isgenericalias(cls)
154
200
  and cls.__module__ == 'typing'
155
201
  and cls.__origin__ is Union): # type: ignore[attr-defined]
156
- if (len(cls.__args__) > 1 # type: ignore[attr-defined]
157
- and cls.__args__[-1] is NoneType): # type: ignore[attr-defined]
158
- if len(cls.__args__) > 2: # type: ignore[attr-defined]
159
- args = ', '.join(restify(a, mode)
160
- for a in cls.__args__[:-1]) # type: ignore[attr-defined]
161
- return ':py:obj:`~typing.Optional`\\ [:obj:`~typing.Union`\\ [%s]]' % args
162
- else:
163
- return ':py:obj:`~typing.Optional`\\ [%s]' % restify(
164
- cls.__args__[0], mode) # type: ignore[attr-defined]
165
- else:
166
- args = ', '.join(restify(a, mode)
167
- for a in cls.__args__) # type: ignore[attr-defined]
168
- return ':py:obj:`~typing.Union`\\ [%s]' % args
202
+ return ' | '.join(restify(a, mode) for a in cls.__args__) # type: ignore[attr-defined]
169
203
  elif inspect.isgenericalias(cls):
170
204
  if isinstance(cls.__origin__, typing._SpecialForm): # type: ignore[attr-defined]
171
205
  text = restify(cls.__origin__, mode) # type: ignore[attr-defined,arg-type]
@@ -195,14 +229,14 @@ def restify(cls: type | None, mode: str = 'fully-qualified-except-typing') -> st
195
229
  literal_args.append(_format_literal_enum_arg(a, mode=mode))
196
230
  else:
197
231
  literal_args.append(repr(a))
198
- text += r"\ [%s]" % ', '.join(literal_args)
232
+ text += fr"\ [{', '.join(literal_args)}]"
199
233
  del literal_args
200
234
  elif cls.__args__:
201
- text += r"\ [%s]" % ", ".join(restify(a, mode) for a in cls.__args__)
235
+ text += fr"\ [{', '.join(restify(a, mode) for a in cls.__args__)}]"
202
236
 
203
237
  return text
204
238
  elif isinstance(cls, typing._SpecialForm):
205
- return f':py:obj:`~{cls.__module__}.{cls._name}`' # type: ignore[attr-defined]
239
+ return f':py:obj:`~{cls.__module__}.{cls._name}`'
206
240
  elif sys.version_info[:2] >= (3, 11) and cls is typing.Any:
207
241
  # handle bpo-46998
208
242
  return f':py:obj:`~{cls.__module__}.{cls.__name__}`'
@@ -212,7 +246,7 @@ def restify(cls: type | None, mode: str = 'fully-qualified-except-typing') -> st
212
246
  else:
213
247
  return f':py:class:`{modprefix}{cls.__module__}.{cls.__qualname__}`'
214
248
  elif isinstance(cls, ForwardRef):
215
- return ':py:class:`%s`' % cls.__forward_arg__
249
+ return f':py:class:`{cls.__forward_arg__}`'
216
250
  else:
217
251
  # not a class (ex. TypeVar)
218
252
  if cls.__module__ == 'typing':
@@ -285,7 +319,7 @@ def stringify_annotation(
285
319
  elif ismock(annotation):
286
320
  return module_prefix + f'{annotation_module}.{annotation_name}'
287
321
  elif is_invalid_builtin_class(annotation):
288
- return module_prefix + INVALID_BUILTIN_CLASSES[annotation]
322
+ return module_prefix + _INVALID_BUILTIN_CLASSES[annotation]
289
323
  elif str(annotation).startswith('typing.Annotated'): # for py310+
290
324
  pass
291
325
  elif annotation_module == 'builtins' and annotation_qualname:
@@ -350,7 +384,7 @@ def stringify_annotation(
350
384
  elif qualname == 'Literal':
351
385
  from sphinx.util.inspect import isenumattribute # lazy loading
352
386
 
353
- def format_literal_arg(arg):
387
+ def format_literal_arg(arg: Any) -> str:
354
388
  if isenumattribute(arg):
355
389
  enumcls = arg.__class__
356
390
 
@@ -384,19 +418,19 @@ def _format_literal_enum_arg(arg: enum.Enum, /, *, mode: str) -> str:
384
418
  return f':py:attr:`{enum_cls.__module__}.{enum_cls.__qualname__}.{arg.name}`'
385
419
 
386
420
 
387
- # deprecated name -> (object to return, canonical path or empty string)
388
- _DEPRECATED_OBJECTS = {
389
- 'stringify': (stringify_annotation, 'sphinx.util.typing.stringify_annotation'),
421
+ # deprecated name -> (object to return, canonical path or empty string, removal version)
422
+ _DEPRECATED_OBJECTS: dict[str, tuple[Any, str, tuple[int, int]]] = {
423
+ 'stringify': (stringify_annotation, 'sphinx.util.typing.stringify_annotation', (8, 0)),
390
424
  }
391
425
 
392
426
 
393
- def __getattr__(name):
427
+ def __getattr__(name: str) -> Any:
394
428
  if name not in _DEPRECATED_OBJECTS:
395
429
  msg = f'module {__name__!r} has no attribute {name!r}'
396
430
  raise AttributeError(msg)
397
431
 
398
432
  from sphinx.deprecation import _deprecation_warning
399
433
 
400
- deprecated_object, canonical_name = _DEPRECATED_OBJECTS[name]
401
- _deprecation_warning(__name__, name, canonical_name, remove=(8, 0))
434
+ deprecated_object, canonical_name, remove = _DEPRECATED_OBJECTS[name]
435
+ _deprecation_warning(__name__, name, canonical_name, remove=remove)
402
436
  return deprecated_object
sphinx/versioning.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Implements the low-level algorithms Sphinx uses for versioning doctrees."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import pickle
@@ -16,9 +17,11 @@ if TYPE_CHECKING:
16
17
  from docutils.nodes import Node
17
18
 
18
19
  from sphinx.application import Sphinx
20
+ from sphinx.util.typing import ExtensionMetadata
19
21
 
20
22
  try:
21
- import Levenshtein
23
+ import Levenshtein # type: ignore[import-not-found]
24
+
22
25
  IS_SPEEDUP = True
23
26
  except ImportError:
24
27
  IS_SPEEDUP = False
@@ -144,6 +147,7 @@ def levenshtein_distance(a: str, b: str) -> int:
144
147
 
145
148
  class UIDTransform(SphinxTransform):
146
149
  """Add UIDs to doctree for versioning."""
150
+
147
151
  default_priority = 880
148
152
 
149
153
  def apply(self, **kwargs: Any) -> None:
@@ -168,7 +172,7 @@ class UIDTransform(SphinxTransform):
168
172
  list(merge_doctrees(old_doctree, self.document, env.versioning_condition))
169
173
 
170
174
 
171
- def setup(app: Sphinx) -> dict[str, Any]:
175
+ def setup(app: Sphinx) -> ExtensionMetadata:
172
176
  app.add_transform(UIDTransform)
173
177
 
174
178
  return {
sphinx/writers/html.py CHANGED
@@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
17
17
  HTMLTranslator = HTML5Translator
18
18
 
19
19
  # A good overview of the purpose behind these classes can be found here:
20
- # http://www.arnebrodowski.de/blog/write-your-own-restructuredtext-writer.html
20
+ # https://www.arnebrodowski.de/blog/write-your-own-restructuredtext-writer.html
21
21
 
22
22
 
23
23
  class HTMLWriter(Writer):
sphinx/writers/html5.py CHANGED
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
28
28
  logger = logging.getLogger(__name__)
29
29
 
30
30
  # A good overview of the purpose behind these classes can be found here:
31
- # http://www.arnebrodowski.de/blog/write-your-own-restructuredtext-writer.html
31
+ # https://www.arnebrodowski.de/blog/write-your-own-restructuredtext-writer.html
32
32
 
33
33
 
34
34
  def multiply_length(length: str, scale: int) -> str:
@@ -247,8 +247,8 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
247
247
  self.depart_desc_parameter(node)
248
248
 
249
249
  def visit_desc_optional(self, node: Element) -> None:
250
- self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter)
251
- for c in node.children])
250
+ self.params_left_at_level = sum(isinstance(c, addnodes.desc_parameter)
251
+ for c in node.children)
252
252
  self.optional_param_level += 1
253
253
  self.max_optional_param_level = self.optional_param_level
254
254
  if self.multi_line_parameter_list:
@@ -338,7 +338,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
338
338
  self.depart_reference(node)
339
339
 
340
340
  # overwritten -- we don't want source comments to show up in the HTML
341
- def visit_comment(self, node: Element) -> None: # type: ignore[override]
341
+ def visit_comment(self, node: Element) -> None:
342
342
  raise nodes.SkipNode
343
343
 
344
344
  # overwritten
@@ -475,6 +475,17 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
475
475
  self.add_fignumber(node.parent)
476
476
  if isinstance(node.parent, nodes.table):
477
477
  self.body.append('<span class="caption-text">')
478
+ # Partially revert https://sourceforge.net/p/docutils/code/9562/
479
+ if (
480
+ isinstance(node.parent, nodes.topic)
481
+ and self.settings.toc_backlinks
482
+ and 'contents' in node.parent['classes']
483
+ and self.body[-1].startswith('<a ')
484
+ # TODO: only remove for EPUB
485
+ ):
486
+ # remove <a class="reference internal" href="#top">
487
+ self.body.pop()
488
+ self.context[-1] = '</p>\n'
478
489
 
479
490
  def depart_title(self, node: Element) -> None:
480
491
  close_tag = self.context[-1]
@@ -589,10 +600,8 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
589
600
 
590
601
  def visit_productionlist(self, node: Element) -> None:
591
602
  self.body.append(self.starttag(node, 'pre'))
592
- names = []
593
603
  productionlist = cast(Iterable[addnodes.production], node)
594
- for production in productionlist:
595
- names.append(production['tokenname'])
604
+ names = (production['tokenname'] for production in productionlist)
596
605
  maxlen = max(len(name) for name in names)
597
606
  lastname = None
598
607
  for production in productionlist:
@@ -846,13 +855,8 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
846
855
 
847
856
  def visit_manpage(self, node: Element) -> None:
848
857
  self.visit_literal_emphasis(node)
849
- if self.manpages_url:
850
- node['refuri'] = self.manpages_url.format(**node.attributes)
851
- self.visit_reference(node)
852
858
 
853
859
  def depart_manpage(self, node: Element) -> None:
854
- if self.manpages_url:
855
- self.depart_reference(node)
856
860
  self.depart_literal_emphasis(node)
857
861
 
858
862
  # overwritten to add even/odd classes
@@ -928,7 +932,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
928
932
 
929
933
  # See Docutils r9413
930
934
  # Re-instate the footnote-reference class
931
- def visit_footnote_reference(self, node):
935
+ def visit_footnote_reference(self, node: Element) -> None:
932
936
  href = '#' + node['refid']
933
937
  classes = ['footnote-reference', self.settings.footnote_references]
934
938
  self.body.append(self.starttag(node, 'a', suffix='', classes=classes,
sphinx/writers/latex.py CHANGED
@@ -29,7 +29,7 @@ try:
29
29
  from docutils.utils.roman import toRoman
30
30
  except ImportError:
31
31
  # In Debian/Ubuntu, roman package is provided as roman, not as docutils.utils.roman
32
- from roman import toRoman # type: ignore[no-redef]
32
+ from roman import toRoman # type: ignore[no-redef, import-not-found]
33
33
 
34
34
  if TYPE_CHECKING:
35
35
  from docutils.nodes import Element, Node, Text
@@ -158,7 +158,7 @@ class Table:
158
158
  return 'tabulary'
159
159
 
160
160
  def get_colspec(self) -> str:
161
- """Returns a column spec of table.
161
+ r"""Returns a column spec of table.
162
162
 
163
163
  This is what LaTeX calls the 'preamble argument' of the used table environment.
164
164
 
@@ -321,7 +321,7 @@ class LaTeXTranslator(SphinxTranslator):
321
321
  self.elements = self.builder.context.copy()
322
322
 
323
323
  # initial section names
324
- self.sectionnames = LATEXSECTIONNAMES[:]
324
+ self.sectionnames = LATEXSECTIONNAMES.copy()
325
325
  if self.theme.toplevel_sectioning == 'section':
326
326
  self.sectionnames.remove('chapter')
327
327
 
@@ -911,8 +911,8 @@ class LaTeXTranslator(SphinxTranslator):
911
911
  self._depart_sig_parameter(node)
912
912
 
913
913
  def visit_desc_optional(self, node: Element) -> None:
914
- self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter)
915
- for c in node.children])
914
+ self.params_left_at_level = sum(isinstance(c, addnodes.desc_parameter)
915
+ for c in node.children)
916
916
  self.optional_param_level += 1
917
917
  self.max_optional_param_level = self.optional_param_level
918
918
  if self.multi_line_parameter_list:
@@ -2121,14 +2121,14 @@ class LaTeXTranslator(SphinxTranslator):
2121
2121
  self.body.append('}}$')
2122
2122
 
2123
2123
  def visit_inline(self, node: Element) -> None:
2124
- classes = node.get('classes', [])
2125
- if classes in [['menuselection']]:
2124
+ classes = node.get('classes', []) # type: ignore[var-annotated]
2125
+ if classes == ['menuselection']:
2126
2126
  self.body.append(r'\sphinxmenuselection{')
2127
2127
  self.context.append('}')
2128
- elif classes in [['guilabel']]:
2128
+ elif classes == ['guilabel']:
2129
2129
  self.body.append(r'\sphinxguilabel{')
2130
2130
  self.context.append('}')
2131
- elif classes in [['accelerator']]:
2131
+ elif classes == ['accelerator']:
2132
2132
  self.body.append(r'\sphinxaccelerator{')
2133
2133
  self.context.append('}')
2134
2134
  elif classes and not self.in_title:
@@ -2153,12 +2153,12 @@ class LaTeXTranslator(SphinxTranslator):
2153
2153
  pass
2154
2154
 
2155
2155
  def visit_container(self, node: Element) -> None:
2156
- classes = node.get('classes', [])
2156
+ classes = node.get('classes', []) # type: ignore[var-annotated]
2157
2157
  for c in classes:
2158
2158
  self.body.append('\n\\begin{sphinxuseclass}{%s}' % c)
2159
2159
 
2160
2160
  def depart_container(self, node: Element) -> None:
2161
- classes = node.get('classes', [])
2161
+ classes = node.get('classes', []) # type: ignore[var-annotated]
2162
2162
  for _c in classes:
2163
2163
  self.body.append('\n\\end{sphinxuseclass}')
2164
2164
 
@@ -2261,6 +2261,6 @@ class LaTeXTranslator(SphinxTranslator):
2261
2261
 
2262
2262
  # FIXME: Workaround to avoid circular import
2263
2263
  # refs: https://github.com/sphinx-doc/sphinx/issues/5433
2264
- from sphinx.builders.latex.nodes import ( # noqa: E402 # isort:skip
2264
+ from sphinx.builders.latex.nodes import ( # NoQA: E402 # isort:skip
2265
2265
  HYPERLINK_SUPPORT_NODES, captioned_literal_block, footnotetext,
2266
2266
  )
sphinx/writers/manpage.py CHANGED
@@ -49,12 +49,13 @@ class NestedInlineTransform:
49
49
  <strong>foo=</strong><emphasis>var</emphasis>
50
50
  <strong>&bar=</strong><emphasis>2</emphasis>
51
51
  """
52
+
52
53
  def __init__(self, document: nodes.document) -> None:
53
54
  self.document = document
54
55
 
55
56
  def apply(self, **kwargs: Any) -> None:
56
57
  matcher = NodeMatcher(nodes.literal, nodes.emphasis, nodes.strong)
57
- for node in list(self.document.findall(matcher)): # type: nodes.TextElement
58
+ for node in list(matcher.findall(self.document)):
58
59
  if any(matcher(subnode) for subnode in node):
59
60
  pos = node.parent.index(node)
60
61
  for subnode in reversed(list(node)):
@@ -244,7 +245,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
244
245
  super().visit_term(node)
245
246
 
246
247
  # overwritten -- we don't want source comments to show up
247
- def visit_comment(self, node: Element) -> None: # type: ignore[override]
248
+ def visit_comment(self, node: Element) -> None:
248
249
  raise nodes.SkipNode
249
250
 
250
251
  # overwritten -- added ensure_eol()
@@ -271,12 +272,10 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
271
272
 
272
273
  def visit_productionlist(self, node: Element) -> None:
273
274
  self.ensure_eol()
274
- names = []
275
275
  self.in_productionlist += 1
276
276
  self.body.append('.sp\n.nf\n')
277
277
  productionlist = cast(Iterable[addnodes.production], node)
278
- for production in productionlist:
279
- names.append(production['tokenname'])
278
+ names = (production['tokenname'] for production in productionlist)
280
279
  maxlen = max(len(name) for name in names)
281
280
  lastname = None
282
281
  for production in productionlist:
@@ -309,13 +308,17 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
309
308
 
310
309
  # overwritten -- don't visit inner marked up nodes
311
310
  def visit_reference(self, node: Element) -> None:
311
+ uri = node.get('refuri', '')
312
+ if uri:
313
+ # OSC 8 link start (using groff's device control directive).
314
+ self.body.append(fr"\X'tty: link {uri}'")
315
+
312
316
  self.body.append(self.defs['reference'][0])
313
317
  # avoid repeating escaping code... fine since
314
318
  # visit_Text calls astext() and only works on that afterwards
315
- self.visit_Text(node) # type: ignore[arg-type]
319
+ self.visit_Text(node)
316
320
  self.body.append(self.defs['reference'][1])
317
321
 
318
- uri = node.get('refuri', '')
319
322
  if uri.startswith(('mailto:', 'http:', 'https:', 'ftp:')):
320
323
  # if configured, put the URL after the link
321
324
  if self.config.man_show_urls and node.astext() != uri:
@@ -325,6 +328,9 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
325
328
  ' <',
326
329
  self.defs['strong'][0], uri, self.defs['strong'][1],
327
330
  '>'])
331
+ if uri:
332
+ # OSC 8 link end.
333
+ self.body.append(r"\X'tty: link'")
328
334
  raise nodes.SkipNode
329
335
 
330
336
  def visit_number_reference(self, node: Element) -> None:
sphinx/writers/texinfo.py CHANGED
@@ -96,7 +96,8 @@ def find_subsections(section: Element) -> list[nodes.section]:
96
96
 
97
97
  def smart_capwords(s: str, sep: str | None = None) -> str:
98
98
  """Like string.capwords() but does not capitalize words that already
99
- contain a capital letter."""
99
+ contain a capital letter.
100
+ """
100
101
  words = s.split(sep)
101
102
  for i, word in enumerate(words):
102
103
  if all(x.islower() for x in word):
@@ -106,6 +107,7 @@ def smart_capwords(s: str, sep: str | None = None) -> str:
106
107
 
107
108
  class TexinfoWriter(writers.Writer):
108
109
  """Texinfo writer for generating Texinfo documents."""
110
+
109
111
  supported = ('texinfo', 'texi')
110
112
 
111
113
  settings_spec: tuple[str, Any, tuple[tuple[str, list[str], dict[str, str]], ...]] = (
@@ -255,7 +257,8 @@ class TexinfoTranslator(SphinxTranslator):
255
257
  def collect_node_names(self) -> None:
256
258
  """Generates a unique id for each section.
257
259
 
258
- Assigns the attribute ``node_name`` to each section."""
260
+ Assigns the attribute ``node_name`` to each section.
261
+ """
259
262
 
260
263
  def add_node_name(name: str) -> str:
261
264
  node_id = self.escape_id(name)
@@ -278,7 +281,7 @@ class TexinfoTranslator(SphinxTranslator):
278
281
  for name, content in self.indices]
279
282
  # each section is also a node
280
283
  for section in self.document.findall(nodes.section):
281
- title = cast(nodes.TextElement, section.next_node(nodes.Titular))
284
+ title = cast(nodes.TextElement, section.next_node(nodes.Titular)) # type: ignore[type-var]
282
285
  name = title.astext() if title else '<untitled>'
283
286
  section['node_name'] = add_node_name(name)
284
287
 
@@ -288,7 +291,7 @@ class TexinfoTranslator(SphinxTranslator):
288
291
  targets: list[Element] = [self.document]
289
292
  targets.extend(self.document.findall(nodes.section))
290
293
  for node in targets:
291
- assert 'node_name' in node and node['node_name'] # NoQA: PT018
294
+ assert node.get('node_name', False)
292
295
  entries = [s['node_name'] for s in find_subsections(node)]
293
296
  node_menus[node['node_name']] = entries
294
297
  # try to find a suitable "Top" node
@@ -352,7 +355,8 @@ class TexinfoTranslator(SphinxTranslator):
352
355
 
353
356
  def escape_arg(self, s: str) -> str:
354
357
  """Return an escaped string suitable for use as an argument
355
- to a Texinfo command."""
358
+ to a Texinfo command.
359
+ """
356
360
  s = self.escape(s)
357
361
  # commas are the argument delimiters
358
362
  s = s.replace(',', '@comma{}')
@@ -430,7 +434,7 @@ class TexinfoTranslator(SphinxTranslator):
430
434
  entries = self.node_menus[name]
431
435
  if not entries:
432
436
  return
433
- self.body.append(f'\n{self.escape(self.node_names[name], )}\n\n')
437
+ self.body.append(f'\n{self.escape(self.node_names[name])}\n\n')
434
438
  self.add_menu_entries(entries)
435
439
  for subentry in entries:
436
440
  _add_detailed_menu(subentry)
@@ -524,7 +528,7 @@ class TexinfoTranslator(SphinxTranslator):
524
528
  try:
525
529
  sid = self.short_ids[id]
526
530
  except KeyError:
527
- sid = hex(len(self.short_ids))[2:]
531
+ sid = f'{len(self.short_ids):x}'
528
532
  self.short_ids[id] = sid
529
533
  return sid
530
534
 
@@ -1287,11 +1291,10 @@ class TexinfoTranslator(SphinxTranslator):
1287
1291
 
1288
1292
  def visit_productionlist(self, node: Element) -> None:
1289
1293
  self.visit_literal_block(None)
1290
- names = []
1291
1294
  productionlist = cast(Iterable[addnodes.production], node)
1292
- for production in productionlist:
1293
- names.append(production['tokenname'])
1295
+ names = (production['tokenname'] for production in productionlist)
1294
1296
  maxlen = max(len(name) for name in names)
1297
+
1295
1298
  for production in productionlist:
1296
1299
  if production['tokenname']:
1297
1300
  for id in production.get('ids'):