Sphinx 8.1.3__py3-none-any.whl → 8.2.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (328) hide show
  1. sphinx/__init__.py +8 -4
  2. sphinx/__main__.py +2 -0
  3. sphinx/_cli/__init__.py +2 -5
  4. sphinx/_cli/util/colour.py +34 -11
  5. sphinx/_cli/util/errors.py +128 -61
  6. sphinx/addnodes.py +51 -35
  7. sphinx/application.py +362 -230
  8. sphinx/builders/__init__.py +87 -64
  9. sphinx/builders/_epub_base.py +65 -56
  10. sphinx/builders/changes.py +17 -23
  11. sphinx/builders/dirhtml.py +8 -13
  12. sphinx/builders/epub3.py +70 -38
  13. sphinx/builders/gettext.py +93 -73
  14. sphinx/builders/html/__init__.py +240 -186
  15. sphinx/builders/html/_assets.py +9 -2
  16. sphinx/builders/html/_build_info.py +3 -0
  17. sphinx/builders/latex/__init__.py +64 -54
  18. sphinx/builders/latex/constants.py +14 -11
  19. sphinx/builders/latex/nodes.py +2 -0
  20. sphinx/builders/latex/theming.py +8 -9
  21. sphinx/builders/latex/transforms.py +7 -5
  22. sphinx/builders/linkcheck.py +193 -149
  23. sphinx/builders/manpage.py +17 -17
  24. sphinx/builders/singlehtml.py +28 -16
  25. sphinx/builders/texinfo.py +28 -21
  26. sphinx/builders/text.py +10 -15
  27. sphinx/builders/xml.py +10 -19
  28. sphinx/cmd/build.py +49 -119
  29. sphinx/cmd/make_mode.py +35 -31
  30. sphinx/cmd/quickstart.py +78 -62
  31. sphinx/config.py +265 -163
  32. sphinx/directives/__init__.py +51 -54
  33. sphinx/directives/admonitions.py +107 -0
  34. sphinx/directives/code.py +24 -19
  35. sphinx/directives/other.py +21 -42
  36. sphinx/directives/patches.py +28 -16
  37. sphinx/domains/__init__.py +54 -31
  38. sphinx/domains/_domains_container.py +22 -17
  39. sphinx/domains/_index.py +5 -8
  40. sphinx/domains/c/__init__.py +366 -245
  41. sphinx/domains/c/_ast.py +378 -256
  42. sphinx/domains/c/_ids.py +89 -31
  43. sphinx/domains/c/_parser.py +283 -214
  44. sphinx/domains/c/_symbol.py +269 -198
  45. sphinx/domains/changeset.py +39 -24
  46. sphinx/domains/citation.py +54 -24
  47. sphinx/domains/cpp/__init__.py +517 -362
  48. sphinx/domains/cpp/_ast.py +999 -682
  49. sphinx/domains/cpp/_ids.py +133 -65
  50. sphinx/domains/cpp/_parser.py +746 -588
  51. sphinx/domains/cpp/_symbol.py +692 -489
  52. sphinx/domains/index.py +10 -8
  53. sphinx/domains/javascript.py +152 -74
  54. sphinx/domains/math.py +50 -40
  55. sphinx/domains/python/__init__.py +402 -211
  56. sphinx/domains/python/_annotations.py +134 -61
  57. sphinx/domains/python/_object.py +155 -68
  58. sphinx/domains/rst.py +94 -49
  59. sphinx/domains/std/__init__.py +510 -249
  60. sphinx/environment/__init__.py +345 -61
  61. sphinx/environment/adapters/asset.py +7 -1
  62. sphinx/environment/adapters/indexentries.py +15 -20
  63. sphinx/environment/adapters/toctree.py +19 -9
  64. sphinx/environment/collectors/__init__.py +3 -1
  65. sphinx/environment/collectors/asset.py +18 -15
  66. sphinx/environment/collectors/dependencies.py +8 -10
  67. sphinx/environment/collectors/metadata.py +6 -4
  68. sphinx/environment/collectors/title.py +3 -1
  69. sphinx/environment/collectors/toctree.py +4 -4
  70. sphinx/errors.py +1 -3
  71. sphinx/events.py +4 -4
  72. sphinx/ext/apidoc/__init__.py +66 -0
  73. sphinx/ext/apidoc/__main__.py +9 -0
  74. sphinx/ext/apidoc/_cli.py +356 -0
  75. sphinx/ext/apidoc/_extension.py +262 -0
  76. sphinx/ext/apidoc/_generate.py +356 -0
  77. sphinx/ext/apidoc/_shared.py +99 -0
  78. sphinx/ext/autodoc/__init__.py +829 -480
  79. sphinx/ext/autodoc/directive.py +57 -21
  80. sphinx/ext/autodoc/importer.py +184 -67
  81. sphinx/ext/autodoc/mock.py +25 -10
  82. sphinx/ext/autodoc/preserve_defaults.py +17 -9
  83. sphinx/ext/autodoc/type_comment.py +56 -29
  84. sphinx/ext/autodoc/typehints.py +49 -26
  85. sphinx/ext/autosectionlabel.py +28 -11
  86. sphinx/ext/autosummary/__init__.py +281 -142
  87. sphinx/ext/autosummary/generate.py +121 -51
  88. sphinx/ext/coverage.py +152 -91
  89. sphinx/ext/doctest.py +169 -101
  90. sphinx/ext/duration.py +12 -6
  91. sphinx/ext/extlinks.py +33 -21
  92. sphinx/ext/githubpages.py +8 -8
  93. sphinx/ext/graphviz.py +175 -109
  94. sphinx/ext/ifconfig.py +11 -6
  95. sphinx/ext/imgconverter.py +48 -25
  96. sphinx/ext/imgmath.py +127 -97
  97. sphinx/ext/inheritance_diagram.py +177 -103
  98. sphinx/ext/intersphinx/__init__.py +22 -13
  99. sphinx/ext/intersphinx/__main__.py +3 -1
  100. sphinx/ext/intersphinx/_cli.py +18 -14
  101. sphinx/ext/intersphinx/_load.py +91 -82
  102. sphinx/ext/intersphinx/_resolve.py +108 -74
  103. sphinx/ext/intersphinx/_shared.py +2 -2
  104. sphinx/ext/linkcode.py +28 -12
  105. sphinx/ext/mathjax.py +60 -29
  106. sphinx/ext/napoleon/__init__.py +19 -7
  107. sphinx/ext/napoleon/docstring.py +229 -231
  108. sphinx/ext/todo.py +44 -49
  109. sphinx/ext/viewcode.py +105 -57
  110. sphinx/extension.py +3 -1
  111. sphinx/highlighting.py +13 -7
  112. sphinx/io.py +9 -13
  113. sphinx/jinja2glue.py +29 -26
  114. sphinx/locale/__init__.py +8 -9
  115. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  116. sphinx/locale/ar/LC_MESSAGES/sphinx.po +2155 -2050
  117. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  118. sphinx/locale/bg/LC_MESSAGES/sphinx.po +2045 -1940
  119. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  120. sphinx/locale/bn/LC_MESSAGES/sphinx.po +2175 -2070
  121. sphinx/locale/ca/LC_MESSAGES/sphinx.js +3 -3
  122. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/ca/LC_MESSAGES/sphinx.po +2690 -2585
  124. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.js +63 -0
  125. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.mo +0 -0
  126. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.po +4216 -0
  127. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  128. sphinx/locale/cak/LC_MESSAGES/sphinx.po +2096 -1991
  129. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  130. sphinx/locale/cs/LC_MESSAGES/sphinx.po +2248 -2143
  131. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  132. sphinx/locale/cy/LC_MESSAGES/sphinx.po +2201 -2096
  133. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  134. sphinx/locale/da/LC_MESSAGES/sphinx.po +2282 -2177
  135. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  136. sphinx/locale/de/LC_MESSAGES/sphinx.po +2261 -2156
  137. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  138. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2045 -1940
  139. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  140. sphinx/locale/el/LC_MESSAGES/sphinx.po +2604 -2499
  141. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  142. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2045 -1940
  143. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2045 -1940
  145. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2631 -2526
  147. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  148. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2045 -1940
  149. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  150. sphinx/locale/eo/LC_MESSAGES/sphinx.po +2078 -1973
  151. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/es/LC_MESSAGES/sphinx.po +2633 -2528
  153. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2045 -1940
  155. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/et/LC_MESSAGES/sphinx.po +2449 -2344
  157. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/eu/LC_MESSAGES/sphinx.po +2241 -2136
  159. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/fa/LC_MESSAGES/sphinx.po +504 -500
  161. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/fi/LC_MESSAGES/sphinx.po +499 -495
  163. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/fr/LC_MESSAGES/sphinx.po +513 -509
  165. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +499 -495
  167. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/gl/LC_MESSAGES/sphinx.po +2644 -2539
  169. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/he/LC_MESSAGES/sphinx.po +499 -495
  171. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/hi/LC_MESSAGES/sphinx.po +504 -500
  173. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +499 -495
  175. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/hr/LC_MESSAGES/sphinx.po +501 -497
  177. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/hu/LC_MESSAGES/sphinx.po +499 -495
  179. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  180. sphinx/locale/id/LC_MESSAGES/sphinx.po +2609 -2504
  181. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  182. sphinx/locale/is/LC_MESSAGES/sphinx.po +499 -495
  183. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  184. sphinx/locale/it/LC_MESSAGES/sphinx.po +2265 -2160
  185. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  186. sphinx/locale/ja/LC_MESSAGES/sphinx.po +2621 -2516
  187. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  188. sphinx/locale/ka/LC_MESSAGES/sphinx.po +2567 -2462
  189. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  190. sphinx/locale/ko/LC_MESSAGES/sphinx.po +2631 -2526
  191. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  192. sphinx/locale/lt/LC_MESSAGES/sphinx.po +2214 -2109
  193. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  194. sphinx/locale/lv/LC_MESSAGES/sphinx.po +2218 -2113
  195. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  196. sphinx/locale/mk/LC_MESSAGES/sphinx.po +2088 -1983
  197. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  198. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2247 -2142
  199. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  200. sphinx/locale/ne/LC_MESSAGES/sphinx.po +2227 -2122
  201. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  202. sphinx/locale/nl/LC_MESSAGES/sphinx.po +2316 -2211
  203. sphinx/locale/pl/LC_MESSAGES/sphinx.js +2 -2
  204. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  205. sphinx/locale/pl/LC_MESSAGES/sphinx.po +2442 -2336
  206. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/pt/LC_MESSAGES/sphinx.po +2045 -1940
  208. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2657 -2552
  210. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2243 -2138
  212. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/ro/LC_MESSAGES/sphinx.po +2244 -2139
  214. sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
  215. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  216. sphinx/locale/ru/LC_MESSAGES/sphinx.po +2660 -2555
  217. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  218. sphinx/locale/si/LC_MESSAGES/sphinx.po +2134 -2029
  219. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  220. sphinx/locale/sk/LC_MESSAGES/sphinx.po +2614 -2509
  221. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  222. sphinx/locale/sl/LC_MESSAGES/sphinx.po +2167 -2062
  223. sphinx/locale/sphinx.pot +2069 -1964
  224. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  225. sphinx/locale/sq/LC_MESSAGES/sphinx.po +2661 -2556
  226. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  227. sphinx/locale/sr/LC_MESSAGES/sphinx.po +2213 -2108
  228. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  229. sphinx/locale/sv/LC_MESSAGES/sphinx.po +2229 -2124
  230. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  231. sphinx/locale/te/LC_MESSAGES/sphinx.po +2045 -1940
  232. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  233. sphinx/locale/tr/LC_MESSAGES/sphinx.po +2608 -2503
  234. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  235. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2167 -2062
  236. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  237. sphinx/locale/ur/LC_MESSAGES/sphinx.po +2045 -1940
  238. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  239. sphinx/locale/vi/LC_MESSAGES/sphinx.po +2204 -2099
  240. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  241. sphinx/locale/yue/LC_MESSAGES/sphinx.po +2045 -1940
  242. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  243. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2045 -1940
  244. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  245. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2659 -2554
  246. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  247. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2045 -1940
  248. sphinx/parsers.py +8 -7
  249. sphinx/project.py +2 -2
  250. sphinx/pycode/__init__.py +31 -21
  251. sphinx/pycode/ast.py +6 -3
  252. sphinx/pycode/parser.py +14 -8
  253. sphinx/pygments_styles.py +4 -5
  254. sphinx/registry.py +192 -92
  255. sphinx/roles.py +58 -7
  256. sphinx/search/__init__.py +75 -54
  257. sphinx/search/en.py +11 -13
  258. sphinx/search/fi.py +1 -1
  259. sphinx/search/ja.py +8 -6
  260. sphinx/search/nl.py +1 -1
  261. sphinx/search/zh.py +19 -21
  262. sphinx/testing/fixtures.py +26 -29
  263. sphinx/testing/path.py +26 -62
  264. sphinx/testing/restructuredtext.py +14 -8
  265. sphinx/testing/util.py +21 -19
  266. sphinx/texinputs/make.bat.jinja +50 -50
  267. sphinx/texinputs/sphinx.sty +4 -3
  268. sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
  269. sphinx/texinputs/sphinxlatexobjects.sty +29 -10
  270. sphinx/themes/basic/static/searchtools.js +8 -5
  271. sphinx/theming.py +49 -61
  272. sphinx/transforms/__init__.py +17 -38
  273. sphinx/transforms/compact_bullet_list.py +5 -3
  274. sphinx/transforms/i18n.py +8 -21
  275. sphinx/transforms/post_transforms/__init__.py +142 -93
  276. sphinx/transforms/post_transforms/code.py +5 -5
  277. sphinx/transforms/post_transforms/images.py +28 -24
  278. sphinx/transforms/references.py +3 -1
  279. sphinx/util/__init__.py +109 -60
  280. sphinx/util/_files.py +39 -23
  281. sphinx/util/_importer.py +4 -1
  282. sphinx/util/_inventory_file_reader.py +76 -0
  283. sphinx/util/_io.py +2 -2
  284. sphinx/util/_lines.py +6 -3
  285. sphinx/util/_pathlib.py +40 -2
  286. sphinx/util/build_phase.py +2 -0
  287. sphinx/util/cfamily.py +19 -14
  288. sphinx/util/console.py +44 -179
  289. sphinx/util/display.py +9 -10
  290. sphinx/util/docfields.py +140 -122
  291. sphinx/util/docstrings.py +1 -1
  292. sphinx/util/docutils.py +118 -77
  293. sphinx/util/fileutil.py +25 -26
  294. sphinx/util/http_date.py +2 -0
  295. sphinx/util/i18n.py +77 -64
  296. sphinx/util/images.py +8 -6
  297. sphinx/util/inspect.py +147 -38
  298. sphinx/util/inventory.py +215 -116
  299. sphinx/util/logging.py +33 -33
  300. sphinx/util/matching.py +12 -4
  301. sphinx/util/nodes.py +18 -13
  302. sphinx/util/osutil.py +38 -39
  303. sphinx/util/parallel.py +22 -13
  304. sphinx/util/parsing.py +2 -1
  305. sphinx/util/png.py +6 -2
  306. sphinx/util/requests.py +33 -2
  307. sphinx/util/rst.py +3 -2
  308. sphinx/util/tags.py +1 -1
  309. sphinx/util/template.py +18 -10
  310. sphinx/util/texescape.py +8 -6
  311. sphinx/util/typing.py +148 -122
  312. sphinx/versioning.py +3 -3
  313. sphinx/writers/html.py +3 -1
  314. sphinx/writers/html5.py +63 -52
  315. sphinx/writers/latex.py +83 -67
  316. sphinx/writers/manpage.py +19 -38
  317. sphinx/writers/texinfo.py +47 -47
  318. sphinx/writers/text.py +50 -32
  319. sphinx/writers/xml.py +11 -8
  320. {sphinx-8.1.3.dist-info → sphinx-8.2.0.dist-info}/LICENSE.rst +1 -1
  321. {sphinx-8.1.3.dist-info → sphinx-8.2.0.dist-info}/METADATA +25 -15
  322. sphinx-8.2.0.dist-info/RECORD +606 -0
  323. {sphinx-8.1.3.dist-info → sphinx-8.2.0.dist-info}/WHEEL +1 -1
  324. sphinx/builders/html/transforms.py +0 -90
  325. sphinx/ext/apidoc.py +0 -721
  326. sphinx/util/exceptions.py +0 -74
  327. sphinx-8.1.3.dist-info/RECORD +0 -598
  328. {sphinx-8.1.3.dist-info → sphinx-8.2.0.dist-info}/entry_points.txt +0 -0
