Sphinx 7.2.6__py3-none-any.whl → 7.3.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 (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.0.dist-info/LICENSE.rst +1 -1
  368. {sphinx-7.2.6.dist-info → sphinx-7.3.0.dist-info}/METADATA +13 -12
  369. sphinx-7.3.0.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.0.dist-info}/WHEEL +0 -0
  388. {sphinx-7.2.6.dist-info → sphinx-7.3.0.dist-info}/entry_points.txt +0 -0
sphinx/ext/intersphinx.py CHANGED
@@ -34,6 +34,7 @@ from docutils.utils import relative_path
34
34
  import sphinx
35
35
  from sphinx.addnodes import pending_xref
36
36
  from sphinx.builders.html import INVENTORY_FILENAME
37
+ from sphinx.deprecation import _deprecation_warning
37
38
  from sphinx.errors import ExtensionError
38
39
  from sphinx.locale import _, __
39
40
  from sphinx.transforms.post_transforms import ReferencesResolver
@@ -53,7 +54,7 @@ if TYPE_CHECKING:
53
54
  from sphinx.config import Config
54
55
  from sphinx.domains import Domain
55
56
  from sphinx.environment import BuildEnvironment
56
- from sphinx.util.typing import Inventory, InventoryItem, RoleFunction
57
+ from sphinx.util.typing import ExtensionMetadata, Inventory, InventoryItem, RoleFunction
57
58
 
58
59
  InventoryCacheEntry = tuple[Union[str, None], int, Inventory]
59
60
 
