Sphinx 7.2.6__py3-none-any.whl → 7.3.1__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (388) hide show
  1. sphinx/__init__.py +8 -9
  2. sphinx/addnodes.py +31 -28
  3. sphinx/application.py +9 -15
  4. sphinx/builders/__init__.py +5 -6
  5. sphinx/builders/_epub_base.py +17 -9
  6. sphinx/builders/changes.py +10 -5
  7. sphinx/builders/dirhtml.py +4 -2
  8. sphinx/builders/dummy.py +3 -2
  9. sphinx/builders/epub3.py +5 -3
  10. sphinx/builders/gettext.py +24 -7
  11. sphinx/builders/html/__init__.py +88 -96
  12. sphinx/builders/html/_assets.py +16 -16
  13. sphinx/builders/html/transforms.py +4 -2
  14. sphinx/builders/latex/__init__.py +40 -33
  15. sphinx/builders/latex/nodes.py +6 -2
  16. sphinx/builders/latex/transforms.py +17 -8
  17. sphinx/builders/latex/util.py +1 -1
  18. sphinx/builders/linkcheck.py +86 -27
  19. sphinx/builders/manpage.py +8 -6
  20. sphinx/builders/singlehtml.py +5 -4
  21. sphinx/builders/texinfo.py +18 -14
  22. sphinx/builders/text.py +3 -2
  23. sphinx/builders/xml.py +5 -2
  24. sphinx/cmd/build.py +119 -76
  25. sphinx/cmd/make_mode.py +4 -9
  26. sphinx/cmd/quickstart.py +13 -16
  27. sphinx/config.py +432 -250
  28. sphinx/deprecation.py +23 -13
  29. sphinx/directives/__init__.py +8 -8
  30. sphinx/directives/code.py +7 -7
  31. sphinx/directives/other.py +23 -13
  32. sphinx/directives/patches.py +7 -6
  33. sphinx/domains/__init__.py +2 -2
  34. sphinx/domains/c/__init__.py +796 -0
  35. sphinx/domains/c/_ast.py +1421 -0
  36. sphinx/domains/c/_ids.py +65 -0
  37. sphinx/domains/c/_parser.py +1048 -0
  38. sphinx/domains/c/_symbol.py +700 -0
  39. sphinx/domains/changeset.py +11 -7
  40. sphinx/domains/citation.py +5 -2
  41. sphinx/domains/cpp/__init__.py +1089 -0
  42. sphinx/domains/cpp/_ast.py +3635 -0
  43. sphinx/domains/cpp/_ids.py +537 -0
  44. sphinx/domains/cpp/_parser.py +2117 -0
  45. sphinx/domains/cpp/_symbol.py +1092 -0
  46. sphinx/domains/index.py +6 -4
  47. sphinx/domains/javascript.py +16 -13
  48. sphinx/domains/math.py +9 -4
  49. sphinx/domains/python/__init__.py +890 -0
  50. sphinx/domains/python/_annotations.py +507 -0
  51. sphinx/domains/python/_object.py +426 -0
  52. sphinx/domains/rst.py +12 -7
  53. sphinx/domains/{std.py → std/__init__.py} +19 -16
  54. sphinx/environment/__init__.py +21 -19
  55. sphinx/environment/adapters/indexentries.py +2 -2
  56. sphinx/environment/adapters/toctree.py +10 -9
  57. sphinx/environment/collectors/__init__.py +6 -3
  58. sphinx/environment/collectors/asset.py +4 -3
  59. sphinx/environment/collectors/dependencies.py +3 -2
  60. sphinx/environment/collectors/metadata.py +6 -5
  61. sphinx/environment/collectors/title.py +3 -2
  62. sphinx/environment/collectors/toctree.py +5 -4
  63. sphinx/errors.py +13 -2
  64. sphinx/events.py +14 -9
  65. sphinx/ext/apidoc.py +9 -11
  66. sphinx/ext/autodoc/__init__.py +105 -71
  67. sphinx/ext/autodoc/directive.py +7 -6
  68. sphinx/ext/autodoc/importer.py +102 -36
  69. sphinx/ext/autodoc/mock.py +7 -5
  70. sphinx/ext/autodoc/preserve_defaults.py +4 -3
  71. sphinx/ext/autodoc/type_comment.py +2 -1
  72. sphinx/ext/autodoc/typehints.py +5 -4
  73. sphinx/ext/autosectionlabel.py +3 -2
  74. sphinx/ext/autosummary/__init__.py +21 -17
  75. sphinx/ext/autosummary/generate.py +9 -9
  76. sphinx/ext/coverage.py +26 -20
  77. sphinx/ext/doctest.py +38 -33
  78. sphinx/ext/duration.py +1 -0
  79. sphinx/ext/extlinks.py +4 -3
  80. sphinx/ext/githubpages.py +3 -2
  81. sphinx/ext/graphviz.py +10 -7
  82. sphinx/ext/ifconfig.py +5 -5
  83. sphinx/ext/imgconverter.py +6 -5
  84. sphinx/ext/imgmath.py +9 -8
  85. sphinx/ext/inheritance_diagram.py +31 -31
  86. sphinx/ext/intersphinx.py +140 -23
  87. sphinx/ext/linkcode.py +3 -2
  88. sphinx/ext/mathjax.py +2 -1
  89. sphinx/ext/napoleon/__init__.py +12 -7
  90. sphinx/ext/napoleon/docstring.py +34 -32
  91. sphinx/ext/todo.py +10 -7
  92. sphinx/ext/viewcode.py +12 -11
  93. sphinx/extension.py +18 -8
  94. sphinx/highlighting.py +39 -20
  95. sphinx/io.py +17 -8
  96. sphinx/jinja2glue.py +16 -15
  97. sphinx/locale/__init__.py +30 -23
  98. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  99. sphinx/locale/ar/LC_MESSAGES/sphinx.po +818 -761
  100. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  101. sphinx/locale/bg/LC_MESSAGES/sphinx.po +811 -754
  102. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  103. sphinx/locale/bn/LC_MESSAGES/sphinx.po +835 -778
  104. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  105. sphinx/locale/ca/LC_MESSAGES/sphinx.po +864 -807
  106. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  107. sphinx/locale/cak/LC_MESSAGES/sphinx.po +816 -759
  108. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  109. sphinx/locale/cs/LC_MESSAGES/sphinx.po +837 -780
  110. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  111. sphinx/locale/cy/LC_MESSAGES/sphinx.po +819 -762
  112. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  113. sphinx/locale/da/LC_MESSAGES/sphinx.po +838 -781
  114. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  115. sphinx/locale/de/LC_MESSAGES/sphinx.po +838 -781
  116. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  117. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +811 -754
  118. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  119. sphinx/locale/el/LC_MESSAGES/sphinx.po +853 -796
  120. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  121. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +811 -754
  122. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +811 -754
  124. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  125. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +856 -799
  126. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  127. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +811 -754
  128. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  129. sphinx/locale/eo/LC_MESSAGES/sphinx.po +820 -763
  130. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  131. sphinx/locale/es/LC_MESSAGES/sphinx.po +856 -799
  132. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  133. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +811 -754
  134. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  135. sphinx/locale/et/LC_MESSAGES/sphinx.po +845 -788
  136. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  137. sphinx/locale/eu/LC_MESSAGES/sphinx.po +837 -780
  138. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  139. sphinx/locale/fa/LC_MESSAGES/sphinx.po +854 -797
  140. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  141. sphinx/locale/fi/LC_MESSAGES/sphinx.po +816 -759
  142. sphinx/locale/fr/LC_MESSAGES/sphinx.js +1 -1
  143. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/fr/LC_MESSAGES/sphinx.po +904 -847
  145. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +811 -754
  147. sphinx/locale/gl/LC_MESSAGES/sphinx.js +54 -54
  148. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  149. sphinx/locale/gl/LC_MESSAGES/sphinx.po +1506 -1449
  150. sphinx/locale/he/LC_MESSAGES/sphinx.js +1 -1
  151. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/he/LC_MESSAGES/sphinx.po +823 -766
  153. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/hi/LC_MESSAGES/sphinx.po +853 -796
  155. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +811 -754
  157. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/hr/LC_MESSAGES/sphinx.po +844 -787
  159. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/hu/LC_MESSAGES/sphinx.po +837 -780
  161. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/id/LC_MESSAGES/sphinx.po +854 -797
  163. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/is/LC_MESSAGES/sphinx.po +811 -754
  165. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/it/LC_MESSAGES/sphinx.po +837 -780
  167. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/ja/LC_MESSAGES/sphinx.po +853 -796
  169. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/ka/LC_MESSAGES/sphinx.po +848 -791
  171. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/ko/LC_MESSAGES/sphinx.po +855 -798
  173. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/lt/LC_MESSAGES/sphinx.po +837 -780
  175. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/lv/LC_MESSAGES/sphinx.po +837 -780
  177. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/mk/LC_MESSAGES/sphinx.po +825 -768
  179. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.js +27 -27
  180. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  181. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +876 -818
  182. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  183. sphinx/locale/ne/LC_MESSAGES/sphinx.po +837 -780
  184. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  185. sphinx/locale/nl/LC_MESSAGES/sphinx.po +844 -787
  186. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  187. sphinx/locale/pl/LC_MESSAGES/sphinx.po +845 -788
  188. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  189. sphinx/locale/pt/LC_MESSAGES/sphinx.po +811 -754
  190. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  191. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +908 -851
  192. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  193. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +837 -780
  194. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  195. sphinx/locale/ro/LC_MESSAGES/sphinx.po +837 -780
  196. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  197. sphinx/locale/ru/LC_MESSAGES/sphinx.po +838 -781
  198. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  199. sphinx/locale/si/LC_MESSAGES/sphinx.po +823 -766
  200. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  201. sphinx/locale/sk/LC_MESSAGES/sphinx.po +854 -797
  202. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  203. sphinx/locale/sl/LC_MESSAGES/sphinx.po +832 -775
  204. sphinx/locale/sphinx.pot +813 -755
  205. sphinx/locale/sq/LC_MESSAGES/sphinx.js +1 -1
  206. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/sq/LC_MESSAGES/sphinx.po +865 -808
  208. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/sr/LC_MESSAGES/sphinx.po +835 -778
  210. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
  212. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/sv/LC_MESSAGES/sphinx.po +837 -780
  214. sphinx/locale/ta/LC_MESSAGES/sphinx.js +54 -54
  215. sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
  216. sphinx/locale/ta/LC_MESSAGES/sphinx.po +1530 -1473
  217. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  218. sphinx/locale/te/LC_MESSAGES/sphinx.po +811 -754
  219. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  220. sphinx/locale/tr/LC_MESSAGES/sphinx.po +853 -796
  221. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  222. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +833 -776
  223. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  224. sphinx/locale/ur/LC_MESSAGES/sphinx.po +811 -754
  225. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  226. sphinx/locale/vi/LC_MESSAGES/sphinx.po +837 -780
  227. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  228. sphinx/locale/yue/LC_MESSAGES/sphinx.po +811 -754
  229. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo +0 -0
  230. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +855 -798
  231. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  232. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +811 -754
  233. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.js +1 -1
  234. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  235. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +879 -822
  236. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  237. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +811 -754
  238. sphinx/parsers.py +7 -5
  239. sphinx/project.py +18 -11
  240. sphinx/pycode/__init__.py +6 -5
  241. sphinx/pycode/ast.py +23 -8
  242. sphinx/pycode/parser.py +6 -5
  243. sphinx/registry.py +12 -6
  244. sphinx/roles.py +103 -57
  245. sphinx/search/__init__.py +17 -18
  246. sphinx/search/da.py +2 -2
  247. sphinx/search/de.py +2 -2
  248. sphinx/search/en.py +1 -1
  249. sphinx/search/es.py +2 -2
  250. sphinx/search/fi.py +2 -2
  251. sphinx/search/fr.py +2 -2
  252. sphinx/search/hu.py +2 -2
  253. sphinx/search/it.py +2 -2
  254. sphinx/search/ja.py +13 -22
  255. sphinx/search/nl.py +2 -2
  256. sphinx/search/no.py +2 -2
  257. sphinx/search/pt.py +2 -2
  258. sphinx/search/ro.py +1 -1
  259. sphinx/search/ru.py +2 -2
  260. sphinx/search/sv.py +2 -2
  261. sphinx/search/tr.py +1 -1
  262. sphinx/search/zh.py +2 -3
  263. sphinx/templates/graphviz/graphviz.css +1 -1
  264. sphinx/testing/fixtures.py +41 -24
  265. sphinx/testing/path.py +1 -1
  266. sphinx/testing/util.py +142 -53
  267. sphinx/texinputs/sphinx.xdy +1 -1
  268. sphinx/texinputs/sphinxlatextables.sty +1 -1
  269. sphinx/texinputs/sphinxpackagesubstitutefont.sty +21 -0
  270. sphinx/themes/agogo/layout.html +4 -4
  271. sphinx/themes/agogo/static/agogo.css_t +1 -1
  272. sphinx/themes/agogo/theme.toml +22 -0
  273. sphinx/themes/basic/defindex.html +1 -1
  274. sphinx/themes/basic/domainindex.html +1 -1
  275. sphinx/themes/basic/genindex-single.html +1 -1
  276. sphinx/themes/basic/genindex-split.html +1 -1
  277. sphinx/themes/basic/genindex.html +1 -1
  278. sphinx/themes/basic/globaltoc.html +1 -1
  279. sphinx/themes/basic/layout.html +1 -1
  280. sphinx/themes/basic/localtoc.html +1 -1
  281. sphinx/themes/basic/page.html +1 -1
  282. sphinx/themes/basic/relations.html +1 -1
  283. sphinx/themes/basic/search.html +5 -20
  284. sphinx/themes/basic/searchbox.html +3 -3
  285. sphinx/themes/basic/searchfield.html +3 -3
  286. sphinx/themes/basic/sourcelink.html +1 -1
  287. sphinx/themes/basic/static/basic.css_t +1 -1
  288. sphinx/themes/basic/static/doctools.js +1 -1
  289. sphinx/themes/basic/static/language_data.js_t +2 -2
  290. sphinx/themes/basic/static/searchtools.js +105 -60
  291. sphinx/themes/basic/theme.toml +23 -0
  292. sphinx/themes/bizstyle/layout.html +1 -6
  293. sphinx/themes/bizstyle/static/bizstyle.css_t +1 -1
  294. sphinx/themes/bizstyle/static/bizstyle.js_t +1 -1
  295. sphinx/themes/bizstyle/static/css3-mediaqueries_src.js +3 -3
  296. sphinx/themes/bizstyle/theme.toml +12 -0
  297. sphinx/themes/classic/layout.html +1 -1
  298. sphinx/themes/classic/static/classic.css_t +1 -1
  299. sphinx/themes/classic/static/sidebar.js_t +1 -1
  300. sphinx/themes/classic/theme.toml +34 -0
  301. sphinx/themes/default/theme.toml +2 -0
  302. sphinx/themes/epub/epub-cover.html +1 -1
  303. sphinx/themes/epub/layout.html +1 -1
  304. sphinx/themes/epub/static/epub.css_t +1 -1
  305. sphinx/themes/epub/theme.toml +10 -0
  306. sphinx/themes/haiku/layout.html +3 -3
  307. sphinx/themes/haiku/static/haiku.css_t +2 -2
  308. sphinx/themes/haiku/theme.toml +16 -0
  309. sphinx/themes/nature/static/nature.css_t +1 -1
  310. sphinx/themes/nature/theme.toml +6 -0
  311. sphinx/themes/nonav/layout.html +1 -1
  312. sphinx/themes/nonav/static/nonav.css_t +1 -1
  313. sphinx/themes/nonav/theme.toml +10 -0
  314. sphinx/themes/pyramid/static/epub.css_t +1 -1
  315. sphinx/themes/pyramid/static/pyramid.css_t +1 -1
  316. sphinx/themes/pyramid/theme.toml +6 -0
  317. sphinx/themes/scrolls/artwork/logo.svg +1 -1
  318. sphinx/themes/scrolls/layout.html +2 -2
  319. sphinx/themes/scrolls/static/scrolls.css_t +1 -1
  320. sphinx/themes/scrolls/theme.toml +15 -0
  321. sphinx/themes/sphinxdoc/static/sphinxdoc.css_t +1 -1
  322. sphinx/themes/sphinxdoc/theme.toml +6 -0
  323. sphinx/themes/traditional/static/traditional.css_t +1 -1
  324. sphinx/themes/traditional/theme.toml +9 -0
  325. sphinx/theming.py +427 -131
  326. sphinx/transforms/__init__.py +21 -24
  327. sphinx/transforms/compact_bullet_list.py +5 -5
  328. sphinx/transforms/i18n.py +30 -28
  329. sphinx/transforms/post_transforms/__init__.py +9 -7
  330. sphinx/transforms/post_transforms/code.py +4 -1
  331. sphinx/transforms/post_transforms/images.py +17 -13
  332. sphinx/transforms/references.py +3 -1
  333. sphinx/util/__init__.py +15 -11
  334. sphinx/util/_io.py +34 -0
  335. sphinx/util/_pathlib.py +23 -18
  336. sphinx/util/build_phase.py +1 -0
  337. sphinx/util/cfamily.py +19 -11
  338. sphinx/util/console.py +101 -21
  339. sphinx/util/display.py +3 -2
  340. sphinx/util/docfields.py +12 -8
  341. sphinx/util/docutils.py +21 -35
  342. sphinx/util/exceptions.py +3 -2
  343. sphinx/util/fileutil.py +5 -5
  344. sphinx/util/http_date.py +9 -2
  345. sphinx/util/i18n.py +40 -9
  346. sphinx/util/inspect.py +317 -245
  347. sphinx/util/inventory.py +22 -5
  348. sphinx/util/logging.py +81 -7
  349. sphinx/util/matching.py +2 -1
  350. sphinx/util/math.py +1 -2
  351. sphinx/util/nodes.py +39 -29
  352. sphinx/util/osutil.py +25 -6
  353. sphinx/util/parallel.py +6 -1
  354. sphinx/util/requests.py +8 -5
  355. sphinx/util/rst.py +8 -6
  356. sphinx/util/tags.py +3 -3
  357. sphinx/util/template.py +8 -3
  358. sphinx/util/typing.py +76 -42
  359. sphinx/versioning.py +6 -2
  360. sphinx/writers/html.py +1 -1
  361. sphinx/writers/html5.py +17 -13
  362. sphinx/writers/latex.py +12 -12
  363. sphinx/writers/manpage.py +13 -7
  364. sphinx/writers/texinfo.py +13 -10
  365. sphinx/writers/text.py +13 -23
  366. sphinx/writers/xml.py +1 -1
  367. sphinx-7.2.6.dist-info/LICENSE → sphinx-7.3.1.dist-info/LICENSE.rst +1 -1
  368. {sphinx-7.2.6.dist-info → sphinx-7.3.1.dist-info}/METADATA +14 -12
  369. sphinx-7.3.1.dist-info/RECORD +581 -0
  370. sphinx/domains/c.py +0 -3906
  371. sphinx/domains/cpp.py +0 -8233
  372. sphinx/domains/python.py +0 -1769
  373. sphinx/themes/agogo/theme.conf +0 -20
  374. sphinx/themes/basic/theme.conf +0 -16
  375. sphinx/themes/bizstyle/theme.conf +0 -10
  376. sphinx/themes/classic/theme.conf +0 -32
  377. sphinx/themes/default/theme.conf +0 -2
  378. sphinx/themes/epub/theme.conf +0 -8
  379. sphinx/themes/haiku/theme.conf +0 -14
  380. sphinx/themes/nature/theme.conf +0 -4
  381. sphinx/themes/nonav/theme.conf +0 -8
  382. sphinx/themes/pyramid/theme.conf +0 -4
  383. sphinx/themes/scrolls/theme.conf +0 -13
  384. sphinx/themes/sphinxdoc/theme.conf +0 -4
  385. sphinx/themes/traditional/theme.conf +0 -7
  386. sphinx-7.2.6.dist-info/RECORD +0 -569
  387. {sphinx-7.2.6.dist-info → sphinx-7.3.1.dist-info}/WHEEL +0 -0
  388. {sphinx-7.2.6.dist-info → sphinx-7.3.1.dist-info}/entry_points.txt +0 -0