sphinx/ext/apidoc.py DELETED
@@ -1,721 +0,0 @@
1
- """Creates reST files corresponding to Python modules for code documentation.
2
-
3
- Parses a directory tree looking for Python modules and packages and creates
4
- ReST files appropriately to create code documentation with Sphinx. It also
5
- creates a modules index (named modules.<suffix>).
6
-
7
- This is derived from the "sphinx-autopackage" script, which is:
8
- Copyright 2008 Société des arts technologiques (SAT),
9
- https://sat.qc.ca/
10
- """
11
-
12
- from __future__ import annotations
13
-
14
- import argparse
15
- import fnmatch
16
- import glob
17
- import locale
18
- import os
19
- import re
20
- import sys
21
- from copy import copy
22
- from importlib.machinery import EXTENSION_SUFFIXES
23
- from os import path
24
- from pathlib import Path
25
- from typing import TYPE_CHECKING, Any, Protocol
26
-
27
- import sphinx.locale
28
- from sphinx import __display_version__, package_dir
29
- from sphinx.cmd.quickstart import EXTENSIONS
30
- from sphinx.locale import __
31
- from sphinx.util import logging
32
- from sphinx.util.osutil import FileAvoidWrite, ensuredir
33
- from sphinx.util.template import ReSTRenderer
34
-
35
- if TYPE_CHECKING:
36
- from collections.abc import Iterator, Sequence
37
-
38
- logger = logging.getLogger(__name__)
39
-
40
- # automodule options
41
- if 'SPHINX_APIDOC_OPTIONS' in os.environ:
42
- OPTIONS = os.environ['SPHINX_APIDOC_OPTIONS'].split(',')
43
- else:
44
- OPTIONS = [
45
- 'members',
46
- 'undoc-members',
47
- # 'inherited-members', # disabled because there's a bug in sphinx
48
- 'show-inheritance',
49
- ]
50
-
51
- PY_SUFFIXES = ('.py', '.pyx', *tuple(EXTENSION_SUFFIXES))
52
-
53
- template_dir = path.join(package_dir, 'templates', 'apidoc')
54
-
55
-
56
- def is_initpy(filename: str | Path) -> bool:
57
- """Check *filename* is __init__ file or not."""
58
- basename = Path(filename).name
59
- return any(
60
- basename == '__init__' + suffix
61
- for suffix in sorted(PY_SUFFIXES, key=len, reverse=True)
62
- )
63
-
64
-
65
- def module_join(*modnames: str | None) -> str:
66
- """Join module names with dots."""
67
- return '.'.join(filter(None, modnames))
68
-
69
-
70
- def is_packagedir(dirname: str | None = None, files: list[str] | None = None) -> bool:
71
- """Check given *files* contains __init__ file."""
72
- if files is None and dirname is None:
73
- return False
74
-
75
- if files is None:
76
- files = os.listdir(dirname)
77
- return any(f for f in files if is_initpy(f))
78
-
79
-
80
- def write_file(name: str, text: str, opts: CliOptions) -> Path:
81
- """Write the output file for module/package <name>."""
82
- fname = Path(opts.destdir, f'{name}.{opts.suffix}')
83
- if opts.dryrun:
84
- if not opts.quiet:
85
- logger.info(__('Would create file %s.'), fname)
86
- return fname
87
- if not opts.force and fname.is_file():
88
- if not opts.quiet:
89
- logger.info(__('File %s already exists, skipping.'), fname)
90
- else:
91
- if not opts.quiet:
92
- logger.info(__('Creating file %s.'), fname)
93
- with FileAvoidWrite(fname) as f:
94
- f.write(text)
95
- return fname
96
-
97
-
98
- def create_module_file(
99
- package: str | None,
100
- basename: str,
101
- opts: CliOptions,
102
- user_template_dir: str | None = None,
103
- ) -> Path:
104
- """Build the text of the file and write the file."""
105
- options = copy(OPTIONS)
106
- if opts.includeprivate and 'private-members' not in options:
107
- options.append('private-members')
108
-
109
- qualname = module_join(package, basename)
110
- context = {
111
- 'show_headings': not opts.noheadings,
112
- 'basename': basename,
113
- 'qualname': qualname,
114
- 'automodule_options': options,
115
- }
116
- if user_template_dir is not None:
117
- template_path = [user_template_dir, template_dir]
118
- else:
119
- template_path = [template_dir]
120
- text = ReSTRenderer(template_path).render('module.rst.jinja', context)
121
- return write_file(qualname, text, opts)
122
-
123
-
124
- def create_package_file(
125
- root: str,
126
- master_package: str | None,
127
- subroot: str,
128
- py_files: list[str],
129
- opts: CliOptions,
130
- subs: list[str],
131
- is_namespace: bool,
132
- excludes: Sequence[re.Pattern[str]] = (),
133
- user_template_dir: str | None = None,
134
- ) -> list[Path]:
135
- """Build the text of the file and write the file.
136
-
137
- Also create submodules if necessary.
138
-
139
- :returns: list of written files
140
- """
141
- # build a list of sub packages (directories containing an __init__ file)
142
- subpackages = [
143
- module_join(master_package, subroot, pkgname)
144
- for pkgname in subs
145
- if not is_skipped_package(Path(root, pkgname), opts, excludes)
146
- ]
147
- # build a list of sub modules
148
- submodules = [
149
- sub.split('.')[0]
150
- for sub in py_files
151
- if not is_skipped_module(Path(root, sub), opts, excludes) and not is_initpy(sub)
152
- ]
153
- submodules = sorted(set(submodules))
154
- submodules = [
155
- module_join(master_package, subroot, modname) for modname in submodules
156
- ]
157
- options = copy(OPTIONS)
158
- if opts.includeprivate and 'private-members' not in options:
159
- options.append('private-members')
160
-
161
- pkgname = module_join(master_package, subroot)
162
- context = {
163
- 'pkgname': pkgname,
164
- 'subpackages': subpackages,
165
- 'submodules': submodules,
166
- 'is_namespace': is_namespace,
167
- 'modulefirst': opts.modulefirst,
168
- 'separatemodules': opts.separatemodules,
169
- 'automodule_options': options,
170
- 'show_headings': not opts.noheadings,
171
- 'maxdepth': opts.maxdepth,
172
- }
173
- if user_template_dir is not None:
174
- template_path = [user_template_dir, template_dir]
175
- else:
176
- template_path = [template_dir]
177
-
178
- written: list[Path] = []
179
-
180
- text = ReSTRenderer(template_path).render('package.rst.jinja', context)
181
- written.append(write_file(pkgname, text, opts))
182
-
183
- if submodules and opts.separatemodules:
184
- written.extend([
185
- create_module_file(None, submodule, opts, user_template_dir)
186
- for submodule in submodules
187
- ])
188
-
189
- return written
190
-
191
-
192
- def create_modules_toc_file(
193
- modules: list[str],
194
- opts: CliOptions,
195
- name: str = 'modules',
196
- user_template_dir: str | None = None,
197
- ) -> Path:
198
- """Create the module's index."""
199
- modules.sort()
200
- prev_module = ''
201
- for module in modules.copy():
202
- # look if the module is a subpackage and, if yes, ignore it
203
- if module.startswith(prev_module + '.'):
204
- modules.remove(module)
205
- else:
206
- prev_module = module
207
-
208
- context = {
209
- 'header': opts.header,
210
- 'maxdepth': opts.maxdepth,
211
- 'docnames': modules,
212
- }
213
- if user_template_dir is not None:
214
- template_path = [user_template_dir, template_dir]
215
- else:
216
- template_path = [template_dir]
217
- text = ReSTRenderer(template_path).render('toc.rst.jinja', context)
218
- return write_file(name, text, opts)
219
-
220
-
221
- def is_skipped_package(
222
- dirname: str | Path, opts: CliOptions, excludes: Sequence[re.Pattern[str]] = ()
223
- ) -> bool:
224
- """Check if we want to skip this module."""
225
- if not Path(dirname).is_dir():
226
- return False
227
-
228
- files = glob.glob(str(Path(dirname, '*.py')))
229
- regular_package = any(f for f in files if is_initpy(f))
230
- if not regular_package and not opts.implicit_namespaces:
231
- # *dirname* is not both a regular package and an implicit namespace package
232
- return True
233
-
234
- # Check there is some showable module inside package
235
- return all(is_excluded(Path(dirname, f), excludes) for f in files)
236
-
237
-
238
- def is_skipped_module(
239
- filename: str | Path, opts: CliOptions, _excludes: Sequence[re.Pattern[str]]
240
- ) -> bool:
241
- """Check if we want to skip this module."""
242
- filename = Path(filename)
243
- if not filename.exists():
244
- # skip if the file doesn't exist
245
- return True
246
- # skip if the module has a "private" name
247
- return filename.name.startswith('_') and not opts.includeprivate
248
-
249
-
250
- def walk(
251
- rootpath: str,
252
- excludes: Sequence[re.Pattern[str]],
253
- opts: CliOptions,
254
- ) -> Iterator[tuple[str, list[str], list[str]]]:
255
- """Walk through the directory and list files and subdirectories up."""
256
- for root, subs, files in os.walk(rootpath, followlinks=opts.followlinks):
257
- # document only Python module files (that aren't excluded)
258
- files = sorted(
259
- f
260
- for f in files
261
- if f.endswith(PY_SUFFIXES) and not is_excluded(Path(root, f), excludes)
262
- )
263
-
264
- # remove hidden ('.') and private ('_') directories, as well as
265
- # excluded dirs
266
- if opts.includeprivate:
267
- exclude_prefixes: tuple[str, ...] = ('.',)
268
- else:
269
- exclude_prefixes = ('.', '_')
270
-
271
- subs[:] = sorted(
272
- sub
273
- for sub in subs
274
- if not sub.startswith(exclude_prefixes)
275
- and not is_excluded(Path(root, sub), excludes)
276
- )
277
-
278
- yield root, subs, files
279
-
280
-
281
- def has_child_module(
282
- rootpath: str, excludes: Sequence[re.Pattern[str]], opts: CliOptions
283
- ) -> bool:
284
- """Check the given directory contains child module/s (at least one)."""
285
- return any(files for _root, _subs, files in walk(rootpath, excludes, opts))
286
-
287
-
288
- def recurse_tree(
289
- rootpath: str,
290
- excludes: Sequence[re.Pattern[str]],
291
- opts: CliOptions,
292
- user_template_dir: str | None = None,
293
- ) -> tuple[list[Path], list[str]]:
294
- """
295
- Look for every file in the directory tree and create the corresponding
296
- ReST files.
297
- """
298
- # check if the base directory is a package and get its name
299
- if is_packagedir(rootpath) or opts.implicit_namespaces:
300
- root_package = rootpath.split(path.sep)[-1]
301
- else:
302
- # otherwise, the base is a directory with packages
303
- root_package = None
304
-
305
- toplevels = []
306
- written_files = []
307
- for root, subs, files in walk(rootpath, excludes, opts):
308
- is_pkg = is_packagedir(None, files)
309
- is_namespace = not is_pkg and opts.implicit_namespaces
310
- if is_pkg:
311
- for f in files.copy():
312
- if is_initpy(f):
313
- files.remove(f)
314
- files.insert(0, f)
315
- elif root != rootpath:
316
- # only accept non-package at toplevel unless using implicit namespaces
317
- if not opts.implicit_namespaces:
318
- subs.clear()
319
- continue
320
-
321
- if is_pkg or is_namespace:
322
- # we are in a package with something to document
323
- if subs or len(files) > 1 or not is_skipped_package(root, opts):
324
- subpackage = (
325
- root[len(rootpath) :].lstrip(path.sep).replace(path.sep, '.')
326
- )
327
- # if this is not a namespace or
328
- # a namespace and there is something there to document
329
- if not is_namespace or has_child_module(root, excludes, opts):
330
- written_files.extend(
331
- create_package_file(
332
- root,
333
- root_package,
334
- subpackage,
335
- files,
336
- opts,
337
- subs,
338
- is_namespace,
339
- excludes,
340
- user_template_dir,
341
- )
342
- )
343
- toplevels.append(module_join(root_package, subpackage))
344
- else:
345
- # if we are at the root level, we don't require it to be a package
346
- assert root == rootpath
347
- assert root_package is None
348
- for py_file in files:
349
- if not is_skipped_module(Path(rootpath, py_file), opts, excludes):
350
- module = py_file.split('.')[0]
351
- written_files.append(
352
- create_module_file(
353
- root_package, module, opts, user_template_dir
354
- )
355
- )
356
- toplevels.append(module)
357
-
358
- return written_files, toplevels
359
-
360
-
361
- def is_excluded(root: str | Path, excludes: Sequence[re.Pattern[str]]) -> bool:
362
- """Check if the directory is in the exclude list.
363
-
364
- Note: by having trailing slashes, we avoid common prefix issues, like
365
- e.g. an exclude "foo" also accidentally excluding "foobar".
366
- """
367
- root_str = str(root)
368
- return any(exclude.match(root_str) for exclude in excludes)
369
-
370
-
371
- def get_parser() -> argparse.ArgumentParser:
372
- parser = argparse.ArgumentParser(
373
- usage='%(prog)s [OPTIONS] -o <OUTPUT_PATH> <MODULE_PATH> [EXCLUDE_PATTERN, ...]',
374
- epilog=__('For more information, visit <https://www.sphinx-doc.org/>.'),
375
- description=__("""
376
- Look recursively in <MODULE_PATH> for Python modules and packages and create
377
- one reST file with automodule directives per package in the <OUTPUT_PATH>.
378
-
379
- The <EXCLUDE_PATTERN>s can be file and/or directory patterns that will be
380
- excluded from generation.
381
-
382
- Note: By default this script will not overwrite already created files."""),
383
- )
384
-
385
- parser.add_argument(
386
- '--version',
387
- action='version',
388
- dest='show_version',
389
- version='%%(prog)s %s' % __display_version__,
390
- )
391
-
392
- parser.add_argument('module_path', help=__('path to module to document'))
393
- parser.add_argument(
394
- 'exclude_pattern',
395
- nargs='*',
396
- help=__(
397
- 'fnmatch-style file and/or directory patterns to exclude from generation'
398
- ),
399
- )
400
-
401
- parser.add_argument(
402
- '-o',
403
- '--output-dir',
404
- action='store',
405
- dest='destdir',
406
- required=True,
407
- help=__('directory to place all output'),
408
- )
409
- parser.add_argument(
410
- '-q',
411
- action='store_true',
412
- dest='quiet',
413
- help=__('no output on stdout, just warnings on stderr'),
414
- )
415
- parser.add_argument(
416
- '-d',
417
- '--maxdepth',
418
- action='store',
419
- dest='maxdepth',
420
- type=int,
421
- default=4,
422
- help=__('maximum depth of submodules to show in the TOC (default: 4)'),
423
- )
424
- parser.add_argument(
425
- '-f',
426
- '--force',
427
- action='store_true',
428
- dest='force',
429
- help=__('overwrite existing files'),
430
- )
431
- parser.add_argument(
432
- '-l',
433
- '--follow-links',
434
- action='store_true',
435
- dest='followlinks',
436
- default=False,
437
- help=__(
438
- 'follow symbolic links. Powerful when combined with collective.recipe.omelette.'
439
- ),
440
- )
441
- parser.add_argument(
442
- '-n',
443
- '--dry-run',
444
- action='store_true',
445
- dest='dryrun',
446
- help=__('run the script without creating files'),
447
- )
448
- parser.add_argument(
449
- '-e',
450
- '--separate',
451
- action='store_true',
452
- dest='separatemodules',
453
- help=__('put documentation for each module on its own page'),
454
- )
455
- parser.add_argument(
456
- '-P',
457
- '--private',
458
- action='store_true',
459
- dest='includeprivate',
460
- help=__('include "_private" modules'),
461
- )
462
- parser.add_argument(
463
- '--tocfile',
464
- action='store',
465
- dest='tocfile',
466
- default='modules',
467
- help=__('filename of table of contents (default: modules)'),
468
- )
469
- parser.add_argument(
470
- '-T',
471
- '--no-toc',
472
- action='store_false',
473
- dest='tocfile',
474
- help=__("don't create a table of contents file"),
475
- )
476
- parser.add_argument(
477
- '-E',
478
- '--no-headings',
479
- action='store_true',
480
- dest='noheadings',
481
- help=__(
482
- "don't create headings for the module/package "
483
- 'packages (e.g. when the docstrings already '
484
- 'contain them)'
485
- ),
486
- )
487
- parser.add_argument(
488
- '-M',
489
- '--module-first',
490
- action='store_true',
491
- dest='modulefirst',
492
- help=__('put module documentation before submodule documentation'),
493
- )
494
- parser.add_argument(
495
- '--implicit-namespaces',
496
- action='store_true',
497
- dest='implicit_namespaces',
498
- help=__(
499
- 'interpret module paths according to PEP-0420 implicit namespaces specification'
500
- ),
501
- )
502
- parser.add_argument(
503
- '-s',
504
- '--suffix',
505
- action='store',
506
- dest='suffix',
507
- default='rst',
508
- help=__('file suffix (default: rst)'),
509
- )
510
- exclusive_group = parser.add_mutually_exclusive_group()
511
- exclusive_group.add_argument(
512
- '--remove-old',
513
- action='store_true',
514
- dest='remove_old',
515
- help=__(
516
- 'Remove existing files in the output directory that were not generated'
517
- ),
518
- )
519
- exclusive_group.add_argument(
520
- '-F',
521
- '--full',
522
- action='store_true',
523
- dest='full',
524
- help=__('generate a full project with sphinx-quickstart'),
525
- )
526
- parser.add_argument(
527
- '-a',
528
- '--append-syspath',
529
- action='store_true',
530
- dest='append_syspath',
531
- help=__('append module_path to sys.path, used when --full is given'),
532
- )
533
- parser.add_argument(
534
- '-H',
535
- '--doc-project',
536
- action='store',
537
- dest='header',
538
- help=__('project name (default: root module name)'),
539
- )
540
- parser.add_argument(
541
- '-A',
542
- '--doc-author',
543
- action='store',
544
- dest='author',
545
- help=__('project author(s), used when --full is given'),
546
- )
547
- parser.add_argument(
548
- '-V',
549
- '--doc-version',
550
- action='store',
551
- dest='version',
552
- help=__('project version, used when --full is given'),
553
- )
554
- parser.add_argument(
555
- '-R',
556
- '--doc-release',
557
- action='store',
558
- dest='release',
559
- help=__(
560
- 'project release, used when --full is given, defaults to --doc-version'
561
- ),
562
- )
563
-
564
- group = parser.add_argument_group(__('extension options'))
565
- group.add_argument(
566
- '--extensions',
567
- metavar='EXTENSIONS',
568
- dest='extensions',
569
- action='append',
570
- help=__('enable arbitrary extensions'),
571
- )
572
- for ext in EXTENSIONS:
573
- group.add_argument(
574
- '--ext-%s' % ext,
575
- action='append_const',
576
- const='sphinx.ext.%s' % ext,
577
- dest='extensions',
578
- help=__('enable %s extension') % ext,
579
- )
580
-
581
- group = parser.add_argument_group(__('Project templating'))
582
- group.add_argument(
583
- '-t',
584
- '--templatedir',
585
- metavar='TEMPLATEDIR',
586
- dest='templatedir',
587
- help=__('template directory for template files'),
588
- )
589
-
590
- return parser
591
-
592
-
593
- class CliOptions(Protocol):
594
- """Arguments parsed from the command line."""
595
-
596
- module_path: str
597
- exclude_pattern: list[str]
598
- destdir: str
599
- quiet: bool
600
- maxdepth: int
601
- force: bool
602
- followlinks: bool
603
- dryrun: bool
604
- separatemodules: bool
605
- includeprivate: bool
606
- tocfile: str
607
- noheadings: bool
608
- modulefirst: bool
609
- implicit_namespaces: bool
610
- suffix: str
611
- full: bool
612
- append_syspath: bool
613
- header: str | None
614
- author: str | None
615
- version: str | None
616
- release: str | None
617
- extensions: list[str] | None
618
- templatedir: str | None
619
- remove_old: bool
620
-
621
-
622
- def main(argv: Sequence[str] = (), /) -> int:
623
- """Parse and check the command line arguments."""
624
- locale.setlocale(locale.LC_ALL, '')
625
- sphinx.locale.init_console()
626
-
627
- parser = get_parser()
628
- args: CliOptions = parser.parse_args(argv or sys.argv[1:])
629
-
630
- rootpath = path.abspath(args.module_path)
631
-
632
- # normalize opts
633
-
634
- if args.header is None:
635
- args.header = rootpath.split(path.sep)[-1]
636
- args.suffix = args.suffix.removeprefix('.')
637
- if not Path(rootpath).is_dir():
638
- logger.error(__('%s is not a directory.'), rootpath)
639
- raise SystemExit(1)
640
- if not args.dryrun:
641
- ensuredir(args.destdir)
642
- excludes = tuple(
643
- re.compile(fnmatch.translate(path.abspath(exclude)))
644
- for exclude in dict.fromkeys(args.exclude_pattern)
645
- )
646
- written_files, modules = recurse_tree(rootpath, excludes, args, args.templatedir)
647
-
648
- if args.full:
649
- from sphinx.cmd import quickstart as qs
650
-
651
- modules.sort()
652
- prev_module = ''
653
- text = ''
654
- for module in modules:
655
- if module.startswith(prev_module + '.'):
656
- continue
657
- prev_module = module
658
- text += ' %s\n' % module
659
- d: dict[str, Any] = {
660
- 'path': args.destdir,
661
- 'sep': False,
662
- 'dot': '_',
663
- 'project': args.header,
664
- 'author': args.author or 'Author',
665
- 'version': args.version or '',
666
- 'release': args.release or args.version or '',
667
- 'suffix': '.' + args.suffix,
668
- 'master': 'index',
669
- 'epub': True,
670
- 'extensions': [
671
- 'sphinx.ext.autodoc',
672
- 'sphinx.ext.viewcode',
673
- 'sphinx.ext.todo',
674
- ],
675
- 'makefile': True,
676
- 'batchfile': True,
677
- 'make_mode': True,
678
- 'mastertocmaxdepth': args.maxdepth,
679
- 'mastertoctree': text,
680
- 'language': 'en',
681
- 'module_path': rootpath,
682
- 'append_syspath': args.append_syspath,
683
- }
684
- if args.extensions:
685
- d['extensions'].extend(args.extensions)
686
- if args.quiet:
687
- d['quiet'] = True
688
-
689
- for ext in d['extensions'][:]:
690
- if ',' in ext:
691
- d['extensions'].remove(ext)
692
- d['extensions'].extend(ext.split(','))
693
-
694
- if not args.dryrun:
695
- qs.generate(
696
- d, silent=True, overwrite=args.force, templatedir=args.templatedir
697
- )
698
- elif args.tocfile:
699
- written_files.append(
700
- create_modules_toc_file(modules, args, args.tocfile, args.templatedir)
701
- )
702
-
703
- if args.remove_old and not args.dryrun:
704
- for existing in Path(args.destdir).glob(f'**/*.{args.suffix}'):
705
- if existing not in written_files:
706
- try:
707
- existing.unlink()
708
- except OSError as exc:
709
- logger.warning(
710
- __('Failed to remove %s: %s'),
711
- existing,
712
- exc.strerror,
713
- type='autodoc',
714
- )
715
-
716
- return 0
717
-
718
-
719
- # So program can be started with "python -m sphinx.apidoc ..."
720
- if __name__ == '__main__':
721
- raise SystemExit(main(sys.argv[1:]))