@@ -245,7 +246,7 @@ def fetch_inventory_group(
245
246
  for fail in failures:
246
247
  logger.info(*fail)
247
248
  else:
248
- issues = '\n'.join([f[0] % f[1:] for f in failures])
249
+ issues = '\n'.join(f[0] % f[1:] for f in failures)
249
250
  logger.warning(__("failed to reach any of the inventories "
250
251
  "with the following issues:") + "\n" + issues)
251
252
 
@@ -334,8 +335,10 @@ def _resolve_reference_in_domain_by_target(
334
335
  if target in inventory[objtype]:
335
336
  # Case sensitive match, use it
336
337
  data = inventory[objtype][target]
337
- elif objtype == 'std:term':
338
- # Check for potential case insensitive matches for terms only
338
+ elif objtype in {'std:label', 'std:term'}:
339
+ # Some types require case insensitive matches:
340
+ # * 'term': https://github.com/sphinx-doc/sphinx/issues/9291
341
+ # * 'label': https://github.com/sphinx-doc/sphinx/issues/12008
339
342
  target_lower = target.lower()
340
343
  insensitive_matches = list(filter(lambda k: k.lower() == target_lower,
341
344
  inventory[objtype].keys()))
@@ -479,7 +482,6 @@ def resolve_reference_detect_inventory(env: BuildEnvironment,
479
482
  to form ``inv_name:newtarget``. If ``inv_name`` is a named inventory, then resolution
480
483
  is tried in that inventory with the new target.
481
484
  """
482
-
483
485
  # ordinary direct lookup, use data as is
484
486
  res = resolve_reference_any_inventory(env, True, node, contnode)
485
487
  if res is not None:
@@ -501,7 +503,6 @@ def resolve_reference_detect_inventory(env: BuildEnvironment,
501
503
  def missing_reference(app: Sphinx, env: BuildEnvironment, node: pending_xref,
502
504
  contnode: TextElement) -> nodes.reference | None:
503
505
  """Attempt to resolve a missing reference via intersphinx references."""
504
-
505
506
  return resolve_reference_detect_inventory(env, node, contnode)
506
507
 
507
508
 
@@ -533,17 +534,90 @@ class IntersphinxRole(SphinxRole):
533
534
  assert self.name == self.orig_name.lower()
534
535
  inventory, name_suffix = self.get_inventory_and_name_suffix(self.orig_name)
535
536
  if inventory and not inventory_exists(self.env, inventory):
536
- logger.warning(__('inventory for external cross-reference not found: %s'),
537
- inventory, location=(self.env.docname, self.lineno))
537
+ self._emit_warning(
538
+ __('inventory for external cross-reference not found: %r'), inventory
539
+ )
538
540
  return [], []
539
541
 
540
- role_name = self.get_role_name(name_suffix)
542
+ domain_name, role_name = self._get_domain_role(name_suffix)
543
+
541
544
  if role_name is None:
542
- logger.warning(__('role for external cross-reference not found: %s'), name_suffix,
543
- location=(self.env.docname, self.lineno))
545
+ self._emit_warning(
546
+ __('invalid external cross-reference suffix: %r'), name_suffix
547
+ )
544
548
  return [], []
545
549
 
546
- result, messages = self.invoke_role(role_name)
550
+ # attempt to find a matching role function
551
+ role_func: RoleFunction | None
552
+
553
+ if domain_name is not None:
554
+ # the user specified a domain, so we only check that
555
+ if (domain := self.env.domains.get(domain_name)) is None:
556
+ self._emit_warning(
557
+ __('domain for external cross-reference not found: %r'), domain_name
558
+ )
559
+ return [], []
560
+ if (role_func := domain.roles.get(role_name)) is None:
561
+ msg = 'role for external cross-reference not found in domain %r: %r'
562
+ if (
563
+ object_types := domain.object_types.get(role_name)
564
+ ) is not None and object_types.roles:
565
+ self._emit_warning(
566
+ __(f'{msg} (perhaps you meant one of: %s)'),
567
+ domain_name,
568
+ role_name,
569
+ self._concat_strings(object_types.roles),
570
+ )
571
+ else:
572
+ self._emit_warning(__(msg), domain_name, role_name)
573
+ return [], []
574
+
575
+ else:
576
+ # the user did not specify a domain,
577
+ # so we check first the default (if available) then standard domains
578
+ domains: list[Domain] = []
579
+ if default_domain := self.env.temp_data.get('default_domain'):
580
+ domains.append(default_domain)
581
+ if (
582
+ std_domain := self.env.domains.get('std')
583
+ ) is not None and std_domain not in domains:
584
+ domains.append(std_domain)
585
+
586
+ role_func = None
587
+ for domain in domains:
588
+ if (role_func := domain.roles.get(role_name)) is not None:
589
+ domain_name = domain.name
590
+ break
591
+
592
+ if role_func is None or domain_name is None:
593
+ domains_str = self._concat_strings(d.name for d in domains)
594
+ msg = 'role for external cross-reference not found in domains %s: %r'
595
+ possible_roles: set[str] = set()
596
+ for d in domains:
597
+ if o := d.object_types.get(role_name):
598
+ possible_roles.update(f'{d.name}:{r}' for r in o.roles)
599
+ if possible_roles:
600
+ msg = f'{msg} (perhaps you meant one of: %s)'
601
+ self._emit_warning(
602
+ __(msg),
603
+ domains_str,
604
+ role_name,
605
+ self._concat_strings(possible_roles),
606
+ )
607
+ else:
608
+ self._emit_warning(__(msg), domains_str, role_name)
609
+ return [], []
610
+
611
+ result, messages = role_func(
612
+ f'{domain_name}:{role_name}',
613
+ self.rawtext,
614
+ self.text,
615
+ self.lineno,
616
+ self.inliner,
617
+ self.options,
618
+ self.content,
619
+ )
620
+
547
621
  for node in result:
548
622
  if isinstance(node, pending_xref):
549
623
  node['intersphinx'] = True
@@ -552,13 +626,17 @@ class IntersphinxRole(SphinxRole):
552
626
  return result, messages
553
627
 
554
628
  def get_inventory_and_name_suffix(self, name: str) -> tuple[str | None, str]:
629
+ """Extract an inventory name (if any) and ``domain+name`` suffix from a role *name*.
630
+ and the domain+name suffix.
631
+
632
+ The role name is expected to be of one of the following forms:
633
+
634
+ - ``external+inv:name`` -- explicit inventory and name, any domain.
635
+ - ``external+inv:domain:name`` -- explicit inventory, domain and name.
636
+ - ``external:name`` -- any inventory and domain, explicit name.
637
+ - ``external:domain:name`` -- any inventory, explicit domain and name.
638
+ """
555
639
  assert name.startswith('external'), name
556
- # either we have an explicit inventory name, i.e,
557
- # :external+inv:role: or
558
- # :external+inv:domain:role:
559
- # or we look in all inventories, i.e.,
560
- # :external:role: or
561
- # :external:domain:role:
562
640
  suffix = name[9:]
563
641
  if name[8] == '+':
564
642
  inv_name, suffix = suffix.split(':', 1)
@@ -569,7 +647,39 @@ class IntersphinxRole(SphinxRole):
569
647
  msg = f'Malformed :external: role name: {name}'
570
648
  raise ValueError(msg)
571
649
 
650
+ def _get_domain_role(self, name: str) -> tuple[str | None, str | None]:
651
+ """Convert the *name* string into a domain and a role name.
652
+
653
+ - If *name* contains no ``:``, return ``(None, name)``.
654
+ - If *name* contains a single ``:``, the domain/role is split on this.
655
+ - If *name* contains multiple ``:``, return ``(None, None)``.
656
+ """
657
+ names = name.split(':')
658
+ if len(names) == 1:
659
+ return None, names[0]
660
+ elif len(names) == 2:
661
+ return names[0], names[1]
662
+ else:
663
+ return None, None
664
+
665
+ def _emit_warning(self, msg: str, /, *args: Any) -> None:
666
+ logger.warning(
667
+ msg,
668
+ *args,
669
+ type='intersphinx',
670
+ subtype='external',
671
+ location=(self.env.docname, self.lineno),
672
+ )
673
+
674
+ def _concat_strings(self, strings: Iterable[str]) -> str:
675
+ return ', '.join(f'{s!r}' for s in sorted(strings))
676
+
677
+ # deprecated methods
678
+
572
679
  def get_role_name(self, name: str) -> tuple[str, str] | None:
680
+ _deprecation_warning(
681
+ __name__, f'{self.__class__.__name__}.get_role_name', '', remove=(9, 0)
682
+ )
573
683
  names = name.split(':')
574
684
  if len(names) == 1:
575
685
  # role
@@ -591,6 +701,9 @@ class IntersphinxRole(SphinxRole):
591
701
  return None
592
702
 
593
703
  def is_existent_role(self, domain_name: str, role_name: str) -> bool:
704
+ _deprecation_warning(
705
+ __name__, f'{self.__class__.__name__}.is_existent_role', '', remove=(9, 0)
706
+ )
594
707
  try:
595
708
  domain = self.env.get_domain(domain_name)
596
709
  return role_name in domain.roles
@@ -598,6 +711,10 @@ class IntersphinxRole(SphinxRole):
598
711
  return False
599
712
 
600
713
  def invoke_role(self, role: tuple[str, str]) -> tuple[list[Node], list[system_message]]:
714
+ """Invoke the role described by a ``(domain, role name)`` pair."""
715
+ _deprecation_warning(
716
+ __name__, f'{self.__class__.__name__}.invoke_role', '', remove=(9, 0)
717
+ )
601
718
  domain = self.env.get_domain(role[0])
602
719
  if domain:
603
720
  role_func = domain.role(role[1])
@@ -681,11 +798,11 @@ def normalize_intersphinx_mapping(app: Sphinx, config: Config) -> None:
681
798
  config.intersphinx_mapping.pop(key)
682
799
 
683
800
 
684
- def setup(app: Sphinx) -> dict[str, Any]:
685
- app.add_config_value('intersphinx_mapping', {}, True)
686
- app.add_config_value('intersphinx_cache_limit', 5, False)
687
- app.add_config_value('intersphinx_timeout', None, False)
688
- app.add_config_value('intersphinx_disabled_reftypes', ['std:doc'], True)
801
+ def setup(app: Sphinx) -> ExtensionMetadata:
802
+ app.add_config_value('intersphinx_mapping', {}, 'env')
803
+ app.add_config_value('intersphinx_cache_limit', 5, '')
804
+ app.add_config_value('intersphinx_timeout', None, '')
805
+ app.add_config_value('intersphinx_disabled_reftypes', ['std:doc'], 'env')
689
806
  app.connect('config-inited', normalize_intersphinx_mapping, priority=800)
690
807
  app.connect('builder-inited', load_mappings)
691
808
  app.connect('source-read', install_dispatcher)
sphinx/ext/linkcode.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING, Any
5
+ from typing import TYPE_CHECKING
6
6
 
7
7
  from docutils import nodes
8
8
 
@@ -15,6 +15,7 @@ if TYPE_CHECKING:
15
15
  from docutils.nodes import Node
16
16
 
17
17
  from sphinx.application import Sphinx
18
+ from sphinx.util.typing import ExtensionMetadata
18
19
 
19
20
 
20
21
  class LinkcodeError(SphinxError):
@@ -71,7 +72,7 @@ def doctree_read(app: Sphinx, doctree: Node) -> None:
71
72
  signode += onlynode
72
73
 
73
74
 
74
- def setup(app: Sphinx) -> dict[str, Any]:
75
+ def setup(app: Sphinx) -> ExtensionMetadata:
75
76
  app.connect('doctree-read', doctree_read)
76
77
  app.add_config_value('linkcode_resolve', None, '')
77
78
  return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
sphinx/ext/mathjax.py CHANGED
@@ -21,6 +21,7 @@ from sphinx.util.math import get_node_equation_number
21
21
 
22
22
  if TYPE_CHECKING:
23
23
  from sphinx.application import Sphinx
24
+ from sphinx.util.typing import ExtensionMetadata
24
25
  from sphinx.writers.html import HTML5Translator
25
26
 
26
27
  # more information for mathjax secure url is here:
@@ -109,7 +110,7 @@ def install_mathjax(app: Sphinx, pagename: str, templatename: str, context: dict
109
110
  builder.add_js_file(app.config.mathjax_path, **options)
110
111
 
111
112
 
112
- def setup(app: Sphinx) -> dict[str, Any]:
113
+ def setup(app: Sphinx) -> ExtensionMetadata:
113
114
  app.add_html_math_renderer('mathjax',
114
115
  (html_visit_math, None),
115
116
  (html_visit_displaymath, None))
@@ -2,13 +2,17 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any
5
+ from typing import TYPE_CHECKING, Any
6
6
 
7
7
  import sphinx
8
8
  from sphinx.application import Sphinx
9
9
  from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring
10
10
  from sphinx.util import inspect
11
11
 
12
+ if TYPE_CHECKING:
13
+ from sphinx.config import _ConfigRebuild
14
+ from sphinx.util.typing import ExtensionMetadata
15
+
12
16
 
13
17
  class Config:
14
18
  """Sphinx napoleon extension settings in `conf.py`.
@@ -261,8 +265,9 @@ class Config:
261
265
  Use the type annotations of class attributes that are documented in the docstring
262
266
  but do not have a type in the docstring.
263
267
 
264
- """
265
- _config_values = {
268
+ """ # NoQA: D301
269
+
270
+ _config_values: dict[str, tuple[Any, _ConfigRebuild]] = {
266
271
  'napoleon_google_docstring': (True, 'env'),
267
272
  'napoleon_numpy_docstring': (True, 'env'),
268
273
  'napoleon_include_init_with_doc': (False, 'env'),
@@ -288,7 +293,7 @@ class Config:
288
293
  setattr(self, name, value)
289
294
 
290
295
 
291
- def setup(app: Sphinx) -> dict[str, Any]:
296
+ def setup(app: Sphinx) -> ExtensionMetadata:
292
297
  """Sphinx extension setup function.
293
298
 
294
299
  When the extension is loaded, Sphinx imports this module and executes
@@ -326,7 +331,7 @@ def setup(app: Sphinx) -> dict[str, Any]:
326
331
 
327
332
 
328
333
  def _patch_python_domain() -> None:
329
- from sphinx.domains.python import PyObject, PyTypedField
334
+ from sphinx.domains.python._object import PyObject, PyTypedField
330
335
  from sphinx.locale import _
331
336
  for doc_field in PyObject.doc_field_types:
332
337
  if doc_field.name == 'parameter':
@@ -335,7 +340,7 @@ def _patch_python_domain() -> None:
335
340
  PyObject.doc_field_types.append(
336
341
  PyTypedField('keyword', label=_('Keyword Arguments'),
337
342
  names=('keyword', 'kwarg', 'kwparam'),
338
- typerolename='obj', typenames=('paramtype', 'kwtype'),
343
+ typerolename='class', typenames=('paramtype', 'kwtype'),
339
344
  can_collapse=True))
340
345
 
341
346
 
@@ -386,7 +391,7 @@ def _process_docstring(app: Sphinx, what: str, name: str, obj: Any,
386
391
  docstring = GoogleDocstring(result_lines, app.config, app, what, name,
387
392
  obj, options)
388
393
  result_lines = docstring.lines()
389
- lines[:] = result_lines[:]
394
+ lines[:] = result_lines.copy()
390
395
 
391
396
 
392
397
  def _skip_member(app: Sphinx, what: str, name: str, obj: Any,
@@ -7,6 +7,7 @@ import contextlib
7
7
  import inspect
8
8
  import re
9
9
  from functools import partial
10
+ from itertools import starmap
10
11
  from typing import TYPE_CHECKING, Any, Callable
11
12
 
12
13
  from sphinx.locale import _, __
@@ -14,6 +15,8 @@ from sphinx.util import logging
14
15
  from sphinx.util.typing import get_type_hints, stringify_annotation
15
16
 
16
17
  if TYPE_CHECKING:
18
+ from collections.abc import Iterator
19
+
17
20
  from sphinx.application import Sphinx
18
21
  from sphinx.config import Config as SphinxConfig
19
22
 
@@ -145,7 +148,7 @@ class GoogleDocstring:
145
148
  """
146
149
 
147
150
  _name_rgx = re.compile(r"^\s*((?::(?P<role>\S+):)?`(?P<name>~?[a-zA-Z0-9_.-]+)`|"
148
- r" (?P<name2>~?[a-zA-Z0-9_.-]+))\s*", re.X)
151
+ r" (?P<name2>~?[a-zA-Z0-9_.-]+))\s*", re.VERBOSE)
149
152
 
150
153
  def __init__(
151
154
  self,
@@ -304,19 +307,18 @@ class GoogleDocstring:
304
307
  _type = _convert_type_spec(_type, self._config.napoleon_type_aliases or {})
305
308
 
306
309
  indent = self._get_indent(line) + 1
307
- _descs = [_desc] + self._dedent(self._consume_indented_block(indent))
310
+ _descs = [_desc, *self._dedent(self._consume_indented_block(indent))]
308
311
  _descs = self.__class__(_descs, self._config).lines()
309
312
  return _name, _type, _descs
310
313
 
311
314
  def _consume_fields(self, parse_type: bool = True, prefer_type: bool = False,
312
315
  multiple: bool = False) -> list[tuple[str, str, list[str]]]:
313
316
  self._consume_empty()
314
- fields = []
317
+ fields: list[tuple[str, str, list[str]]] = []
315
318
  while not self._is_section_break():
316
319
  _name, _type, _desc = self._consume_field(parse_type, prefer_type)
317
320
  if multiple and _name:
318
- for name in _name.split(","):
319
- fields.append((name.strip(), _type, _desc))
321
+ fields.extend((name.strip(), _type, _desc) for name in _name.split(","))
320
322
  elif _name or _type or _desc:
321
323
  fields.append((_name, _type, _desc))
322
324
  return fields
@@ -327,7 +329,7 @@ class GoogleDocstring:
327
329
  if not colon or not _desc:
328
330
  _type, _desc = _desc, _type
329
331
  _desc += colon
330
- _descs = [_desc] + self._dedent(self._consume_to_end())
332
+ _descs = [_desc, *self._dedent(self._consume_to_end())]
331
333
  _descs = self.__class__(_descs, self._config).lines()
332
334
  return _type, _descs
333
335
 
@@ -399,15 +401,15 @@ class GoogleDocstring:
399
401
 
400
402
  def _fix_field_desc(self, desc: list[str]) -> list[str]:
401
403
  if self._is_list(desc):
402
- desc = [''] + desc
404
+ desc = ['', *desc]
403
405
  elif desc[0].endswith('::'):
404
406
  desc_block = desc[1:]
405
407
  indent = self._get_indent(desc[0])
406
408
  block_indent = self._get_initial_indent(desc_block)
407
409
  if block_indent > indent:
408
- desc = [''] + desc
410
+ desc = ['', *desc]
409
411
  else:
410
- desc = ['', desc[0]] + self._indent(desc_block, 4)
412
+ desc = ['', desc[0], *self._indent(desc_block, 4)]
411
413
  return desc
412
414
 
413
415
  def _format_admonition(self, admonition: str, lines: list[str]) -> list[str]:
@@ -416,7 +418,7 @@ class GoogleDocstring:
416
418
  return [f'.. {admonition}:: {lines[0].strip()}', '']
417
419
  elif lines:
418
420
  lines = self._indent(self._dedent(lines), 3)
419
- return ['.. %s::' % admonition, ''] + lines + ['']
421
+ return ['.. %s::' % admonition, '', *lines, '']
420
422
  else:
421
423
  return ['.. %s::' % admonition, '']
422
424
 
@@ -453,7 +455,7 @@ class GoogleDocstring:
453
455
 
454
456
  if _type:
455
457
  lines.append(f':{type_role} {_name}: {_type}')
456
- return lines + ['']
458
+ return [*lines, '']
457
459
 
458
460
  def _format_field(self, _name: str, _type: str, _desc: list[str]) -> list[str]:
459
461
  _desc = self._strip_empty(_desc)
@@ -480,7 +482,7 @@ class GoogleDocstring:
480
482
  if _desc[0]:
481
483
  return [field + _desc[0]] + _desc[1:]
482
484
  else:
483
- return [field] + _desc
485
+ return [field, *_desc]
484
486
  else:
485
487
  return [field]
486
488
 
@@ -537,7 +539,7 @@ class GoogleDocstring:
537
539
  return [(' ' * n) + line for line in lines]
538
540
 
539
541
  def _is_indented(self, line: str, indent: int = 1) -> bool:
540
- for i, s in enumerate(line): # noqa: SIM110
542
+ for i, s in enumerate(line): # NoQA: SIM110
541
543
  if i >= indent:
542
544
  return True
543
545
  elif not s.isspace():
@@ -623,7 +625,7 @@ class GoogleDocstring:
623
625
  self._is_in_section = True
624
626
  self._section_indent = self._get_current_indent()
625
627
  if _directive_regex.match(section):
626
- lines = [section] + self._consume_to_next_section()
628
+ lines = [section, *self._consume_to_next_section()]
627
629
  else:
628
630
  lines = self._sections[section.lower()](section)
629
631
  finally:
@@ -711,7 +713,7 @@ class GoogleDocstring:
711
713
  else:
712
714
  header = '.. rubric:: %s' % section
713
715
  if lines:
714
- return [header, ''] + lines + ['']
716
+ return [header, '', *lines, '']
715
717
  else:
716
718
  return [header, '']
717
719
 
@@ -733,7 +735,7 @@ class GoogleDocstring:
733
735
  if 'no-index' in self._opt or 'noindex' in self._opt:
734
736
  lines.append(' :no-index:')
735
737
  if _desc:
736
- lines.extend([''] + self._indent(_desc, 3))
738
+ lines.extend(['', *self._indent(_desc, 3)])
737
739
  lines.append('')
738
740
  return lines
739
741
 
@@ -888,7 +890,7 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]:
888
890
  token_queue = collections.deque(tokens)
889
891
  keywords = ("optional", "default")
890
892
 
891
- def takewhile_set(tokens):
893
+ def takewhile_set(tokens: collections.deque[str]) -> Iterator[str]:
892
894
  open_braces = 0
893
895
  previous_token = None
894
896
  while True:
@@ -924,7 +926,7 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]:
924
926
  if open_braces == 0:
925
927
  break
926
928
 
927
- def combine_set(tokens):
929
+ def combine_set(tokens: collections.deque[str]) -> Iterator[str]:
928
930
  while True:
929
931
  try:
930
932
  token = tokens.popleft()
@@ -941,7 +943,7 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]:
941
943
 
942
944
 
943
945
  def _tokenize_type_spec(spec: str) -> list[str]:
944
- def postprocess(item):
946
+ def postprocess(item: str) -> list[str]:
945
947
  if _default_regex.match(item):
946
948
  default = item[:7]
947
949
  # can't be separated by anything other than a single space
@@ -962,7 +964,7 @@ def _tokenize_type_spec(spec: str) -> list[str]:
962
964
 
963
965
 
964
966
  def _token_type(token: str, location: str | None = None) -> str:
965
- def is_numeric(token):
967
+ def is_numeric(token: str) -> bool:
966
968
  try:
967
969
  # use complex to make sure every numeric value is detected as literal
968
970
  complex(token)
@@ -1026,7 +1028,7 @@ def _convert_numpy_type_spec(
1026
1028
  if translations is None:
1027
1029
  translations = {}
1028
1030
 
1029
- def convert_obj(obj, translations, default_translation):
1031
+ def convert_obj(obj: str, translations: dict[str, str], default_translation: str) -> str:
1030
1032
  translation = translations.get(obj, obj)
1031
1033
 
1032
1034
  # use :class: (the default) only if obj is not a standard singleton
@@ -1155,6 +1157,7 @@ class NumpyDocstring(GoogleDocstring):
1155
1157
  The lines of the docstring in a list.
1156
1158
 
1157
1159
  """
1160
+
1158
1161
  def __init__(
1159
1162
  self,
1160
1163
  docstring: str | list[str],
@@ -1180,13 +1183,13 @@ class NumpyDocstring(GoogleDocstring):
1180
1183
  elif filepath is None:
1181
1184
  filepath = ""
1182
1185
 
1183
- return ":".join([filepath, "docstring of %s" % name])
1186
+ return f"{filepath}:docstring of {name}"
1184
1187
 
1185
1188
  def _escape_args_and_kwargs(self, name: str) -> str:
1186
1189
  func = super()._escape_args_and_kwargs
1187
1190
 
1188
1191
  if ", " in name:
1189
- return ", ".join(func(param) for param in name.split(", "))
1192
+ return ", ".join(map(func, name.split(", ")))
1190
1193
  else:
1191
1194
  return func(name)
1192
1195
 
@@ -1233,7 +1236,7 @@ class NumpyDocstring(GoogleDocstring):
1233
1236
  line1, line2 = self._lines.get(0), self._lines.get(1)
1234
1237
  return (not self._lines or
1235
1238
  self._is_section_header() or
1236
- ['', ''] == [line1, line2] or
1239
+ (line1 == line2 == '') or
1237
1240
  (self._is_in_section and
1238
1241
  line1 and
1239
1242
  not self._is_indented(line1, self._section_indent)))
@@ -1269,7 +1272,7 @@ class NumpyDocstring(GoogleDocstring):
1269
1272
  func_name1, func_name2, :meth:`func_name`, func_name3
1270
1273
 
1271
1274
  """
1272
- items = []
1275
+ items: list[tuple[str, list[str], str | None]] = []
1273
1276
 
1274
1277
  def parse_item_name(text: str) -> tuple[str, str | None]:
1275
1278
  """Match ':role:`name`' or 'name'"""
@@ -1286,10 +1289,12 @@ class NumpyDocstring(GoogleDocstring):
1286
1289
  if not name:
1287
1290
  return
1288
1291
  name, role = parse_item_name(name)
1289
- items.append((name, list(rest), role))
1290
- del rest[:]
1292
+ items.append((name, rest.copy(), role))
1293
+ rest.clear()
1291
1294
 
1292
- def translate(func, description, role):
1295
+ def translate(
1296
+ func: str, description: list[str], role: str | None,
1297
+ ) -> tuple[str, list[str], str | None]:
1293
1298
  translations = self._config.napoleon_type_aliases
1294
1299
  if role is not None or not translations:
1295
1300
  return func, description, role
@@ -1336,10 +1341,7 @@ class NumpyDocstring(GoogleDocstring):
1336
1341
  return []
1337
1342
 
1338
1343
  # apply type aliases
1339
- items = [
1340
- translate(func, description, role)
1341
- for func, description, role in items
1342
- ]
1344
+ items = list(starmap(translate, items))
1343
1345
 
1344
1346
  lines: list[str] = []
1345
1347
  last_had_desc = True
sphinx/ext/todo.py CHANGED
@@ -7,7 +7,9 @@ with a backlink to the original location.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- from typing import TYPE_CHECKING, Any, cast
10
+ import functools
11
+ import operator
12
+ from typing import TYPE_CHECKING, Any, ClassVar, cast
11
13
 
12
14
  from docutils import nodes
13
15
  from docutils.parsers.rst import directives
@@ -26,7 +28,7 @@ if TYPE_CHECKING:
26
28
 
27
29
  from sphinx.application import Sphinx
28
30
  from sphinx.environment import BuildEnvironment
29
- from sphinx.util.typing import OptionSpec
31
+ from sphinx.util.typing import ExtensionMetadata, OptionSpec
30
32
  from sphinx.writers.html import HTML5Translator
31
33
  from sphinx.writers.latex import LaTeXTranslator
32
34
 
@@ -51,7 +53,7 @@ class Todo(BaseAdmonition, SphinxDirective):
51
53
  required_arguments = 0
52
54
  optional_arguments = 0
53
55
  final_argument_whitespace = False
54
- option_spec: OptionSpec = {
56
+ option_spec: ClassVar[OptionSpec] = {
55
57
  'class': directives.class_option,
56
58
  'name': directives.unchanged,
57
59
  }
@@ -85,7 +87,7 @@ class TodoDomain(Domain):
85
87
  def clear_doc(self, docname: str) -> None:
86
88
  self.todos.pop(docname, None)
87
89
 
88
- def merge_domaindata(self, docnames: list[str], otherdata: dict) -> None:
90
+ def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
89
91
  for docname in docnames:
90
92
  self.todos[docname] = otherdata['todos'][docname]
91
93
 
@@ -110,7 +112,7 @@ class TodoList(SphinxDirective):
110
112
  required_arguments = 0
111
113
  optional_arguments = 0
112
114
  final_argument_whitespace = False
113
- option_spec: OptionSpec = {}
115
+ option_spec: ClassVar[OptionSpec] = {}
114
116
 
115
117
  def run(self) -> list[Node]:
116
118
  # Simply insert an empty todolist node which will be replaced later
@@ -129,7 +131,8 @@ class TodoListProcessor:
129
131
  self.process(doctree, docname)
130
132
 
131
133
  def process(self, doctree: nodes.document, docname: str) -> None:
132
- todos: list[todo_node] = sum(self.domain.todos.values(), [])
134
+ todos: list[todo_node] = functools.reduce(
135
+ operator.iadd, self.domain.todos.values(), [])
133
136
  for node in list(doctree.findall(todolist)):
134
137
  if not self.config.todo_include_todos:
135
138
  node.parent.remove(node)
@@ -221,7 +224,7 @@ def latex_depart_todo_node(self: LaTeXTranslator, node: todo_node) -> None:
221
224
  self.body.append('\\end{sphinxadmonition}\n')
222
225
 
223
226
 
224
- def setup(app: Sphinx) -> dict[str, Any]:
227
+ def setup(app: Sphinx) -> ExtensionMetadata:
225
228
  app.add_event('todo-defined')
226
229
  app.add_config_value('todo_include_todos', False, 'html')
227
230
  app.add_config_value('todo_link_only', False, 'html')