sphinx/util/inspect.py CHANGED
@@ -11,41 +11,45 @@ import re
11
11
  import sys
12
12
  import types
13
13
  import typing
14
- from collections.abc import Mapping, Sequence
14
+ from collections.abc import Mapping
15
15
  from functools import cached_property, partial, partialmethod, singledispatchmethod
16
16
  from importlib import import_module
17
- from inspect import ( # noqa: F401
18
- Parameter,
19
- isasyncgenfunction,
20
- isclass,
21
- ismethod,
22
- ismethoddescriptor,
23
- ismodule,
24
- )
17
+ from inspect import Parameter, Signature
25
18
  from io import StringIO
26
- from types import (
27
- ClassMethodDescriptorType,
28
- MethodDescriptorType,
29
- MethodType,
30
- ModuleType,
31
- WrapperDescriptorType,
32
- )
33
- from typing import Any, Callable, cast
19
+ from types import ClassMethodDescriptorType, MethodDescriptorType, WrapperDescriptorType
20
+ from typing import TYPE_CHECKING, Any
34
21
 
35
22
  from sphinx.pycode.ast import unparse as ast_unparse
36
23
  from sphinx.util import logging
37
24
  from sphinx.util.typing import ForwardRef, stringify_annotation
38
25
 
26
+ if TYPE_CHECKING:
27
+ from collections.abc import Callable, Sequence
28
+ from inspect import _ParameterKind
29
+ from types import MethodType, ModuleType
30
+ from typing import Final
31
+
39
32
  logger = logging.getLogger(__name__)
