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