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/parsers.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
  import docutils.parsers
8
8
  import docutils.parsers.rst
@@ -19,6 +19,7 @@ if TYPE_CHECKING:
19
19
  from sphinx.application import Sphinx
20
20
  from sphinx.config import Config
21
21
  from sphinx.environment import BuildEnvironment
22
+ from sphinx.util.typing import ExtensionMetadata
22
23
 
23
24
 
24
25
  class Parser(docutils.parsers.Parser):
@@ -65,13 +66,14 @@ class RSTParser(docutils.parsers.rst.Parser, Parser):
65
66
  self.statemachine = states.RSTStateMachine(
66
67
  state_classes=self.state_classes,
67
68
  initial_state=self.initial_state,
68
- debug=document.reporter.debug_flag)
69
+ debug=document.reporter.debug_flag,
70
+ )
69
71
 
70
72
  # preprocess inputstring
71
73
  if isinstance(inputstring, str):
72
74
  lines = docutils.statemachine.string2lines(
73
- inputstring, tab_width=document.settings.tab_width,
74
- convert_whitespace=True)
75
+ inputstring, tab_width=document.settings.tab_width, convert_whitespace=True
76
+ )
75
77
 
76
78
  inputlines = StringList(lines, document.current_source)
77
79
  else:
@@ -87,7 +89,7 @@ class RSTParser(docutils.parsers.rst.Parser, Parser):
87
89
  append_epilog(content, self.config.rst_epilog)
88
90
 
89
91
 
90
- def setup(app: Sphinx) -> dict[str, Any]:
92
+ def setup(app: Sphinx) -> ExtensionMetadata:
91
93
  app.add_source_parser(RSTParser)
92
94
 
