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
@@ -0,0 +1,890 @@
1
+ """The Python domain."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import builtins
6
+ import inspect
7
+ import typing
8
+ from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple, cast
9
+
10
+ from docutils import nodes
11
+ from docutils.parsers.rst import directives
12
+
13
+ from sphinx import addnodes
14
+ from sphinx.domains import Domain, Index, IndexEntry, ObjType
15
+ from sphinx.domains.python._annotations import _parse_annotation
16
+ from sphinx.domains.python._object import PyObject
17
+ from sphinx.locale import _, __
18
+ from sphinx.roles import XRefRole
19
+ from sphinx.util import logging
20
+ from sphinx.util.docutils import SphinxDirective
21
+ from sphinx.util.nodes import (
22
+ find_pending_xref_condition,
23
+ make_id,
24
+ make_refnode,
25
+ nested_parse_with_titles,
26
+ )
27
+
28
+ if TYPE_CHECKING:
29
+ from collections.abc import Iterable, Iterator
30
+
31
+ from docutils.nodes import Element, Node
32
+
33
+ from sphinx.addnodes import desc_signature, pending_xref
34
+ from sphinx.application import Sphinx
35
+ from sphinx.builders import Builder
36
+ from sphinx.environment import BuildEnvironment
37
+ from sphinx.util.typing import ExtensionMetadata, OptionSpec
38
+
39
+ logger = logging.getLogger(__name__)
40
+
41
+ pairindextypes = {
42
+ 'module': 'module',
43
+ 'keyword': 'keyword',
44
+ 'operator': 'operator',
45
+ 'object': 'object',
46
+ 'exception': 'exception',
47
+ 'statement': 'statement',
48
+ 'builtin': 'built-in function',
49
+ }
50
+
51
+
52
+ class ObjectEntry(NamedTuple):
53
+ docname: str
54
+ node_id: str
55
+ objtype: str
56
+ aliased: bool
57
+
58
+
59
+ class ModuleEntry(NamedTuple):
60
+ docname: str
61
+ node_id: str
62
+ synopsis: str
63
+ platform: str
64
+ deprecated: bool
65
+
66
+
67
+ class PyFunction(PyObject):
68
+ """Description of a function."""
69
+
70
+ option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy() # noqa: F821
71
+ option_spec.update({
72
+ 'async': directives.flag,
73
+ })
74
+
75
+ def get_signature_prefix(self, sig: str) -> list[nodes.Node]:
76
+ if 'async' in self.options:
77
+ return [addnodes.desc_sig_keyword('', 'async'),
78
+ addnodes.desc_sig_space()]
79
+ else:
80
+ return []
81
+
82
+ def needs_arglist(self) -> bool:
83
+ return True
84
+
85
+ def add_target_and_index(self, name_cls: tuple[str, str], sig: str,
86
+ signode: desc_signature) -> None:
87
+ super().add_target_and_index(name_cls, sig, signode)
88
+ if 'no-index-entry' not in self.options:
89
+ modname = self.options.get('module', self.env.ref_context.get('py:module'))
90
+ node_id = signode['ids'][0]
91
+
92
+ name, cls = name_cls
93
+ if modname:
94
+ text = _('%s() (in module %s)') % (name, modname)
95
+ self.indexnode['entries'].append(('single', text, node_id, '', None))
96
+ else:
97
+ text = f'built-in function; {name}()'
98
+ self.indexnode['entries'].append(('pair', text, node_id, '', None))
99
+
100
+ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str:
101
+ # add index in own add_target_and_index() instead.
102
+ return ''
103
+
104
+
105
+ class PyDecoratorFunction(PyFunction):
106
+ """Description of a decorator."""
107
+
108
+ def run(self) -> list[Node]:
109
+ # a decorator function is a function after all
110
+ self.name = 'py:function'
111
+ return super().run()
112
+
113
+ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str]:
114
+ ret = super().handle_signature(sig, signode)
115
+ signode.insert(0, addnodes.desc_addname('@', '@'))
116
+ return ret
117
+
118
+ def needs_arglist(self) -> bool:
119
+ return False
120
+
121
+
122
+ class PyVariable(PyObject):
123
+ """Description of a variable."""
124
+
125
+ option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy()
126
+ option_spec.update({
127
+ 'type': directives.unchanged,
128
+ 'value': directives.unchanged,
129
+ })
130
+
131
+ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str]:
132
+ fullname, prefix = super().handle_signature(sig, signode)
133
+
134
+ typ = self.options.get('type')
135
+ if typ:
136
+ annotations = _parse_annotation(typ, self.env)
137
+ signode += addnodes.desc_annotation(typ, '',
138
+ addnodes.desc_sig_punctuation('', ':'),
139
+ addnodes.desc_sig_space(), *annotations)
140
+
141
+ value = self.options.get('value')
142
+ if value:
143
+ signode += addnodes.desc_annotation(value, '',
144
+ addnodes.desc_sig_space(),
145
+ addnodes.desc_sig_punctuation('', '='),
146
+ addnodes.desc_sig_space(),
147
+ nodes.Text(value))
148
+
149
+ return fullname, prefix
150
+
151
+ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str:
152
+ name, cls = name_cls
153
+ if modname:
154
+ return _('%s (in module %s)') % (name, modname)
155
+ else:
156
+ return _('%s (built-in variable)') % name
157
+
158
+
159
+ class PyClasslike(PyObject):
160
+ """
161
+ Description of a class-like object (classes, interfaces, exceptions).
162
+ """
163
+
164
+ option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy()
165
+ option_spec.update({
166
+ 'final': directives.flag,
167
+ })
168
+
169
+ allow_nesting = True
170
+
171
+ def get_signature_prefix(self, sig: str) -> list[nodes.Node]:
172
+ if 'final' in self.options:
173
+ return [nodes.Text('final'), addnodes.desc_sig_space(),
174
+ nodes.Text(self.objtype), addnodes.desc_sig_space()]
175
+ else:
176
+ return [nodes.Text(self.objtype), addnodes.desc_sig_space()]
177
+
178
+ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str:
179
+ if self.objtype == 'class':
180
+ if not modname:
181
+ return _('%s (built-in class)') % name_cls[0]
182
+ return _('%s (class in %s)') % (name_cls[0], modname)
183
+ elif self.objtype == 'exception':
184
+ return name_cls[0]
185
+ else:
186
+ return ''
187
+
188
+
189
+ class PyMethod(PyObject):
190
+ """Description of a method."""
191
+
192
+ option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy()
193
+ option_spec.update({
194
+ 'abstractmethod': directives.flag,
195
+ 'async': directives.flag,
196
+ 'classmethod': directives.flag,
197
+ 'final': directives.flag,
198
+ 'staticmethod': directives.flag,
199
+ })
200
+
201
+ def needs_arglist(self) -> bool:
202
+ return True
203
+
204
+ def get_signature_prefix(self, sig: str) -> list[nodes.Node]:
205
+ prefix: list[nodes.Node] = []
206
+ if 'final' in self.options:
207
+ prefix.append(nodes.Text('final'))
208
+ prefix.append(addnodes.desc_sig_space())
209
+ if 'abstractmethod' in self.options:
210
+ prefix.append(nodes.Text('abstract'))
211
+ prefix.append(addnodes.desc_sig_space())
212
+ if 'async' in self.options:
213
+ prefix.append(nodes.Text('async'))
214
+ prefix.append(addnodes.desc_sig_space())
215
+ if 'classmethod' in self.options:
216
+ prefix.append(nodes.Text('classmethod'))
217
+ prefix.append(addnodes.desc_sig_space())
218
+ if 'staticmethod' in self.options:
219
+ prefix.append(nodes.Text('static'))
220
+ prefix.append(addnodes.desc_sig_space())
221
+ return prefix
222
+
223
+ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str:
224
+ name, cls = name_cls
225
+ try:
226
+ clsname, methname = name.rsplit('.', 1)
227
+ if modname and self.env.config.add_module_names:
228
+ clsname = f'{modname}.{clsname}'
229
+ except ValueError:
230
+ if modname:
231
+ return _('%s() (in module %s)') % (name, modname)
232
+ else:
233
+ return '%s()' % name
234
+
235
+ if 'classmethod' in self.options:
236
+ return _('%s() (%s class method)') % (methname, clsname)
237
+ elif 'staticmethod' in self.options:
238
+ return _('%s() (%s static method)') % (methname, clsname)
239
+ else:
240
+ return _('%s() (%s method)') % (methname, clsname)
241
+
242
+
243
+ class PyClassMethod(PyMethod):
244
+ """Description of a classmethod."""
245
+
246
+ option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy()
247
+
248
+ def run(self) -> list[Node]:
249
+ self.name = 'py:method'
250
+ self.options['classmethod'] = True
251
+
252
+ return super().run()
253
+
254
+
255
+ class PyStaticMethod(PyMethod):
256
+ """Description of a staticmethod."""
257
+
258
+ option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy()
259
+
260
+ def run(self) -> list[Node]:
261
+ self.name = 'py:method'
262
+ self.options['staticmethod'] = True
263
+
264
+ return super().run()
265
+
266
+
267
+ class PyDecoratorMethod(PyMethod):
268
+ """Description of a decoratormethod."""
269
+
270
+ def run(self) -> list[Node]:
271
+ self.name = 'py:method'
272
+ return super().run()
273
+
274
+ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str]:
275
+ ret = super().handle_signature(sig, signode)
276
+ signode.insert(0, addnodes.desc_addname('@', '@'))
277
+ return ret
278
+
279
+ def needs_arglist(self) -> bool:
280
+ return False
281
+
282
+
283
+ class PyAttribute(PyObject):
284
+ """Description of an attribute."""
285
+
286
+ option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy()
287
+ option_spec.update({
288
+ 'type': directives.unchanged,
289
+ 'value': directives.unchanged,
290
+ })
291
+
292
+ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str]:
293
+ fullname, prefix = super().handle_signature(sig, signode)
294
+
295
+ typ = self.options.get('type')
296
+ if typ:
297
+ annotations = _parse_annotation(typ, self.env)
298
+ signode += addnodes.desc_annotation(typ, '',
299
+ addnodes.desc_sig_punctuation('', ':'),
300
+ addnodes.desc_sig_space(),
301
+ *annotations)
302
+
303
+ value = self.options.get('value')
304
+ if value:
305
+ signode += addnodes.desc_annotation(value, '',
306
+ addnodes.desc_sig_space(),
307
+ addnodes.desc_sig_punctuation('', '='),
308
+ addnodes.desc_sig_space(),
309
+ nodes.Text(value))
310
+
311
+ return fullname, prefix
312
+
313
+ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str:
314
+ name, cls = name_cls
315
+ try:
316
+ clsname, attrname = name.rsplit('.', 1)
317
+ if modname and self.env.config.add_module_names:
318
+ clsname = f'{modname}.{clsname}'
319
+ except ValueError:
320
+ if modname:
321
+ return _('%s (in module %s)') % (name, modname)
322
+ else:
323
+ return name
324
+
325
+ return _('%s (%s attribute)') % (attrname, clsname)
326
+
327
+
328
+ class PyProperty(PyObject):
329
+ """Description of an attribute."""
330
+
331
+ option_spec = PyObject.option_spec.copy()
332
+ option_spec.update({
333
+ 'abstractmethod': directives.flag,
334
+ 'classmethod': directives.flag,
335
+ 'type': directives.unchanged,
336
+ })
337
+
338
+ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str]:
339
+ fullname, prefix = super().handle_signature(sig, signode)
340
+
341
+ typ = self.options.get('type')
342
+ if typ:
343
+ annotations = _parse_annotation(typ, self.env)
344
+ signode += addnodes.desc_annotation(typ, '',
345
+ addnodes.desc_sig_punctuation('', ':'),
346
+ addnodes.desc_sig_space(),
347
+ *annotations)
348
+
349
+ return fullname, prefix
350
+
351
+ def get_signature_prefix(self, sig: str) -> list[nodes.Node]:
352
+ prefix: list[nodes.Node] = []
353
+ if 'abstractmethod' in self.options:
354
+ prefix.append(nodes.Text('abstract'))
355
+ prefix.append(addnodes.desc_sig_space())
356
+ if 'classmethod' in self.options:
357
+ prefix.append(nodes.Text('class'))
358
+ prefix.append(addnodes.desc_sig_space())
359
+
360
+ prefix.append(nodes.Text('property'))
361
+ prefix.append(addnodes.desc_sig_space())
362
+ return prefix
363
+
364
+ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str:
365
+ name, cls = name_cls
366
+ try:
367
+ clsname, attrname = name.rsplit('.', 1)
368
+ if modname and self.env.config.add_module_names:
369
+ clsname = f'{modname}.{clsname}'
370
+ except ValueError:
371
+ if modname:
372
+ return _('%s (in module %s)') % (name, modname)
373
+ else:
374
+ return name
375
+
376
+ return _('%s (%s property)') % (attrname, clsname)
377
+
378
+
379
+ class PyModule(SphinxDirective):
380
+ """
381
+ Directive to mark description of a new module.
382
+ """
383
+
384
+ has_content = True
385
+ required_arguments = 1
386
+ optional_arguments = 0
387
+ final_argument_whitespace = False
388
+ option_spec: ClassVar[OptionSpec] = {
389
+ 'platform': lambda x: x,
390
+ 'synopsis': lambda x: x,
391
+ 'no-index': directives.flag,
392
+ 'no-contents-entry': directives.flag,
393
+ 'no-typesetting': directives.flag,
394
+ 'noindex': directives.flag,
395
+ 'nocontentsentry': directives.flag,
396
+ 'deprecated': directives.flag,
397
+ }
398
+
399
+ def run(self) -> list[Node]:
400
+ domain = cast(PythonDomain, self.env.get_domain('py'))
401
+
402
+ modname = self.arguments[0].strip()
403
+ no_index = 'no-index' in self.options or 'noindex' in self.options
404
+ self.env.ref_context['py:module'] = modname
405
+
406
+ content_node: Element = nodes.section()
407
+ # necessary so that the child nodes get the right source/line set
408
+ content_node.document = self.state.document
409
+ nested_parse_with_titles(self.state, self.content, content_node, self.content_offset)
410
+
411
+ ret: list[Node] = []
412
+ if not no_index:
413
+ # note module to the domain
414
+ node_id = make_id(self.env, self.state.document, 'module', modname)
415
+ target = nodes.target('', '', ids=[node_id], ismod=True)
416
+ self.set_source_info(target)
417
+ self.state.document.note_explicit_target(target)
418
+
419
+ domain.note_module(modname,
420
+ node_id,
421
+ self.options.get('synopsis', ''),
422
+ self.options.get('platform', ''),
423
+ 'deprecated' in self.options)
424
+ domain.note_object(modname, 'module', node_id, location=target)
425
+
426
+ # the platform and synopsis aren't printed; in fact, they are only
427
+ # used in the modindex currently
428
+ indextext = f'module; {modname}'
429
+ inode = addnodes.index(entries=[('pair', indextext, node_id, '', None)])
430
+ # The node order is: index node first, then target node.
431
+ ret.append(inode)
432
+ ret.append(target)
433
+ ret.extend(content_node.children)
434
+ return ret
435
+
436
+
437
+ class PyCurrentModule(SphinxDirective):
438
+ """
439
+ This directive is just to tell Sphinx that we're documenting
440
+ stuff in module foo, but links to module foo won't lead here.
441
+ """
442
+
443
+ has_content = False
444
+ required_arguments = 1
445
+ optional_arguments = 0
446
+ final_argument_whitespace = False
447
+ option_spec: ClassVar[OptionSpec] = {}
448
+
449
+ def run(self) -> list[Node]:
450
+ modname = self.arguments[0].strip()
451
+ if modname == 'None':
452
+ self.env.ref_context.pop('py:module', None)
453
+ else:
454
+ self.env.ref_context['py:module'] = modname
455
+ return []
456
+
457
+
458
+ class PyXRefRole(XRefRole):
459
+ def process_link(self, env: BuildEnvironment, refnode: Element,
460
+ has_explicit_title: bool, title: str, target: str) -> tuple[str, str]:
461
+ refnode['py:module'] = env.ref_context.get('py:module')
462
+ refnode['py:class'] = env.ref_context.get('py:class')
463
+ if not has_explicit_title:
464
+ title = title.lstrip('.') # only has a meaning for the target
465
+ target = target.lstrip('~') # only has a meaning for the title
466
+ # if the first character is a tilde, don't display the module/class
467
+ # parts of the contents
468
+ if title[0:1] == '~':
469
+ title = title[1:]
470
+ dot = title.rfind('.')
471
+ if dot != -1:
472
+ title = title[dot + 1:]
473
+ # if the first character is a dot, search more specific namespaces first
474
+ # else search builtins first
475
+ if target[0:1] == '.':
476
+ target = target[1:]
477
+ refnode['refspecific'] = True
478
+ return title, target
479
+
480
+
481
+ def filter_meta_fields(app: Sphinx, domain: str, objtype: str, content: Element) -> None:
482
+ """Filter ``:meta:`` field from its docstring."""
483
+ if domain != 'py':
484
+ return
485
+
486
+ for node in content:
487
+ if isinstance(node, nodes.field_list):
488
+ fields = cast(list[nodes.field], node)
489
+ # removing list items while iterating the list needs reversed()
490
+ for field in reversed(fields):
491
+ field_name = cast(nodes.field_body, field[0]).astext().strip()
492
+ if field_name == 'meta' or field_name.startswith('meta '):
493
+ node.remove(field)
494
+
495
+
496
+ class PythonModuleIndex(Index):
497
+ """
498
+ Index subclass to provide the Python module index.
499
+ """
500
+
501
+ name = 'modindex'
502
+ localname = _('Python Module Index')
503
+ shortname = _('modules')
504
+
505
+ def generate(self, docnames: Iterable[str] | None = None,
506
+ ) -> tuple[list[tuple[str, list[IndexEntry]]], bool]:
507
+ content: dict[str, list[IndexEntry]] = {}
508
+ # list of prefixes to ignore
509
+ ignores: list[str] = self.domain.env.config['modindex_common_prefix']
510
+ ignores = sorted(ignores, key=len, reverse=True)
511
+ # list of all modules, sorted by module name
512
+ modules = sorted(self.domain.data['modules'].items(),
513
+ key=lambda x: x[0].lower())
514
+ # sort out collapsible modules
515
+ prev_modname = ''
516
+ num_toplevels = 0
517
+ for modname, (docname, node_id, synopsis, platforms, deprecated) in modules:
518
+ if docnames and docname not in docnames:
519
+ continue
520
+
521
+ for ignore in ignores:
522
+ if modname.startswith(ignore):
523
+ modname = modname[len(ignore):]
524
+ stripped = ignore
525
+ break
526
+ else:
527
+ stripped = ''
528
+
529
+ # we stripped the whole module name?
530
+ if not modname:
531
+ modname, stripped = stripped, ''
532
+
533
+ entries = content.setdefault(modname[0].lower(), [])
534
+
535
+ package = modname.split('.')[0]
536
+ if package != modname:
537
+ # it's a submodule
538
+ if prev_modname == package:
539
+ # first submodule - make parent a group head
540
+ if entries:
541
+ last = entries[-1]
542
+ entries[-1] = IndexEntry(last[0], 1, last[2], last[3],
543
+ last[4], last[5], last[6])
544
+ elif not prev_modname.startswith(package):
545
+ # submodule without parent in list, add dummy entry
546
+ entries.append(IndexEntry(stripped + package, 1, '', '', '', '', ''))
547
+ subtype = 2
548
+ else:
549
+ num_toplevels += 1
550
+ subtype = 0
551
+
552
+ qualifier = _('Deprecated') if deprecated else ''
553
+ entries.append(IndexEntry(stripped + modname, subtype, docname,
554
+ node_id, platforms, qualifier, synopsis))
555
+ prev_modname = modname
556
+
557
+ # apply heuristics when to collapse modindex at page load:
558
+ # only collapse if number of toplevel modules is larger than
559
+ # number of submodules
560
+ collapse = len(modules) - num_toplevels < num_toplevels
561
+
562
+ # sort by first letter
563
+ sorted_content = sorted(content.items())
564
+
565
+ return sorted_content, collapse
566
+
567
+
568
+ class PythonDomain(Domain):
569
+ """Python language domain."""
570
+
571
+ name = 'py'
572
+ label = 'Python'
573
+ object_types: dict[str, ObjType] = {
574
+ 'function': ObjType(_('function'), 'func', 'obj'),
575
+ 'data': ObjType(_('data'), 'data', 'obj'),
576
+ 'class': ObjType(_('class'), 'class', 'exc', 'obj'),
577
+ 'exception': ObjType(_('exception'), 'exc', 'class', 'obj'),
578
+ 'method': ObjType(_('method'), 'meth', 'obj'),
579
+ 'classmethod': ObjType(_('class method'), 'meth', 'obj'),
580
+ 'staticmethod': ObjType(_('static method'), 'meth', 'obj'),
581
+ 'attribute': ObjType(_('attribute'), 'attr', 'obj'),
582
+ 'property': ObjType(_('property'), 'attr', '_prop', 'obj'),
583
+ 'module': ObjType(_('module'), 'mod', 'obj'),
584
+ }
585
+
586
+ directives = {
587
+ 'function': PyFunction,
588
+ 'data': PyVariable,
589
+ 'class': PyClasslike,
590
+ 'exception': PyClasslike,
591
+ 'method': PyMethod,
592
+ 'classmethod': PyClassMethod,
593
+ 'staticmethod': PyStaticMethod,
594
+ 'attribute': PyAttribute,
595
+ 'property': PyProperty,
596
+ 'module': PyModule,
597
+ 'currentmodule': PyCurrentModule,
598
+ 'decorator': PyDecoratorFunction,
599
+ 'decoratormethod': PyDecoratorMethod,
600
+ }
601
+ roles = {
602
+ 'data': PyXRefRole(),
603
+ 'exc': PyXRefRole(),
604
+ 'func': PyXRefRole(fix_parens=True),
605
+ 'class': PyXRefRole(),
606
+ 'const': PyXRefRole(),
607
+ 'attr': PyXRefRole(),
608
+ 'meth': PyXRefRole(fix_parens=True),
609
+ 'mod': PyXRefRole(),
610
+ 'obj': PyXRefRole(),
611
+ }
612
+ initial_data: dict[str, dict[str, tuple[Any]]] = {
613
+ 'objects': {}, # fullname -> docname, objtype
614
+ 'modules': {}, # modname -> docname, synopsis, platform, deprecated
615
+ }
616
+ indices = [
617
+ PythonModuleIndex,
618
+ ]
619
+
620
+ @property
621
+ def objects(self) -> dict[str, ObjectEntry]:
622
+ return self.data.setdefault('objects', {}) # fullname -> ObjectEntry
623
+
624
+ def note_object(self, name: str, objtype: str, node_id: str,
625
+ aliased: bool = False, location: Any = None) -> None:
626
+ """Note a python object for cross reference.
627
+
628
+ .. versionadded:: 2.1
629
+ """
630
+ if name in self.objects:
631
+ other = self.objects[name]
632
+ if other.aliased and aliased is False:
633
+ # The original definition found. Override it!
634
+ pass
635
+ elif other.aliased is False and aliased:
636
+ # The original definition is already registered.
637
+ return
638
+ else:
639
+ # duplicated
640
+ logger.warning(__('duplicate object description of %s, '
641
+ 'other instance in %s, use :no-index: for one of them'),
642
+ name, other.docname, location=location)
643
+ self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype, aliased)
644
+
645
+ @property
646
+ def modules(self) -> dict[str, ModuleEntry]:
647
+ return self.data.setdefault('modules', {}) # modname -> ModuleEntry
648
+
649
+ def note_module(self, name: str, node_id: str, synopsis: str,
650
+ platform: str, deprecated: bool) -> None:
651
+ """Note a python module for cross reference.
652
+
653
+ .. versionadded:: 2.1
654
+ """
655
+ self.modules[name] = ModuleEntry(self.env.docname, node_id,
656
+ synopsis, platform, deprecated)
657
+
658
+ def clear_doc(self, docname: str) -> None:
659
+ for fullname, obj in list(self.objects.items()):
660
+ if obj.docname == docname:
661
+ del self.objects[fullname]
662
+ for modname, mod in list(self.modules.items()):
663
+ if mod.docname == docname:
664
+ del self.modules[modname]
665
+
666
+ def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
667
+ # XXX check duplicates?
668
+ for fullname, obj in otherdata['objects'].items():
669
+ if obj.docname in docnames:
670
+ self.objects[fullname] = obj
671
+ for modname, mod in otherdata['modules'].items():
672
+ if mod.docname in docnames:
673
+ self.modules[modname] = mod
674
+
675
+ def find_obj(self, env: BuildEnvironment, modname: str, classname: str,
676
+ name: str, type: str | None, searchmode: int = 0,
677
+ ) -> list[tuple[str, ObjectEntry]]:
678
+ """Find a Python object for "name", perhaps using the given module
679
+ and/or classname. Returns a list of (name, object entry) tuples.
680
+ """
681
+ # skip parens
682
+ if name[-2:] == '()':
683
+ name = name[:-2]
684
+
685
+ if not name:
686
+ return []
687
+
688
+ matches: list[tuple[str, ObjectEntry]] = []
689
+
690
+ newname = None
691
+ if searchmode == 1:
692
+ if type is None:
693
+ objtypes: list[str] | None = list(self.object_types)
694
+ else:
695
+ objtypes = self.objtypes_for_role(type)
696
+ if objtypes is not None:
697
+ if modname and classname:
698
+ fullname = modname + '.' + classname + '.' + name
699
+ if fullname in self.objects and self.objects[fullname].objtype in objtypes:
700
+ newname = fullname
701
+ if not newname:
702
+ if modname and modname + '.' + name in self.objects and \
703
+ self.objects[modname + '.' + name].objtype in objtypes:
704
+ newname = modname + '.' + name
705
+ elif name in self.objects and self.objects[name].objtype in objtypes:
706
+ newname = name
707
+ else:
708
+ # "fuzzy" searching mode
709
+ searchname = '.' + name
710
+ matches = [(oname, self.objects[oname]) for oname in self.objects
711
+ if oname.endswith(searchname) and
712
+ self.objects[oname].objtype in objtypes]
713
+ else:
714
+ # NOTE: searching for exact match, object type is not considered
715
+ if name in self.objects:
716
+ newname = name
717
+ elif type == 'mod':
718
+ # only exact matches allowed for modules
719
+ return []
720
+ elif classname and classname + '.' + name in self.objects:
721
+ newname = classname + '.' + name
722
+ elif modname and modname + '.' + name in self.objects:
723
+ newname = modname + '.' + name
724
+ elif modname and classname and \
725
+ modname + '.' + classname + '.' + name in self.objects:
726
+ newname = modname + '.' + classname + '.' + name
727
+ if newname is not None:
728
+ matches.append((newname, self.objects[newname]))
729
+ return matches
730
+
731
+ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
732
+ type: str, target: str, node: pending_xref, contnode: Element,
733
+ ) -> Element | None:
734
+ modname = node.get('py:module')
735
+ clsname = node.get('py:class')
736
+ searchmode = 1 if node.hasattr('refspecific') else 0
737
+ matches = self.find_obj(env, modname, clsname, target,
738
+ type, searchmode)
739
+
740
+ if not matches and type == 'attr':
741
+ # fallback to meth (for property; Sphinx 2.4.x)
742
+ # this ensures that `:attr:` role continues to refer to the old property entry
743
+ # that defined by ``method`` directive in old reST files.
744
+ matches = self.find_obj(env, modname, clsname, target, 'meth', searchmode)
745
+ if not matches and type == 'meth':
746
+ # fallback to attr (for property)
747
+ # this ensures that `:meth:` in the old reST files can refer to the property
748
+ # entry that defined by ``property`` directive.
749
+ #
750
+ # Note: _prop is a secret role only for internal look-up.
751
+ matches = self.find_obj(env, modname, clsname, target, '_prop', searchmode)
752
+
753
+ if not matches:
754
+ return None
755
+ elif len(matches) > 1:
756
+ canonicals = [m for m in matches if not m[1].aliased]
757
+ if len(canonicals) == 1:
758
+ matches = canonicals
759
+ else:
760
+ logger.warning(__('more than one target found for cross-reference %r: %s'),
761
+ target, ', '.join(match[0] for match in matches),
762
+ type='ref', subtype='python', location=node)
763
+ name, obj = matches[0]
764
+
765
+ if obj[2] == 'module':
766
+ return self._make_module_refnode(builder, fromdocname, name, contnode)
767
+ else:
768
+ # determine the content of the reference by conditions
769
+ content = find_pending_xref_condition(node, 'resolved')
770
+ if content:
771
+ children = content.children
772
+ else:
773
+ # if not found, use contnode
774
+ children = [contnode]
775
+
776
+ return make_refnode(builder, fromdocname, obj[0], obj[1], children, name)
777
+
778
+ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
779
+ target: str, node: pending_xref, contnode: Element,
780
+ ) -> list[tuple[str, Element]]:
781
+ modname = node.get('py:module')
782
+ clsname = node.get('py:class')
783
+ results: list[tuple[str, Element]] = []
784
+
785
+ # always search in "refspecific" mode with the :any: role
786
+ matches = self.find_obj(env, modname, clsname, target, None, 1)
787
+ multiple_matches = len(matches) > 1
788
+
789
+ for name, obj in matches:
790
+
791
+ if multiple_matches and obj.aliased:
792
+ # Skip duplicated matches
793
+ continue
794
+
795
+ if obj[2] == 'module':
796
+ results.append(('py:mod',
797
+ self._make_module_refnode(builder, fromdocname,
798
+ name, contnode)))
799
+ else:
800
+ # determine the content of the reference by conditions
801
+ content = find_pending_xref_condition(node, 'resolved')
802
+ if content:
803
+ children = content.children
804
+ else:
805
+ # if not found, use contnode
806
+ children = [contnode]
807
+
808
+ role = 'py:' + self.role_for_objtype(obj[2]) # type: ignore[operator]
809
+ results.append((role, make_refnode(builder, fromdocname, obj[0], obj[1],
810
+ children, name)))
811
+ return results
812
+
813
+ def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str,
814
+ contnode: Node) -> Element:
815
+ # get additional info for modules
816
+ module = self.modules[name]
817
+ title = name
818
+ if module.synopsis:
819
+ title += ': ' + module.synopsis
820
+ if module.deprecated:
821
+ title += _(' (deprecated)')
822
+ if module.platform:
823
+ title += ' (' + module.platform + ')'
824
+ return make_refnode(builder, fromdocname, module.docname, module.node_id,
825
+ contnode, title)
826
+
827
+ def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
828
+ for modname, mod in self.modules.items():
829
+ yield (modname, modname, 'module', mod.docname, mod.node_id, 0)
830
+ for refname, obj in self.objects.items():
831
+ if obj.objtype != 'module': # modules are already handled
832
+ if obj.aliased:
833
+ # aliased names are not full-text searchable.
834
+ yield (refname, refname, obj.objtype, obj.docname, obj.node_id, -1)
835
+ else:
836
+ yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1)
837
+
838
+ def get_full_qualified_name(self, node: Element) -> str | None:
839
+ modname = node.get('py:module')
840
+ clsname = node.get('py:class')
841
+ target = node.get('reftarget')
842
+ if target is None:
843
+ return None
844
+ else:
845
+ return '.'.join(filter(None, [modname, clsname, target]))
846
+
847
+
848
+ def builtin_resolver(app: Sphinx, env: BuildEnvironment,
849
+ node: pending_xref, contnode: Element) -> Element | None:
850
+ """Do not emit nitpicky warnings for built-in types."""
851
+ def istyping(s: str) -> bool:
852
+ if s.startswith('typing.'):
853
+ s = s.split('.', 1)[1]
854
+
855
+ return s in typing.__all__
856
+
857
+ if node.get('refdomain') != 'py':
858
+ return None
859
+ elif node.get('reftype') in ('class', 'obj') and node.get('reftarget') == 'None':
860
+ return contnode
861
+ elif node.get('reftype') in ('class', 'obj', 'exc'):
862
+ reftarget = node.get('reftarget')
863
+ if inspect.isclass(getattr(builtins, reftarget, None)):
864
+ # built-in class
865
+ return contnode
866
+ if istyping(reftarget):
867
+ # typing class
868
+ return contnode
869
+
870
+ return None
871
+
872
+
873
+ def setup(app: Sphinx) -> ExtensionMetadata:
874
+ app.setup_extension('sphinx.directives')
875
+
876
+ app.add_domain(PythonDomain)
877
+ app.add_config_value('python_use_unqualified_type_names', False, 'env')
878
+ app.add_config_value(
879
+ 'python_maximum_signature_line_length', None, 'env', {int, type(None)},
880
+ )
881
+ app.add_config_value('python_display_short_literal_types', False, 'env')
882
+ app.connect('object-description-transform', filter_meta_fields)
883
+ app.connect('missing-reference', builtin_resolver, priority=900)
884
+
885
+ return {
886
+ 'version': 'builtin',
887
+ 'env_version': 4,
888
+ 'parallel_read_safe': True,
889
+ 'parallel_write_safe': True,
890
+ }