40
33
 
41
34
  memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
42
35
 
36
+ # re-export as is
37
+ isasyncgenfunction = inspect.isasyncgenfunction
38
+ ismethod = inspect.ismethod
39
+ ismethoddescriptor = inspect.ismethoddescriptor
40
+ isclass = inspect.isclass
41
+ ismodule = inspect.ismodule
42
+
43
43
 
44
44
  def unwrap(obj: Any) -> Any:
45
- """Get an original object from wrapped object (wrapped functions)."""
45
+ """Get an original object from wrapped object (wrapped functions).
46
+
47
+ Mocked objects are returned as is.
48
+ """
46
49
  if hasattr(obj, '__sphinx_mock__'):
47
50
  # Skip unwrapping mock object to avoid RecursionError
48
51
  return obj
52
+
49
53
  try:
50
54
  return inspect.unwrap(obj)
51
55
  except ValueError:
@@ -53,14 +57,28 @@ def unwrap(obj: Any) -> Any:
53
57
  return obj
54
58
 
55
59
 
56
- def unwrap_all(obj: Any, *, stop: Callable | None = None) -> Any:
57
- """
58
- Get an original object from wrapped object (unwrapping partials, wrapped
59
- functions, and other decorators).
60
+ def unwrap_all(obj: Any, *, stop: Callable[[Any], bool] | None = None) -> Any:
61
+ """Get an original object from wrapped object.
62
+
63
+ Unlike :func:`unwrap`, this unwraps partial functions, wrapped functions,
64
+ class methods and static methods.
65
+
66
+ When specified, *stop* is a predicate indicating whether an object should
67
+ be unwrapped or not.
60
68
  """
69
+ if callable(stop):
70
+ while not stop(obj):
71
+ if ispartial(obj):
72
+ obj = obj.func
73
+ elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'):
74
+ obj = obj.__wrapped__
75
+ elif isclassmethod(obj) or isstaticmethod(obj):
76
+ obj = obj.__func__
77
+ else:
78
+ return obj
79
+ return obj # in case the while loop never starts
80
+
61
81
  while True:
62
- if stop and stop(obj):
63
- return obj
64
82
  if ispartial(obj):
65
83
  obj = obj.func
66
84
  elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'):
@@ -72,10 +90,11 @@ def unwrap_all(obj: Any, *, stop: Callable | None = None) -> Any:
72
90
 
73
91
 
74
92
  def getall(obj: Any) -> Sequence[str] | None:
75
- """Get __all__ attribute of the module as dict.
93
+ """Get the ``__all__`` attribute of an object as sequence.
76
94
 
77
- Return None if given *obj* does not have __all__.
78
- Raises ValueError if given *obj* have invalid __all__.
95
+ This returns ``None`` if the given ``obj.__all__`` does not exist and
96
+ raises :exc:`ValueError` if ``obj.__all__`` is not a list or tuple of
97
+ strings.
79
98
  """