93
95
  return {
sphinx/project.py CHANGED
@@ -28,7 +28,7 @@ class Project:
28
28
 
29
29
  #: source_suffix. Same as :confval:`source_suffix`.
30
30
  self.source_suffix = tuple(source_suffix)
31
- self._first_source_suffix = next(iter(self.source_suffix), "")
31
+ self._first_source_suffix = next(iter(self.source_suffix), '')
32
32
 
33
33
  #: The name of documents belonging to this project.
34
34
  self.docnames: set[str] = set()
@@ -43,12 +43,12 @@ class Project:
43
43
  self._path_to_docname = other._path_to_docname
44
44
  self._docname_to_path = other._docname_to_path
45
45
 
46
- def discover(self, exclude_paths: Iterable[str] = (),
47
- include_paths: Iterable[str] = ("**",)) -> set[str]:
46
+ def discover(
47
+ self, exclude_paths: Iterable[str] = (), include_paths: Iterable[str] = ('**',)
48
+ ) -> set[str]:
48
49
  """Find all document files in the source directory and put them in
49
50
  :attr:`docnames`.
50
51
  """
51
-
52
52
  self.docnames.clear()
53
53
  self._path_to_docname.clear()
54
54
  self._docname_to_path.clear()
@@ -56,23 +56,30 @@ class Project:
56
56
  for filename in get_matching_files(
57
57
  self.srcdir,
58
58
  include_paths,
59
- [*exclude_paths] + EXCLUDE_PATHS,
59
+ [*exclude_paths, *EXCLUDE_PATHS],
60
60
  ):
61
61
  if docname := self.path2doc(filename):
62
62
  if docname in self.docnames:
63
63
  pattern = os.path.join(self.srcdir, docname) + '.*'
64
64
  files = [relpath(f, self.srcdir) for f in glob(pattern)]
65
- logger.warning(__('multiple files found for the document "%s": %r\n'
66
- 'Use %r for the build.'),
67
- docname, files, self.doc2path(docname, absolute=True),
68
- once=True)
65
+ logger.warning(
66
+ __(
67
+ 'multiple files found for the document "%s": %r\n'
68
+ 'Use %r for the build.'
69
+ ),
70
+ docname,
71
+ files,
72
+ self.doc2path(docname, absolute=True),
73
+ once=True,
74
+ )
69
75
  elif os.access(os.path.join(self.srcdir, filename), os.R_OK):
70
76
  self.docnames.add(docname)
71
77
  self._path_to_docname[filename] = docname
72
78
  self._docname_to_path[docname] = filename
73
79
  else:
74
- logger.warning(__("Ignored unreadable document %r."),
75
- filename, location=docname)
80
+ logger.warning(
81
+ __('Ignored unreadable document %r.'), filename, location=docname
82
+ )
76
83
 
77
84
  return self.docnames
78
85
 
sphinx/pycode/__init__.py CHANGED
@@ -69,12 +69,13 @@ class ModuleAnalyzer:
69
69
  return filename, None
70
70
 
71
71
  @classmethod
72
- def for_string(cls, string: str, modname: str, srcname: str = '<string>',
73
- ) -> ModuleAnalyzer:
72
+ def for_string(
73
+ cls: type[ModuleAnalyzer], string: str, modname: str, srcname: str = '<string>',
74
+ ) -> ModuleAnalyzer:
74
75
  return cls(string, modname, srcname)
75
76
 
76
77
  @classmethod
77
- def for_file(cls, filename: str, modname: str) -> ModuleAnalyzer:
78
+ def for_file(cls: type[ModuleAnalyzer], filename: str, modname: str) -> ModuleAnalyzer:
78
79
  if ('file', filename) in cls.cache:
79
80
  return cls.cache['file', filename]
80
81
  try:
@@ -87,7 +88,7 @@ class ModuleAnalyzer:
87
88
  return obj
88
89
 
89
90
  @classmethod
90
- def for_module(cls, modname: str) -> ModuleAnalyzer:
91
+ def for_module(cls: type[ModuleAnalyzer], modname: str) -> ModuleAnalyzer:
91
92
  if ('module', modname) in cls.cache:
92
93
  entry = cls.cache['module', modname]
93
94
  if isinstance(entry, PycodeError):
@@ -127,7 +128,7 @@ class ModuleAnalyzer:
127
128
  self.attr_docs = {}
128
129
  for (scope, comment) in parser.comments.items():
129
130
  if comment:
130
- self.attr_docs[scope] = comment.splitlines() + ['']
131
+ self.attr_docs[scope] = [*comment.splitlines(), '']
131
132
  else:
132
133
  self.attr_docs[scope] = ['']
133
134
 
sphinx/pycode/ast.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import ast
6
- from typing import overload
6
+ from typing import NoReturn, overload
7
7
 
8
8
  OPERATORS: dict[type[ast.AST], str] = {
9
9
  ast.Add: "+",
@@ -85,9 +85,8 @@ class _UnparseVisitor(ast.NodeVisitor):
85
85
  for _ in range(len(kw_defaults), len(node.kwonlyargs)):
86
86
  kw_defaults.insert(0, None)
87
87
 
88
- args: list[str] = []
89
- for i, arg in enumerate(node.posonlyargs):
90
- args.append(self._visit_arg_with_default(arg, defaults[i]))
88
+ args: list[str] = [self._visit_arg_with_default(arg, defaults[i])
89
+ for i, arg in enumerate(node.posonlyargs)]
91
90
 
92
91
  if node.posonlyargs:
93
92
  args.append('/')
@@ -115,15 +114,17 @@ class _UnparseVisitor(ast.NodeVisitor):
115
114
  # Special case ``**`` to not have surrounding spaces.
116
115
  if isinstance(node.op, ast.Pow):
117
116
  return "".join(map(self.visit, (node.left, node.op, node.right)))
118
- return " ".join(self.visit(e) for e in [node.left, node.op, node.right])
117
+ return " ".join(map(self.visit, (node.left, node.op, node.right)))
119
118
 
120
119
  def visit_BoolOp(self, node: ast.BoolOp) -> str:
121
120
  op = " %s " % self.visit(node.op)
122
121
  return op.join(self.visit(e) for e in node.values)
123
122
 
124
123
  def visit_Call(self, node: ast.Call) -> str:
125
- args = ', '.join([self.visit(e) for e in node.args]
126
- + [f"{k.arg}={self.visit(k.value)}" for k in node.keywords])
124
+ args = ', '.join(
125
+ [self.visit(e) for e in node.args]
126
+ + [f"{k.arg}={self.visit(k.value)}" for k in node.keywords],
127
+ )
127
128
  return f"{self.visit(node.func)}({args})"
128
129
 
129
130
  def visit_Constant(self, node: ast.Constant) -> str:
@@ -155,6 +156,20 @@ class _UnparseVisitor(ast.NodeVisitor):
155
156
  def visit_Set(self, node: ast.Set) -> str:
156
157
  return "{" + ", ".join(self.visit(e) for e in node.elts) + "}"
157
158
 
159
+ def visit_Slice(self, node: ast.Slice) -> str:
160
+ if not node.lower and not node.upper and not node.step:
161
+ # Empty slice with default values -> [:]
162
+ return ":"
163
+
164
+ start = self.visit(node.lower) if node.lower else ""
165
+ stop = self.visit(node.upper) if node.upper else ""
166
+ if not node.step:
167
+ # Default step size -> [start:stop]
168
+ return f"{start}:{stop}"
169
+
170
+ step = self.visit(node.step) if node.step else ""
171
+ return f"{start}:{stop}:{step}"
172
+
158
173
  def visit_Subscript(self, node: ast.Subscript) -> str:
159
174
  def is_simple_tuple(value: ast.expr) -> bool:
160
175
  return (
@@ -184,5 +199,5 @@ class _UnparseVisitor(ast.NodeVisitor):
184
199
  else:
185
200
  return "(" + ", ".join(self.visit(e) for e in node.elts) + ")"
186
201
 
187
- def generic_visit(self, node):
202
+ def generic_visit(self, node: ast.AST) -> NoReturn:
188
203
  raise NotImplementedError('Unable to parse %s object' % type(node).__name__)
sphinx/pycode/parser.py CHANGED
@@ -4,8 +4,10 @@ from __future__ import annotations
4
4
 
5
5
  import ast
6
6
  import contextlib
7
+ import functools
7
8
  import inspect
8
9
  import itertools
10
+ import operator
9
11
  import re
10
12
  import tokenize
11
13
  from inspect import Signature
@@ -243,7 +245,7 @@ class VariableCommentPicker(ast.NodeVisitor):
243
245
  else:
244
246
  return None
245
247
  else:
246
- return self.context + [name]
248
+ return [*self.context, name]
247
249
 
248
250
  def add_entry(self, name: str) -> None:
249
251
  qualname = self.get_qualname_for(name)
@@ -350,9 +352,8 @@ class VariableCommentPicker(ast.NodeVisitor):
350
352
  """Handles Assign node and pick up a variable comment."""
351
353
  try:
352
354
  targets = get_assign_targets(node)
353
- varnames: list[str] = sum(
354
- [get_lvar_names(t, self=self.get_self()) for t in targets], [],
355
- )
355
+ varnames: list[str] = functools.reduce(
356
+ operator.iadd, [get_lvar_names(t, self=self.get_self()) for t in targets], [])
356
357
  current_line = self.get_line(node.lineno)
357
358
  except TypeError:
358
359
  return # this assignment is not new definition!
@@ -476,7 +477,7 @@ class DefinitionFinder(TokenProcessor):
476
477
 
477
478
  def add_definition(self, name: str, entry: tuple[str, int, int]) -> None:
478
479
  """Add a location of definition."""
479
- if self.indents and self.indents[-1][0] == 'def' and entry[0] == 'def':
480
+ if self.indents and self.indents[-1][0] == entry[0] == 'def':
480
481
  # ignore definition of inner function
481
482
  pass
482
483
  else:
sphinx/registry.py CHANGED
@@ -39,7 +39,12 @@ if TYPE_CHECKING:
39
39
  from sphinx.config import Config
40
40
  from sphinx.environment import BuildEnvironment
41
41
  from sphinx.ext.autodoc import Documenter
42
- from sphinx.util.typing import RoleFunction, TitleGetter
42
+ from sphinx.util.typing import (
43
+ ExtensionMetadata,
44
+ RoleFunction,
45
+ TitleGetter,
46
+ _ExtensionSetupFunc,
47
+ )
43
48
 
44
49
  logger = logging.getLogger(__name__)
45
50
 
@@ -47,6 +52,7 @@ logger = logging.getLogger(__name__)
47
52
  # Values are Sphinx version that merge the extension.
48
53
  EXTENSION_BLACKLIST = {
49
54
  "sphinxjp.themecore": "1.2",
55
+ "sphinxprettysearchresults": "2.0.0",
50
56
  }
51
57
 
52
58
 
@@ -55,7 +61,7 @@ class SphinxComponentRegistry:
55
61
  #: special attrgetter for autodoc; class object -> attrgetter
56
62
  self.autodoc_attrgettrs: dict[type, Callable[[Any, str, Any], Any]] = {}
57
63
 
58
- #: builders; a dict of builder name -> bulider class
64
+ #: builders; a dict of builder name -> builder class
59
65
  self.builders: dict[str, type[Builder]] = {}
60
66
 
61
67
  #: autodoc documenters; a dict of documenter name -> documenter class
@@ -450,11 +456,11 @@ class SphinxComponentRegistry:
450
456
  raise ExtensionError(__('Could not import extension %s') % extname,
451
457
  err) from err
452
458
 
453
- setup = getattr(mod, 'setup', None)
459
+ setup: _ExtensionSetupFunc | None = getattr(mod, 'setup', None)
454
460
  if setup is None:
455
461
  logger.warning(__('extension %r has no setup() function; is it really '
456
462
  'a Sphinx extension module?'), extname)
457
- metadata: dict[str, Any] = {}
463
+ metadata: ExtensionMetadata = {}
458
464
  else:
459
465
  try:
460
466
  metadata = setup(app)
@@ -476,7 +482,7 @@ class SphinxComponentRegistry:
476
482
 
477
483
  app.extensions[extname] = Extension(extname, mod, **metadata)
478
484
 
479
- def get_envversion(self, app: Sphinx) -> dict[str, str]:
485
+ def get_envversion(self, app: Sphinx) -> dict[str, int]:
480
486
  from sphinx.environment import ENV_VERSION
481
487
  envversion = {ext.name: ext.metadata['env_version'] for ext in app.extensions.values()
482
488
  if ext.metadata.get('env_version')}
@@ -507,7 +513,7 @@ def merge_source_suffix(app: Sphinx, config: Config) -> None:
507
513
  app.registry.source_suffix = app.config.source_suffix
508
514
 
509
515
 
510
- def setup(app: Sphinx) -> dict[str, Any]:
516
+ def setup(app: Sphinx) -> ExtensionMetadata:
511
517
  app.connect('config-inited', merge_source_suffix, priority=800)
512
518
 
513
519
  return {
sphinx/roles.py CHANGED
@@ -22,7 +22,7 @@ if TYPE_CHECKING:
22
22
 
23
23
  from sphinx.application import Sphinx
24
24
  from sphinx.environment import BuildEnvironment
25
- from sphinx.util.typing import RoleFunction
25
+ from sphinx.util.typing import ExtensionMetadata, RoleFunction
26
26
 
27
27
 
28
28
  generic_docroles = {
@@ -31,7 +31,6 @@ generic_docroles = {
31
31
  'kbd': nodes.literal,
32
32
  'mailheader': addnodes.literal_emphasis,
33
33
  'makevar': addnodes.literal_strong,
34
- 'manpage': addnodes.manpage,
35
34
  'mimetype': addnodes.literal_emphasis,
36
35
  'newsgroup': addnodes.literal_emphasis,
37
36
  'program': addnodes.literal_strong, # XXX should be an x-ref
@@ -41,6 +40,7 @@ generic_docroles = {
41
40
 
42
41
  # -- generic cross-reference role ----------------------------------------------
43
42
 
43
+
44
44
  class XRefRole(ReferenceRole):
45
45
  """
46
46
  A generic cross-referencing role. To create a callable that can be used as
@@ -68,10 +68,14 @@ class XRefRole(ReferenceRole):
68
68
  nodeclass: type[Element] = addnodes.pending_xref
69
69
  innernodeclass: type[TextElement] = nodes.literal
70
70
 
71
- def __init__(self, fix_parens: bool = False, lowercase: bool = False,
72
- nodeclass: type[Element] | None = None,
73
- innernodeclass: type[TextElement] | None = None,
74
- warn_dangling: bool = False) -> None:
71
+ def __init__(
72
+ self,
73
+ fix_parens: bool = False,
74
+ lowercase: bool = False,
75
+ nodeclass: type[Element] | None = None,
76
+ innernodeclass: type[TextElement] | None = None,
77
+ warn_dangling: bool = False,
78
+ ) -> None:
75
79
  self.fix_parens = fix_parens
76
80
  self.lowercase = lowercase
77
81
  self.warn_dangling = warn_dangling
@@ -112,7 +116,7 @@ class XRefRole(ReferenceRole):
112
116
  text = utils.unescape(self.text[1:])
113
117
  if self.fix_parens:
114
118
  self.has_explicit_title = False # treat as implicit
115
- text, target = self.update_title_and_target(text, "")
119
+ text, target = self.update_title_and_target(text, '')
116
120
 
117
121
  node = self.innernodeclass(self.rawtext, text, classes=self.classes)
118
122
  return self.result_nodes(self.inliner.document, self.env, node, is_ref=False)
@@ -126,17 +130,20 @@ class XRefRole(ReferenceRole):
126
130
  title, target = self.update_title_and_target(title, target)
127
131
 
128
132
  # create the reference node
129
- options = {'refdoc': self.env.docname,
130
- 'refdomain': self.refdomain,
131
- 'reftype': self.reftype,
132
- 'refexplicit': self.has_explicit_title,
133
- 'refwarn': self.warn_dangling}
133
+ options = {
134
+ 'refdoc': self.env.docname,
135
+ 'refdomain': self.refdomain,
136
+ 'reftype': self.reftype,
137
+ 'refexplicit': self.has_explicit_title,
138
+ 'refwarn': self.warn_dangling,
139
+ }
134
140
  refnode = self.nodeclass(self.rawtext, **options)
135
141
  self.set_source_info(refnode)
136
142
 
137
143
  # determine the target and title for the class
138
- title, target = self.process_link(self.env, refnode, self.has_explicit_title,
139
- title, target)
144
+ title, target = self.process_link(
145
+ self.env, refnode, self.has_explicit_title, title, target
146
+ )
140
147
  refnode['reftarget'] = target
141
148
  refnode += self.innernodeclass(self.rawtext, title, classes=self.classes)
142
149
 
@@ -144,8 +151,14 @@ class XRefRole(ReferenceRole):
144
151
 
145
152
  # methods that can be overwritten
146
153
 
147
- def process_link(self, env: BuildEnvironment, refnode: Element, has_explicit_title: bool,
148
- title: str, target: str) -> tuple[str, str]:
154
+ def process_link(
155
+ self,
156
+ env: BuildEnvironment,
157
+ refnode: Element,
158
+ has_explicit_title: bool,
159
+ title: str,
160
+ target: str,
161
+ ) -> tuple[str, str]:
149
162
  """Called after parsing title and target text, and creating the
150
163
  reference node (given in *refnode*). This method can alter the
151
164
  reference node and must return a new (or the same) ``(title, target)``
@@ -153,8 +166,9 @@ class XRefRole(ReferenceRole):
153
166
  """
154
167
  return title, ws_re.sub(' ', target)
155
168
 
156
- def result_nodes(self, document: nodes.document, env: BuildEnvironment, node: Element,
157
- is_ref: bool) -> tuple[list[Node], list[system_message]]:
169
+ def result_nodes(
170
+ self, document: nodes.document, env: BuildEnvironment, node: Element, is_ref: bool
171
+ ) -> tuple[list[Node], list[system_message]]:
158
172
  """Called before returning the finished nodes. *node* is the reference
159
173
  node if one was created (*is_ref* is then true), else the content node.
160
174
  This method can add other nodes and must return a ``(nodes, messages)``
@@ -164,8 +178,14 @@ class XRefRole(ReferenceRole):
164
178
 
165
179
 
166
180
  class AnyXRefRole(XRefRole):
167
- def process_link(self, env: BuildEnvironment, refnode: Element, has_explicit_title: bool,
168
- title: str, target: str) -> tuple[str, str]:
181
+ def process_link(
182
+ self,
183
+ env: BuildEnvironment,
184
+ refnode: Element,
185
+ has_explicit_title: bool,
186
+ title: str,
187
+ target: str,
188
+ ) -> tuple[str, str]:
169
189
  result = super().process_link(env, refnode, has_explicit_title, title, target)
170
190
  # add all possible context info (i.e. std:program, py:module etc.)
171
191
  refnode.attributes.update(env.ref_context)
@@ -175,8 +195,15 @@ class AnyXRefRole(XRefRole):
175
195
  class PEP(ReferenceRole):
176
196
  def run(self) -> tuple[list[Node], list[system_message]]:
177
197
  target_id = 'index-%s' % self.env.new_serialno('index')
178
- entries = [('single', _('Python Enhancement Proposals; PEP %s') % self.target,
179
- target_id, '', None)]
198
+ entries = [
199
+ (
200
+ 'single',
201
+ _('Python Enhancement Proposals; PEP %s') % self.target,
202
+ target_id,
203
+ '',
204
+ None,
205
+ )
206
+ ]
180
207
 
181
208
  index = addnodes.index(entries=entries)
182
209
  target = nodes.target('', '', ids=[target_id])
@@ -188,11 +215,12 @@ class PEP(ReferenceRole):
188
215
  if self.has_explicit_title:
189
216
  reference += nodes.strong(self.title, self.title)
190
217
  else:
191
- title = "PEP " + self.title
218
+ title = 'PEP ' + self.title
192
219
  reference += nodes.strong(title, title)
193
220
  except ValueError:
194
- msg = self.inliner.reporter.error(__('invalid PEP number %s') % self.target,
195
- line=self.lineno)
221
+ msg = self.inliner.reporter.error(
222
+ __('invalid PEP number %s') % self.target, line=self.lineno
223
+ )
196
224
  prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
197
225
  return [prb], [msg]
198
226
 
@@ -222,11 +250,12 @@ class RFC(ReferenceRole):
222
250
  if self.has_explicit_title:
223
251
  reference += nodes.strong(self.title, self.title)
224
252
  else:
225
- title = "RFC " + self.title
253
+ title = 'RFC ' + self.title
226
254
  reference += nodes.strong(title, title)
227
255
  except ValueError:
228
- msg = self.inliner.reporter.error(__('invalid RFC number %s') % self.target,
229
- line=self.lineno)
256
+ msg = self.inliner.reporter.error(
257
+ __('invalid RFC number %s') % self.target, line=self.lineno
258
+ )
230
259
  prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
231
260
  return [prb], [msg]
232
261
 
@@ -241,9 +270,6 @@ class RFC(ReferenceRole):
241
270
  return base_url + self.inliner.rfc_url % int(ret[0])
242
271
 
243
272
 
244
- _amp_re = re.compile(r'(?<!&)&(?![&\s])')
245
-
246
-
247
273
  class GUILabel(SphinxRole):
248
274
  amp_re = re.compile(r'(?<!&)&(?![&\s])')
249
275
 
@@ -270,17 +296,14 @@ class MenuSelection(GUILabel):
270
296
  return super().run()
271
297
 
272
298
 
273
- _litvar_re = re.compile('{([^}]+)}')
274
- parens_re = re.compile(r'(\\*{|\\*})')
275
-
276
-
277
299
  class EmphasizedLiteral(SphinxRole):
278
300
  parens_re = re.compile(r'(\\\\|\\{|\\}|{|})')
279
301
 
280
302
  def run(self) -> tuple[list[Node], list[system_message]]:
281
303
  children = self.parse(self.text)
282
- node = nodes.literal(self.rawtext, '', *children,
283
- role=self.name.lower(), classes=[self.name])
304
+ node = nodes.literal(
305
+ self.rawtext, '', *children, role=self.name.lower(), classes=[self.name]
306
+ )
284
307
 
285
308
  return [node], []
286
309
 
@@ -292,14 +315,13 @@ class EmphasizedLiteral(SphinxRole):
292
315
  if part == '\\\\': # escaped backslash
293
316
  stack[-1] += '\\'
294
317
  elif part == '{':
295
- if len(stack) >= 2 and stack[-2] == "{": # nested
296
- stack[-1] += "{"
318
+ if len(stack) >= 2 and stack[-2] == '{': # nested
319
+ stack[-1] += '{'
297
320
  else:
298
321
  # start emphasis
299
- stack.append('{')
300
- stack.append('')
322
+ stack.extend(('{', ''))
301
323
  elif part == '}':
302
- if len(stack) == 3 and stack[1] == "{" and len(stack[2]) > 0:
324
+ if len(stack) == 3 and stack[1] == '{' and len(stack[2]) > 0:
303
325
  # emphasized word found
304
326
  if stack[0]:
305
327
  result.append(nodes.Text(stack[0]))
@@ -324,17 +346,14 @@ class EmphasizedLiteral(SphinxRole):
324
346
  return result
325
347
 
326
348
 
327
- _abbr_re = re.compile(r'\((.*)\)$', re.S)
328
-
329
-
330
349
  class Abbreviation(SphinxRole):
331
- abbr_re = re.compile(r'\((.*)\)$', re.S)
350
+ abbr_re = re.compile(r'\((.*)\)$', re.DOTALL)
332
351
 
333
352
  def run(self) -> tuple[list[Node], list[system_message]]:
334
353
  options = self.options.copy()
335
354
  matched = self.abbr_re.search(self.text)
336
355
  if matched:
337
- text = self.text[:matched.start()].strip()
356
+ text = self.text[: matched.start()].strip()
338
357
  options['explanation'] = matched.group(1)
339
358
  else:
340
359
  text = self.text
@@ -342,6 +361,28 @@ class Abbreviation(SphinxRole):
342
361
  return [nodes.abbreviation(self.rawtext, text, **options)], []
343
362
 
344
363
 
364
+ class Manpage(ReferenceRole):
365
+ _manpage_re = re.compile(r'^(?P<path>(?P<page>.+)[(.](?P<section>[1-9]\w*)?\)?)$')
366
+
367
+ def run(self) -> tuple[list[Node], list[system_message]]:
368
+ manpage = ws_re.sub(' ', self.target)
369
+ if m := self._manpage_re.match(manpage):
370
+ info = m.groupdict()
371
+ else:
372
+ info = {'path': manpage, 'page': manpage, 'section': ''}
373
+
374
+ inner: nodes.Node
375
+ text = self.title[1:] if self.disabled else self.title
376
+ if not self.disabled and self.config.manpages_url:
377
+ uri = self.config.manpages_url.format(**info)
378
+ inner = nodes.reference('', text, classes=[self.name], refuri=uri)
379
+ else:
380
+ inner = nodes.Text(text)
381
+ node = addnodes.manpage(self.rawtext, '', inner, classes=[self.name], **info)
382
+
383
+ return [node], []
384
+
385
+
345
386
  # Sphinx provides the `code-block` directive for highlighting code blocks.
346
387
  # Docutils provides the `code` role which in theory can be used similarly by
347
388
  # defining a custom role for a given programming language:
@@ -366,10 +407,15 @@ class Abbreviation(SphinxRole):
366
407
  # way as the Sphinx `code-block` directive.
367
408
  #
368
409
  # TODO: Change to use `SphinxRole` once SphinxRole is fixed to support options.
369
- def code_role(name: str, rawtext: str, text: str, lineno: int,
370
- inliner: docutils.parsers.rst.states.Inliner,
371
- options: dict | None = None, content: Sequence[str] = (),
372
- ) -> tuple[list[Node], list[system_message]]:
410
+ def code_role(
411
+ name: str,
412
+ rawtext: str,
413
+ text: str,
414
+ lineno: int,
415
+ inliner: docutils.parsers.rst.states.Inliner,
416
+ options: dict[str, Any] | None = None,
417
+ content: Sequence[str] = (),
418
+ ) -> tuple[list[Node], list[system_message]]:
373
419
  if options is None:
374
420
  options = {}
375
421
  options = options.copy()
@@ -400,7 +446,6 @@ specific_docroles: dict[str, RoleFunction] = {
400
446
  'download': XRefRole(nodeclass=addnodes.download_reference),
401
447
  # links to anything
402
448
  'any': AnyXRefRole(warn_dangling=True),
403
-
404
449
  'pep': PEP(),
405
450
  'rfc': RFC(),
406
451
  'guilabel': GUILabel(),
@@ -408,23 +453,24 @@ specific_docroles: dict[str, RoleFunction] = {
408
453
  'file': EmphasizedLiteral(),
409
454
  'samp': EmphasizedLiteral(),
410
455
  'abbr': Abbreviation(),
456
+ 'manpage': Manpage(),
411
457
  }
412
458
 
413
459
 
414
- def setup(app: Sphinx) -> dict[str, Any]:
460
+ def setup(app: Sphinx) -> ExtensionMetadata:
415
461
  from docutils.parsers.rst import roles
416
462
 
417
463
  for rolename, nodeclass in generic_docroles.items():
418
464
  generic = roles.GenericRole(rolename, nodeclass)
419
- role = roles.CustomRole(rolename, generic, {'classes': [rolename]})
420
- roles.register_local_role(rolename, role)
465
+ role = roles.CustomRole(rolename, generic, {'classes': [rolename]}) # type: ignore[arg-type]
466
+ roles.register_local_role(rolename, role) # type: ignore[arg-type]
421
467
 
422
468
  for rolename, func in specific_docroles.items():
423
- roles.register_local_role(rolename, func)
469
+ roles.register_local_role(rolename, func) # type: ignore[arg-type]
424
470
 
425
471
  # Since docutils registers it as a canonical role, override it as a
426
472
  # canonical role as well.
427
- roles.register_canonical_role('code', code_role)
473
+ roles.register_canonical_role('code', code_role) # type: ignore[arg-type]
428
474
 
429
475
  return {
430
476
  'version': 'builtin',