80
99
  __all__ = safe_getattr(obj, '__all__', None)
81
100
  if __all__ is None:
@@ -86,35 +105,42 @@ def getall(obj: Any) -> Sequence[str] | None:
86
105
 
87
106
 
88
107
  def getannotations(obj: Any) -> Mapping[str, Any]:
89
- """Get __annotations__ from given *obj* safely."""
90
- __annotations__ = safe_getattr(obj, '__annotations__', None)
108
+ """Safely get the ``__annotations__`` attribute of an object."""
109
+ if sys.version_info >= (3, 10, 0) or not isinstance(obj, type):
110
+ __annotations__ = safe_getattr(obj, '__annotations__', None)
111
+ else:
112
+ # Workaround for bugfix not available until python 3.10 as recommended by docs
113
+ # https://docs.python.org/3.10/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
114
+ __dict__ = safe_getattr(obj, '__dict__', {})
115
+ __annotations__ = __dict__.get('__annotations__', None)
91
116
  if isinstance(__annotations__, Mapping):
92
117
  return __annotations__
93
- else:
94
- return {}
118
+ return {}
95
119
 
96
120
 
97
121
  def getglobals(obj: Any) -> Mapping[str, Any]:
98
- """Get __globals__ from given *obj* safely."""
122
+ """Safely get :attr:`obj.__globals__ <function.__globals__>`."""
99
123
  __globals__ = safe_getattr(obj, '__globals__', None)
100
124
  if isinstance(__globals__, Mapping):
101
125
  return __globals__
102
- else:
103
- return {}
126
+ return {}
104
127
 
105
128
 
106
129
  def getmro(obj: Any) -> tuple[type, ...]:
107
- """Get __mro__ from given *obj* safely."""
130
+ """Safely get :attr:`obj.__mro__ <class.__mro__>`."""
108
131
  __mro__ = safe_getattr(obj, '__mro__', None)
109
132
  if isinstance(__mro__, tuple):
110
133
  return __mro__
111
- else:
112
- return ()
134
+ return ()
113
135
 
114
136
 
115
137
  def getorigbases(obj: Any) -> tuple[Any, ...] | None:
116
- """Get __orig_bases__ from *obj* safely."""
117
- if not inspect.isclass(obj):
138
+ """Safely get ``obj.__orig_bases__``.
139
+
140
+ This returns ``None`` if the object is not a class or if ``__orig_bases__``
141
+ is not well-defined (e.g., a non-tuple object or an empty sequence).
142
+ """
143
+ if not isclass(obj):
118
144
  return None
119
145
 
120
146
  # Get __orig_bases__ from obj.__dict__ to avoid accessing the parent's __orig_bases__.
@@ -123,18 +149,17 @@ def getorigbases(obj: Any) -> tuple[Any, ...] | None:
123
149
  __orig_bases__ = __dict__.get('__orig_bases__')
124
150
  if isinstance(__orig_bases__, tuple) and len(__orig_bases__) > 0:
125
151
  return __orig_bases__
126
- else:
127
- return None
152
+ return None
128
153
 
129
154
 
130
- def getslots(obj: Any) -> dict[str, Any] | None:
131
- """Get __slots__ attribute of the class as dict.
155
+ def getslots(obj: Any) -> dict[str, Any] | dict[str, None] | None:
156
+ """Safely get :term:`obj.__slots__ <__slots__>` as a dictionary if any.
132
157
 
133
- Return None if gienv *obj* does not have __slots__.
134
- Raises TypeError if given *obj* is not a class.
135
- Raises ValueError if given *obj* have invalid __slots__.
158
+ - This returns ``None`` if ``obj.__slots__`` does not exist.
159
+ - This raises a :exc:`TypeError` if *obj* is not a class.
160
+ - This raises a :exc:`ValueError` if ``obj.__slots__`` is invalid.
136
161
  """
137
- if not inspect.isclass(obj):
162
+ if not isclass(obj):
138
163
  raise TypeError
139
164
 
140
165
  __slots__ = safe_getattr(obj, '__slots__', None)
@@ -151,7 +176,7 @@ def getslots(obj: Any) -> dict[str, Any] | None:
151
176
 
152
177
 
153
178
  def isNewType(obj: Any) -> bool:
154
- """Check the if object is a kind of NewType."""
179
+ """Check the if object is a kind of :class:`~typing.NewType`."""
155
180
  if sys.version_info[:2] >= (3, 10):
156
181
  return isinstance(obj, typing.NewType)
157
182
  __module__ = safe_getattr(obj, '__module__', None)
@@ -160,72 +185,71 @@ def isNewType(obj: Any) -> bool:
160
185
 
161
186
 
162
187
  def isenumclass(x: Any) -> bool:
163
- """Check if the object is subclass of enum."""
164
- return inspect.isclass(x) and issubclass(x, enum.Enum)
188
+ """Check if the object is an :class:`enumeration class <enum.Enum>`."""
189
+ return isclass(x) and issubclass(x, enum.Enum)
165
190
 
166
191
 
167
192
  def isenumattribute(x: Any) -> bool:
168
- """Check if the object is attribute of enum."""
193
+ """Check if the object is an enumeration attribute."""
169
194
  return isinstance(x, enum.Enum)
170
195
 
171
196
 
172
197
  def unpartial(obj: Any) -> Any:
173
- """Get an original object from partial object.
198
+ """Get an original object from a partial-like object.
174
199
 
175
- This returns given object itself if not partial.
200
+ If *obj* is not a partial object, it is returned as is.
201
+
202
+ .. seealso:: :func:`ispartial`
176
203
  """
177
204
  while ispartial(obj):
178
205
  obj = obj.func
179
-
180
206
  return obj
181
207
 
182
208
 
183
209
  def ispartial(obj: Any) -> bool:
184
- """Check if the object is partial."""
210
+ """Check if the object is a partial function or method."""
185
211
  return isinstance(obj, (partial, partialmethod))
186
212
 
187
213
 
188
214
  def isclassmethod(obj: Any, cls: Any = None, name: str | None = None) -> bool:
189
- """Check if the object is classmethod."""
215
+ """Check if the object is a :class:`classmethod`."""
190
216
  if isinstance(obj, classmethod):
191
217
  return True
192
- if inspect.ismethod(obj) and obj.__self__ is not None and isclass(obj.__self__):
218
+ if ismethod(obj) and obj.__self__ is not None and isclass(obj.__self__):
193
219
  return True
194
220
  if cls and name:
195
- placeholder = object()
221
+ # trace __mro__ if the method is defined in parent class
222
+ sentinel = object()
196
223
  for basecls in getmro(cls):
197
- meth = basecls.__dict__.get(name, placeholder)
198
- if meth is not placeholder:
224
+ meth = basecls.__dict__.get(name, sentinel)
225
+ if meth is not sentinel:
199
226
  return isclassmethod(meth)
200
-
201
227
  return False
202
228
 
203
229
 
204
230
  def isstaticmethod(obj: Any, cls: Any = None, name: str | None = None) -> bool:
205
- """Check if the object is staticmethod."""
231
+ """Check if the object is a :class:`staticmethod`."""
206
232
  if isinstance(obj, staticmethod):
207
233
  return True
208
234
  if cls and name:
209
235
  # trace __mro__ if the method is defined in parent class
210
- #
211
- # .. note:: This only works well with new style classes.
236
+ sentinel = object()
212
237
  for basecls in getattr(cls, '__mro__', [cls]):
213
- meth = basecls.__dict__.get(name)
214
- if meth:
238
+ meth = basecls.__dict__.get(name, sentinel)
239
+ if meth is not sentinel:
215
240
  return isinstance(meth, staticmethod)
216
241
  return False
217
242
 
218
243
 
219
244
  def isdescriptor(x: Any) -> bool:
220
- """Check if the object is some kind of descriptor."""
245
+ """Check if the object is a :external+python:term:`descriptor`."""
221
246
  return any(
222
- callable(safe_getattr(x, item, None))
223
- for item in ['__get__', '__set__', '__delete__']
247
+ callable(safe_getattr(x, item, None)) for item in ('__get__', '__set__', '__delete__')
224
248
  )
225
249
 
226
250
 
227
251
  def isabstractmethod(obj: Any) -> bool:
228
- """Check if the object is an abstractmethod."""
252
+ """Check if the object is an :func:`abstractmethod`."""
229
253
  return safe_getattr(obj, '__isabstractmethod__', False) is True
230
254
 
231
255
 
@@ -242,86 +266,106 @@ def is_cython_function_or_method(obj: Any) -> bool:
242
266
  return False
243
267
 
244
268
 
269
+ _DESCRIPTOR_LIKE: Final[tuple[type, ...]] = (
270
+ ClassMethodDescriptorType,
271
+ MethodDescriptorType,
272
+ WrapperDescriptorType,
273
+ )
274
+
275
+
245
276
  def isattributedescriptor(obj: Any) -> bool:
246
- """Check if the object is an attribute like descriptor."""
277
+ """Check if the object is an attribute-like descriptor."""
247
278
  if inspect.isdatadescriptor(obj):
248
279
  # data descriptor is kind of attribute
249
280
  return True
250
281
  if isdescriptor(obj):
251
282
  # non data descriptor
252
283
  unwrapped = unwrap(obj)
253
- if isfunction(unwrapped) or isbuiltin(unwrapped) or inspect.ismethod(unwrapped):
284
+ if isfunction(unwrapped) or isbuiltin(unwrapped) or ismethod(unwrapped):
254
285
  # attribute must not be either function, builtin and method
255
286
  return False
256
287
  if is_cython_function_or_method(unwrapped):
257
288
  # attribute must not be either function and method (for cython)
258
289
  return False
259
- if inspect.isclass(unwrapped):
290
+ if isclass(unwrapped):
260
291
  # attribute must not be a class
261
292
  return False
262
- if isinstance(unwrapped, (ClassMethodDescriptorType,
263
- MethodDescriptorType,
264
- WrapperDescriptorType)):
293
+ if isinstance(unwrapped, _DESCRIPTOR_LIKE):
265
294
  # attribute must not be a method descriptor
266
295
  return False
267
- if type(unwrapped).__name__ == "instancemethod":
268
- # attribute must not be an instancemethod (C-API)
269
- return False
270
- return True
296
+ # attribute must not be an instancemethod (C-API)
297
+ return type(unwrapped).__name__ != 'instancemethod'
271
298
  return False
272
299
 
273
300
 
274
301
  def is_singledispatch_function(obj: Any) -> bool:
275
- """Check if the object is singledispatch function."""
276
- return (inspect.isfunction(obj) and
277
- hasattr(obj, 'dispatch') and
278
- hasattr(obj, 'register') and
279
- obj.dispatch.__module__ == 'functools')
302
+ """Check if the object is a :func:`~functools.singledispatch` function."""
303
+ return (
304
+ inspect.isfunction(obj)
305
+ and hasattr(obj, 'dispatch')
306
+ and hasattr(obj, 'register')
307
+ and obj.dispatch.__module__ == 'functools'
308
+ )
280
309
 
281
310
 
282
311
  def is_singledispatch_method(obj: Any) -> bool:
283
- """Check if the object is singledispatch method."""
312
+ """Check if the object is a :class:`~functools.singledispatchmethod`."""
284
313
  return isinstance(obj, singledispatchmethod)
285
314
 
286
315
 
287
316
  def isfunction(obj: Any) -> bool:
288
- """Check if the object is function."""
317
+ """Check if the object is a user-defined function.
318
+
319
+ Partial objects are unwrapped before checking them.
320
+
321
+ .. seealso:: :external+python:func:`inspect.isfunction`
322
+ """
289
323
  return inspect.isfunction(unpartial(obj))
290
324
 
291
325
 
292
326
  def isbuiltin(obj: Any) -> bool:
293
- """Check if the object is function."""
327
+ """Check if the object is a built-in function or method.
328
+
329
+ Partial objects are unwrapped before checking them.
330
+
331
+ .. seealso:: :external+python:func:`inspect.isbuiltin`
332
+ """
294
333
  return inspect.isbuiltin(unpartial(obj))
295
334
 
296
335
 
297
336
  def isroutine(obj: Any) -> bool:
298
- """Check is any kind of function or method."""
337
+ """Check if the object is a kind of function or method.
338
+
339
+ Partial objects are unwrapped before checking them.
340
+
341
+ .. seealso:: :external+python:func:`inspect.isroutine`
342
+ """
299
343
  return inspect.isroutine(unpartial(obj))
300
344
 
301
345
 
302
346
  def iscoroutinefunction(obj: Any) -> bool:
303
- """Check if the object is coroutine-function."""
304
- def iswrappedcoroutine(obj: Any) -> bool:
305
- """Check if the object is wrapped coroutine-function."""
306
- if isstaticmethod(obj) or isclassmethod(obj) or ispartial(obj):
307
- # staticmethod, classmethod and partial method are not a wrapped coroutine-function
308
- # Note: Since 3.10, staticmethod and classmethod becomes a kind of wrappers
309
- return False
310
- return hasattr(obj, '__wrapped__')
311
-
312
- obj = unwrap_all(obj, stop=iswrappedcoroutine)
347
+ """Check if the object is a :external+python:term:`coroutine` function."""
348
+ obj = unwrap_all(obj, stop=_is_wrapped_coroutine)
313
349
  return inspect.iscoroutinefunction(obj)
314
350
 
315
351
 
352
+ def _is_wrapped_coroutine(obj: Any) -> bool:
353
+ """Check if the object is wrapped coroutine-function."""
354
+ if isstaticmethod(obj) or isclassmethod(obj) or ispartial(obj):
355
+ # staticmethod, classmethod and partial method are not a wrapped coroutine-function
356
+ # Note: Since 3.10, staticmethod and classmethod becomes a kind of wrappers
357
+ return False
358
+ return hasattr(obj, '__wrapped__')
359
+
360
+
316
361
  def isproperty(obj: Any) -> bool:
317
- """Check if the object is property."""
362
+ """Check if the object is property (possibly cached)."""
318
363
  return isinstance(obj, (property, cached_property))
319
364
 
320
365
 
321
366
  def isgenericalias(obj: Any) -> bool:
322
- """Check if the object is GenericAlias."""
323
- return isinstance(
324
- obj, (types.GenericAlias, typing._BaseGenericAlias)) # type: ignore[attr-defined]
367
+ """Check if the object is a generic alias."""
368
+ return isinstance(obj, (types.GenericAlias, typing._BaseGenericAlias)) # type: ignore[attr-defined]
325
369
 
326
370
 
327
371
  def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
@@ -346,7 +390,7 @@ def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
346
390
  raise AttributeError(name) from exc
347
391
 
348
392
 
349
- def object_description(obj: Any, *, _seen: frozenset = frozenset()) -> str:
393
+ def object_description(obj: Any, *, _seen: frozenset[int] = frozenset()) -> str:
350
394
  """A repr() implementation that returns text safe to use in reST context.
351
395
 
352
396
  Maintains a set of 'seen' object IDs to detect and avoid infinite recursion.
@@ -362,8 +406,10 @@ def object_description(obj: Any, *, _seen: frozenset = frozenset()) -> str:
362
406
  # Cannot sort dict keys, fall back to using descriptions as a sort key
363
407
  sorted_keys = sorted(obj, key=lambda k: object_description(k, _seen=seen))
364
408
 
365
- items = ((object_description(key, _seen=seen),
366
- object_description(obj[key], _seen=seen)) for key in sorted_keys)
409
+ items = (
410
+ (object_description(key, _seen=seen), object_description(obj[key], _seen=seen))
411
+ for key in sorted_keys
412
+ )
367
413
  return '{%s}' % ', '.join(f'{key}: {value}' for (key, value) in items)
368
414
  elif isinstance(obj, set):
369
415
  if id(obj) in seen:
@@ -384,15 +430,18 @@ def object_description(obj: Any, *, _seen: frozenset = frozenset()) -> str:
384
430
  except TypeError:
385
431
  # Cannot sort frozenset values, fall back to using descriptions as a sort key
386
432
  sorted_values = sorted(obj, key=lambda x: object_description(x, _seen=seen))
387
- return 'frozenset({%s})' % ', '.join(object_description(x, _seen=seen)
388
- for x in sorted_values)
433
+ return 'frozenset({%s})' % ', '.join(
434
+ object_description(x, _seen=seen) for x in sorted_values
435
+ )
389
436
  elif isinstance(obj, enum.Enum):
437
+ if obj.__repr__.__func__ is not enum.Enum.__repr__: # type: ignore[attr-defined]
438
+ return repr(obj)
390
439
  return f'{obj.__class__.__name__}.{obj.name}'
391
440
  elif isinstance(obj, tuple):
392
441
  if id(obj) in seen:
393
442
  return 'tuple(...)'
394
443
  seen |= frozenset([id(obj)])
395
- return '(%s%s)' % (
444
+ return '({}{})'.format(
396
445
  ', '.join(object_description(x, _seen=seen) for x in obj),
397
446
  ',' * (len(obj) == 1),
398
447
  )
@@ -413,16 +462,18 @@ def object_description(obj: Any, *, _seen: frozenset = frozenset()) -> str:
413
462
 
414
463
 
415
464
  def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
416
- """If attr_name is implemented at builtin class, return True.
465
+ """Check whether *attr_name* is implemented on a builtin class.
417
466
 
418
467
  >>> is_builtin_class_method(int, '__init__')
419
468
  True
420
469
 
421
- Why this function needed? CPython implements int.__init__ by Descriptor
422
- but PyPy implements it by pure Python code.
470
+
471
+ This function is needed since CPython implements ``int.__init__`` via
472
+ descriptors, but PyPy implementation is written in pure Python code.
423
473
  """
474
+ mro = getmro(obj)
475
+
424
476
  try:
425
- mro = getmro(obj)
426
477
  cls = next(c for c in mro if attr_name in safe_getattr(c, '__dict__', {}))
427
478
  except StopIteration:
428
479
  return False
@@ -449,10 +500,11 @@ class DefaultValue:
449
500
 
450
501
 
451
502
  class TypeAliasForwardRef:
452
- """Pseudo typing class for autodoc_type_aliases.
503
+ """Pseudo typing class for :confval:`autodoc_type_aliases`.
453
504
 
454
- This avoids the error on evaluating the type inside `get_type_hints()`.
505
+ This avoids the error on evaluating the type inside :func:`typing.get_type_hints()`.
455
506
  """
507
+
456
508
  def __init__(self, name: str) -> None:
457
509
  self.name = name
458
510
 
@@ -471,9 +523,9 @@ class TypeAliasForwardRef:
471
523
 
472
524
 
473
525
  class TypeAliasModule:
474
- """Pseudo module class for autodoc_type_aliases."""
526
+ """Pseudo module class for :confval:`autodoc_type_aliases`."""
475
527
 
476
- def __init__(self, modname: str, mapping: dict[str, str]) -> None:
528
+ def __init__(self, modname: str, mapping: Mapping[str, str]) -> None:
477
529
  self.__modname = modname
478
530
  self.__mapping = mapping
479
531
 
@@ -504,12 +556,13 @@ class TypeAliasModule:
504
556
 
505
557
 
506
558
  class TypeAliasNamespace(dict[str, Any]):
507
- """Pseudo namespace class for autodoc_type_aliases.
559
+ """Pseudo namespace class for :confval:`autodoc_type_aliases`.
508
560
 
509
- This enables to look up nested modules and classes like `mod1.mod2.Class`.
561
+ Useful for looking up nested objects via ``namespace.foo.bar.Class``.
510
562
  """
511
563
 
512
- def __init__(self, mapping: dict[str, str]) -> None:
564
+ def __init__(self, mapping: Mapping[str, str]) -> None:
565
+ super().__init__()
513
566
  self.__mapping = mapping
514
567
 
515
568
  def __getitem__(self, key: str) -> Any:
@@ -526,19 +579,21 @@ class TypeAliasNamespace(dict[str, Any]):
526
579
  raise KeyError
527
580
 
528
581
 
529
- def _should_unwrap(subject: Callable) -> bool:
582
+ def _should_unwrap(subject: Callable[..., Any]) -> bool:
530
583
  """Check the function should be unwrapped on getting signature."""
531
584
  __globals__ = getglobals(subject)
532
- if (__globals__.get('__name__') == 'contextlib' and
533
- __globals__.get('__file__') == contextlib.__file__):
534
- # contextmanger should be unwrapped
535
- return True
536
-
537
- return False
585
+ # contextmanger should be unwrapped
586
+ return (
587
+ __globals__.get('__name__') == 'contextlib'
588
+ and __globals__.get('__file__') == contextlib.__file__
589
+ )
538
590
 
539
591
 
540
- def signature(subject: Callable, bound_method: bool = False, type_aliases: dict | None = None,
541
- ) -> inspect.Signature:
592
+ def signature(
593
+ subject: Callable[..., Any],
594
+ bound_method: bool = False,
595
+ type_aliases: Mapping[str, str] | None = None,
596
+ ) -> Signature:
542
597
  """Return a Signature object for the given *subject*.
543
598
 
544
599
  :param bound_method: Specify *subject* is a bound method or not
@@ -591,37 +646,17 @@ def signature(subject: Callable, bound_method: bool = False, type_aliases: dict
591
646
  #
592
647
  # For example, this helps a function having a default value `inspect._empty`.
593
648
  # refs: https://github.com/sphinx-doc/sphinx/issues/7935
594
- return inspect.Signature(parameters, return_annotation=return_annotation,
595
- __validate_parameters__=False)
649
+ return Signature(
650
+ parameters, return_annotation=return_annotation, __validate_parameters__=False
651
+ )
596
652
 
597
653
 
598
- def evaluate_signature(sig: inspect.Signature, globalns: dict | None = None,
599
- localns: dict | None = None,
600
- ) -> inspect.Signature:
654
+ def evaluate_signature(
655
+ sig: Signature,
656
+ globalns: dict[str, Any] | None = None,
657
+ localns: dict[str, Any] | None = None,
658
+ ) -> Signature:
601
659
  """Evaluate unresolved type annotations in a signature object."""
602
- def evaluate_forwardref(ref: ForwardRef, globalns: dict, localns: dict) -> Any:
603
- """Evaluate a forward reference."""
604
- return ref._evaluate(globalns, localns, frozenset())
605
-
606
- def evaluate(annotation: Any, globalns: dict, localns: dict) -> Any:
607
- """Evaluate unresolved type annotation."""
608
- try:
609
- if isinstance(annotation, str):
610
- ref = ForwardRef(annotation, True)
611
- annotation = evaluate_forwardref(ref, globalns, localns)
612
-
613
- if isinstance(annotation, ForwardRef):
614
- annotation = evaluate_forwardref(ref, globalns, localns)
615
- elif isinstance(annotation, str):
616
- # might be a ForwardRef'ed annotation in overloaded functions
617
- ref = ForwardRef(annotation, True)
618
- annotation = evaluate_forwardref(ref, globalns, localns)
619
- except (NameError, TypeError):
620
- # failed to evaluate type. skipped.
621
- pass
622
-
623
- return annotation
624
-
625
660
  if globalns is None:
626
661
  globalns = {}
627
662
  if localns is None:
@@ -630,20 +665,56 @@ def evaluate_signature(sig: inspect.Signature, globalns: dict | None = None,
630
665
  parameters = list(sig.parameters.values())
631
666
  for i, param in enumerate(parameters):
632
667
  if param.annotation:
633
- annotation = evaluate(param.annotation, globalns, localns)
668
+ annotation = _evaluate(param.annotation, globalns, localns)
634
669
  parameters[i] = param.replace(annotation=annotation)
635
670
 
636
671
  return_annotation = sig.return_annotation
637
672
  if return_annotation:
638
- return_annotation = evaluate(return_annotation, globalns, localns)
673
+ return_annotation = _evaluate(return_annotation, globalns, localns)
639
674
 
640
675
  return sig.replace(parameters=parameters, return_annotation=return_annotation)
641
676
 
642
677
 
643
- def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
644
- show_return_annotation: bool = True,
645
- unqualified_typehints: bool = False) -> str:
646
- """Stringify a Signature object.
678
+ def _evaluate_forwardref(
679
+ ref: ForwardRef,
680
+ globalns: dict[str, Any] | None,
681
+ localns: dict[str, Any] | None,
682
+ ) -> Any:
683
+ """Evaluate a forward reference."""
684
+ return ref._evaluate(globalns, localns, frozenset())
685
+
686
+
687
+ def _evaluate(
688
+ annotation: Any,
689
+ globalns: dict[str, Any],
690
+ localns: dict[str, Any],
691
+ ) -> Any:
692
+ """Evaluate unresolved type annotation."""
693
+ try:
694
+ if isinstance(annotation, str):
695
+ ref = ForwardRef(annotation, True)
696
+ annotation = _evaluate_forwardref(ref, globalns, localns)
697
+
698
+ if isinstance(annotation, ForwardRef):
699
+ annotation = _evaluate_forwardref(ref, globalns, localns)
700
+ elif isinstance(annotation, str):
701
+ # might be a ForwardRef'ed annotation in overloaded functions
702
+ ref = ForwardRef(annotation, True)
703
+ annotation = _evaluate_forwardref(ref, globalns, localns)
704
+ except (NameError, TypeError):
705
+ # failed to evaluate type. skipped.
706
+ pass
707
+
708
+ return annotation
709
+
710
+
711
+ def stringify_signature(
712
+ sig: Signature,
713
+ show_annotation: bool = True,
714
+ show_return_annotation: bool = True,
715
+ unqualified_typehints: bool = False,
716
+ ) -> str:
717
+ """Stringify a :class:`~inspect.Signature` object.
647
718
 
648
719
  :param show_annotation: If enabled, show annotations on the signature
649
720
  :param show_return_annotation: If enabled, show annotation of the return value
@@ -655,31 +726,35 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
655
726
  else:
656
727
  mode = 'fully-qualified'
657
728
 
729
+ EMPTY = Parameter.empty
730
+
658
731
  args = []
659
732
  last_kind = None
660
733
  for param in sig.parameters.values():
661
- if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY:
734
+ if param.kind != Parameter.POSITIONAL_ONLY and last_kind == Parameter.POSITIONAL_ONLY:
662
735
  # PEP-570: Separator for Positional Only Parameter: /
663
736
  args.append('/')
664
- if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD,
665
- param.POSITIONAL_ONLY,
666
- None):
737
+ if param.kind == Parameter.KEYWORD_ONLY and last_kind in (
738
+ Parameter.POSITIONAL_OR_KEYWORD,
739
+ Parameter.POSITIONAL_ONLY,
740
+ None,
741
+ ):
667
742
  # PEP-3102: Separator for Keyword Only Parameter: *
668
743
  args.append('*')
669
744
 
670
745
  arg = StringIO()
671
- if param.kind == param.VAR_POSITIONAL:
746
+ if param.kind is Parameter.VAR_POSITIONAL:
672
747
  arg.write('*' + param.name)
673
- elif param.kind == param.VAR_KEYWORD:
748
+ elif param.kind is Parameter.VAR_KEYWORD:
674
749
  arg.write('**' + param.name)
675
750
  else:
676
751
  arg.write(param.name)
677
752
 
678
- if show_annotation and param.annotation is not param.empty:
753
+ if show_annotation and param.annotation is not EMPTY:
679
754
  arg.write(': ')
680
755
  arg.write(stringify_annotation(param.annotation, mode))
681
- if param.default is not param.empty:
682
- if show_annotation and param.annotation is not param.empty:
756
+ if param.default is not EMPTY:
757
+ if show_annotation and param.annotation is not EMPTY:
683
758
  arg.write(' = ')
684
759
  else:
685
760
  arg.write('=')
@@ -688,91 +763,86 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
688
763
  args.append(arg.getvalue())
689
764
  last_kind = param.kind
690
765
 
691
- if last_kind == Parameter.POSITIONAL_ONLY:
766
+ if last_kind is Parameter.POSITIONAL_ONLY:
692
767
  # PEP-570: Separator for Positional Only Parameter: /
693
768
  args.append('/')
694
769
 
695
770
  concatenated_args = ', '.join(args)
696
- if (sig.return_annotation is Parameter.empty or
697
- show_annotation is False or
698
- show_return_annotation is False):
771
+ if sig.return_annotation is EMPTY or not show_annotation or not show_return_annotation:
699
772
  return f'({concatenated_args})'
700
773
  else:
701
- annotation = stringify_annotation(sig.return_annotation, mode)
702
- return f'({concatenated_args}) -> {annotation}'
774
+ retann = stringify_annotation(sig.return_annotation, mode)
775
+ return f'({concatenated_args}) -> {retann}'
703
776
 
704
777
 
705
- def signature_from_str(signature: str) -> inspect.Signature:
706
- """Create a Signature object from string."""
778
+ def signature_from_str(signature: str) -> Signature:
779
+ """Create a :class:`~inspect.Signature` object from a string."""
707
780
  code = 'def func' + signature + ': pass'
708
781
  module = ast.parse(code)
709
- function = cast(ast.FunctionDef, module.body[0])
782
+ function = typing.cast(ast.FunctionDef, module.body[0])
710
783
 
711
784
  return signature_from_ast(function, code)
712
785
 
713
786
 
714
- def signature_from_ast(node: ast.FunctionDef, code: str = '') -> inspect.Signature:
715
- """Create a Signature object from AST *node*."""
716
- args = node.args
717
- defaults = list(args.defaults)
718
- params = []
719
- if hasattr(args, "posonlyargs"):
720
- posonlyargs = len(args.posonlyargs)
721
- positionals = posonlyargs + len(args.args)
722
- else:
723
- posonlyargs = 0
724
- positionals = len(args.args)
725
-
726
- for _ in range(len(defaults), positionals):
727
- defaults.insert(0, Parameter.empty) # type: ignore[arg-type]
728
-
729
- if hasattr(args, "posonlyargs"):
730
- for i, arg in enumerate(args.posonlyargs):
731
- if defaults[i] is Parameter.empty:
732
- default = Parameter.empty
733
- else:
734
- default = DefaultValue(
735
- ast_unparse(defaults[i], code)) # type: ignore[assignment]
736
-
737
- annotation = ast_unparse(arg.annotation, code) or Parameter.empty
738
- params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY,
739
- default=default, annotation=annotation))
740
-
741
- for i, arg in enumerate(args.args):
742
- if defaults[i + posonlyargs] is Parameter.empty:
743
- default = Parameter.empty
744
- else:
745
- default = DefaultValue(
746
- ast_unparse(defaults[i + posonlyargs], code), # type: ignore[assignment]
747
- )
748
-
749
- annotation = ast_unparse(arg.annotation, code) or Parameter.empty
750
- params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD,
751
- default=default, annotation=annotation))
787
+ def signature_from_ast(node: ast.FunctionDef, code: str = '') -> Signature:
788
+ """Create a :class:`~inspect.Signature` object from an AST node."""
789
+ EMPTY = Parameter.empty
752
790
 
791
+ args: ast.arguments = node.args
792
+ defaults: tuple[ast.expr | None, ...] = tuple(args.defaults)
793
+ pos_only_offset = len(args.posonlyargs)
794
+ defaults_offset = pos_only_offset + len(args.args) - len(defaults)
795
+ # The sequence ``D = args.defaults`` contains non-None AST expressions,
796
+ # so we can use ``None`` as a sentinel value for that to indicate that
797
+ # there is no default value for a specific parameter.
798
+ #
799
+ # Let *p* be the number of positional-only and positional-or-keyword
800
+ # arguments. Note that ``0 <= len(D) <= p`` and ``D[0]`` is the default
801
+ # value corresponding to a positional-only *or* a positional-or-keyword
802
+ # argument. Since a non-default argument cannot follow a default argument,
803
+ # the sequence *D* can be completed on the left by adding None sentinels
804
+ # so that ``len(D) == p`` and ``D[i]`` is the *i*-th default argument.
805
+ defaults = (None,) * defaults_offset + defaults
806
+
807
+ # construct the parameter list
808
+ params: list[Parameter] = []
809
+
810
+ # positional-only arguments (introduced in Python 3.8)
811
+ for arg, defexpr in zip(args.posonlyargs, defaults):
812
+ params.append(_define(Parameter.POSITIONAL_ONLY, arg, code, defexpr=defexpr))
813
+
814
+ # normal arguments
815
+ for arg, defexpr in zip(args.args, defaults[pos_only_offset:]):
816
+ params.append(_define(Parameter.POSITIONAL_OR_KEYWORD, arg, code, defexpr=defexpr))
817
+
818
+ # variadic positional argument (no possible default expression)
753
819
  if args.vararg:
754
- annotation = ast_unparse(args.vararg.annotation, code) or Parameter.empty
755
- params.append(Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL,
756
- annotation=annotation))
820
+ params.append(_define(Parameter.VAR_POSITIONAL, args.vararg, code, defexpr=None))
757
821
 
758
- for i, arg in enumerate(args.kwonlyargs):
759
- if args.kw_defaults[i] is None:
760
- default = Parameter.empty
761
- else:
762
- default = DefaultValue(
763
- ast_unparse(args.kw_defaults[i], code)) # type: ignore[arg-type,assignment]
764
- annotation = ast_unparse(arg.annotation, code) or Parameter.empty
765
- params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default,
766
- annotation=annotation))
822
+ # keyword-only arguments
823
+ for arg, defexpr in zip(args.kwonlyargs, args.kw_defaults):
824
+ params.append(_define(Parameter.KEYWORD_ONLY, arg, code, defexpr=defexpr))
767
825
 
826
+ # variadic keyword argument (no possible default expression)
768
827
  if args.kwarg:
769
- annotation = ast_unparse(args.kwarg.annotation, code) or Parameter.empty
770
- params.append(Parameter(args.kwarg.arg, Parameter.VAR_KEYWORD,
771
- annotation=annotation))
828
+ params.append(_define(Parameter.VAR_KEYWORD, args.kwarg, code, defexpr=None))
829
+
830
+ return_annotation = ast_unparse(node.returns, code) or EMPTY
831
+ return Signature(params, return_annotation=return_annotation)
832
+
772
833
 
773
- return_annotation = ast_unparse(node.returns, code) or Parameter.empty
834
+ def _define(
835
+ kind: _ParameterKind,
836
+ arg: ast.arg,
837
+ code: str,
838
+ *,
839
+ defexpr: ast.expr | None,
840
+ ) -> Parameter:
841
+ EMPTY = Parameter.empty
774
842
 
775
- return inspect.Signature(params, return_annotation=return_annotation)
843
+ default = EMPTY if defexpr is None else DefaultValue(ast_unparse(defexpr, code))
844
+ annotation = ast_unparse(arg.annotation, code) or EMPTY
845
+ return Parameter(arg.arg, kind, default=default, annotation=annotation)
776
846
 
777
847
 
778
848
  def getdoc(
@@ -790,13 +860,6 @@ def getdoc(
790
860
  * inherited docstring
791
861
  * inherited decorated methods
792
862
  """
793
- def getdoc_internal(obj: Any, attrgetter: Callable = safe_getattr) -> str | None:
794
- doc = attrgetter(obj, '__doc__', None)
795
- if isinstance(doc, str):
796
- return doc
797
- else:
798
- return None
799
-
800
863
  if cls and name and isclassmethod(obj, cls, name):
801
864
  for basecls in getmro(cls):
802
865
  meth = basecls.__dict__.get(name)
@@ -805,7 +868,7 @@ def getdoc(
805
868
  if doc is not None or not allow_inherited:
806
869
  return doc
807
870
 
808
- doc = getdoc_internal(obj)
871
+ doc = _getdoc_internal(obj)
809
872
  if ispartial(obj) and doc == obj.__class__.__doc__:
810
873
  return getdoc(obj.func)
811
874
  elif doc is None and allow_inherited:
@@ -814,7 +877,7 @@ def getdoc(
814
877
  for basecls in getmro(cls):
815
878
  meth = safe_getattr(basecls, name, None)
816
879
  if meth is not None:
817
- doc = getdoc_internal(meth)
880
+ doc = _getdoc_internal(meth)
818
881
  if doc is not None:
819
882
  break
820
883
 
@@ -831,3 +894,12 @@ def getdoc(
831
894
  doc = inspect.getdoc(obj)
832
895
 
833
896
  return doc
897
+
898
+
899
+ def _getdoc_internal(
900
+ obj: Any, attrgetter: Callable[[Any, str, Any], Any] = safe_getattr
901
+ ) -> str | None:
902
+ doc = attrgetter(obj, '__doc__', None)
903
+ if isinstance(doc, str):
904
+ return doc
905
+ return None