Sphinx 8.0.1__py3-none-any.whl → 8.1.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.
- sphinx/__init__.py +6 -3
- sphinx/_cli/__init__.py +40 -20
- sphinx/_cli/util/colour.py +5 -4
- sphinx/_cli/util/errors.py +28 -11
- sphinx/application.py +361 -38
- sphinx/builders/__init__.py +229 -83
- sphinx/builders/_epub_base.py +118 -71
- sphinx/builders/changes.py +39 -21
- sphinx/builders/dirhtml.py +4 -4
- sphinx/builders/dummy.py +2 -5
- sphinx/builders/epub3.py +43 -22
- sphinx/builders/gettext.py +43 -25
- sphinx/builders/html/__init__.py +284 -218
- sphinx/builders/html/_assets.py +62 -26
- sphinx/builders/html/_build_info.py +76 -0
- sphinx/builders/html/transforms.py +11 -9
- sphinx/builders/latex/__init__.py +139 -81
- sphinx/builders/latex/constants.py +7 -7
- sphinx/builders/latex/nodes.py +3 -2
- sphinx/builders/latex/theming.py +7 -5
- sphinx/builders/latex/transforms.py +27 -19
- sphinx/builders/linkcheck.py +146 -72
- sphinx/builders/manpage.py +30 -13
- sphinx/builders/singlehtml.py +22 -14
- sphinx/builders/texinfo.py +67 -37
- sphinx/builders/text.py +5 -5
- sphinx/builders/xml.py +6 -9
- sphinx/cmd/build.py +282 -103
- sphinx/cmd/make_mode.py +106 -63
- sphinx/cmd/quickstart.py +341 -145
- sphinx/config.py +45 -12
- sphinx/deprecation.py +8 -2
- sphinx/directives/__init__.py +28 -19
- sphinx/directives/code.py +86 -56
- sphinx/directives/other.py +50 -36
- sphinx/directives/patches.py +29 -19
- sphinx/domains/__init__.py +20 -120
- sphinx/domains/_domains_container.py +281 -0
- sphinx/domains/_index.py +110 -0
- sphinx/domains/c/__init__.py +3 -3
- sphinx/domains/c/_parser.py +10 -6
- sphinx/domains/changeset.py +5 -3
- sphinx/domains/citation.py +5 -3
- sphinx/domains/cpp/__init__.py +9 -11
- sphinx/domains/cpp/_parser.py +8 -7
- sphinx/domains/index.py +3 -3
- sphinx/domains/javascript.py +12 -7
- sphinx/domains/math.py +2 -2
- sphinx/domains/python/__init__.py +10 -5
- sphinx/domains/python/_object.py +1 -1
- sphinx/domains/rst.py +5 -5
- sphinx/domains/std/__init__.py +16 -11
- sphinx/environment/__init__.py +206 -146
- sphinx/environment/adapters/asset.py +3 -2
- sphinx/environment/adapters/indexentries.py +74 -33
- sphinx/environment/adapters/toctree.py +100 -43
- sphinx/environment/collectors/__init__.py +19 -8
- sphinx/environment/collectors/asset.py +47 -15
- sphinx/environment/collectors/dependencies.py +8 -4
- sphinx/environment/collectors/metadata.py +7 -2
- sphinx/environment/collectors/title.py +7 -2
- sphinx/environment/collectors/toctree.py +54 -22
- sphinx/errors.py +4 -1
- sphinx/events.py +314 -7
- sphinx/ext/apidoc.py +42 -18
- sphinx/ext/autodoc/__init__.py +52 -24
- sphinx/ext/autodoc/importer.py +6 -9
- sphinx/ext/autosectionlabel.py +1 -2
- sphinx/ext/autosummary/__init__.py +3 -1
- sphinx/ext/autosummary/generate.py +28 -14
- sphinx/ext/coverage.py +7 -7
- sphinx/ext/doctest.py +4 -8
- sphinx/ext/duration.py +6 -5
- sphinx/ext/inheritance_diagram.py +1 -1
- sphinx/ext/intersphinx/_cli.py +6 -4
- sphinx/ext/intersphinx/_load.py +77 -32
- sphinx/ext/intersphinx/_resolve.py +173 -79
- sphinx/ext/intersphinx/_shared.py +7 -5
- sphinx/ext/linkcode.py +7 -1
- sphinx/ext/mathjax.py +1 -2
- sphinx/ext/napoleon/__init__.py +37 -24
- sphinx/ext/napoleon/docstring.py +202 -134
- sphinx/ext/todo.py +5 -3
- sphinx/highlighting.py +10 -3
- sphinx/io.py +1 -1
- sphinx/jinja2glue.py +27 -6
- sphinx/locale/__init__.py +6 -2
- sphinx/locale/ar/LC_MESSAGES/sphinx.js +8 -1
- sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ar/LC_MESSAGES/sphinx.po +2246 -2288
- sphinx/locale/bg/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bg/LC_MESSAGES/sphinx.po +2113 -2159
- sphinx/locale/bn/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bn/LC_MESSAGES/sphinx.po +2349 -2395
- sphinx/locale/ca/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca/LC_MESSAGES/sphinx.po +2846 -2892
- sphinx/locale/cak/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.po +2213 -2259
- sphinx/locale/cs/LC_MESSAGES/sphinx.js +6 -1
- sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cs/LC_MESSAGES/sphinx.po +2225 -2269
- sphinx/locale/cy/LC_MESSAGES/sphinx.js +6 -1
- sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cy/LC_MESSAGES/sphinx.po +2403 -2447
- sphinx/locale/da/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/da/LC_MESSAGES/sphinx.po +2214 -2260
- sphinx/locale/de/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de/LC_MESSAGES/sphinx.po +2230 -2276
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2113 -2159
- sphinx/locale/el/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/el/LC_MESSAGES/sphinx.po +2619 -2665
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2113 -2159
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2113 -2159
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2519 -2565
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2113 -2159
- sphinx/locale/eo/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eo/LC_MESSAGES/sphinx.po +2232 -2278
- sphinx/locale/es/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es/LC_MESSAGES/sphinx.po +2516 -2561
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2114 -2159
- sphinx/locale/et/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/et/LC_MESSAGES/sphinx.po +2317 -2363
- sphinx/locale/eu/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eu/LC_MESSAGES/sphinx.po +2218 -2264
- sphinx/locale/fa/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fa/LC_MESSAGES/sphinx.po +2505 -2551
- sphinx/locale/fi/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fi/LC_MESSAGES/sphinx.po +2303 -2349
- sphinx/locale/fr/LC_MESSAGES/sphinx.js +6 -2
- sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr/LC_MESSAGES/sphinx.po +2863 -2908
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +2114 -2159
- sphinx/locale/gl/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.po +2571 -2617
- sphinx/locale/he/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.po +2307 -2352
- sphinx/locale/hi/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi/LC_MESSAGES/sphinx.po +2580 -2626
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +2113 -2159
- sphinx/locale/hr/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hr/LC_MESSAGES/sphinx.po +2238 -2283
- sphinx/locale/hu/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hu/LC_MESSAGES/sphinx.po +2228 -2274
- sphinx/locale/id/LC_MESSAGES/sphinx.js +3 -1
- sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/id/LC_MESSAGES/sphinx.po +2787 -2834
- sphinx/locale/is/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/is/LC_MESSAGES/sphinx.po +2224 -2270
- sphinx/locale/it/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/it/LC_MESSAGES/sphinx.po +2231 -2276
- sphinx/locale/ja/LC_MESSAGES/sphinx.js +3 -1
- sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ja/LC_MESSAGES/sphinx.po +2507 -2554
- sphinx/locale/ka/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ka/LC_MESSAGES/sphinx.po +2428 -2474
- sphinx/locale/ko/LC_MESSAGES/sphinx.js +3 -1
- sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ko/LC_MESSAGES/sphinx.po +2516 -2563
- sphinx/locale/lt/LC_MESSAGES/sphinx.js +6 -1
- sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lt/LC_MESSAGES/sphinx.po +2425 -2469
- sphinx/locale/lv/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lv/LC_MESSAGES/sphinx.po +2362 -2407
- sphinx/locale/mk/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/mk/LC_MESSAGES/sphinx.po +2121 -2167
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2220 -2266
- sphinx/locale/ne/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ne/LC_MESSAGES/sphinx.po +2221 -2267
- sphinx/locale/nl/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nl/LC_MESSAGES/sphinx.po +2240 -2286
- sphinx/locale/pl/LC_MESSAGES/sphinx.js +6 -1
- sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pl/LC_MESSAGES/sphinx.po +2319 -2363
- sphinx/locale/pt/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt/LC_MESSAGES/sphinx.po +2114 -2159
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2854 -2899
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2224 -2269
- sphinx/locale/ro/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ro/LC_MESSAGES/sphinx.po +2226 -2271
- sphinx/locale/ru/LC_MESSAGES/sphinx.js +8 -3
- sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ru/LC_MESSAGES/sphinx.po +2841 -2885
- sphinx/locale/si/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/si/LC_MESSAGES/sphinx.po +2294 -2340
- sphinx/locale/sk/LC_MESSAGES/sphinx.js +6 -1
- sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sk/LC_MESSAGES/sphinx.po +2497 -2541
- sphinx/locale/sl/LC_MESSAGES/sphinx.js +6 -1
- sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sl/LC_MESSAGES/sphinx.po +2331 -2375
- sphinx/locale/sphinx.pot +2121 -2167
- sphinx/locale/sq/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sq/LC_MESSAGES/sphinx.po +2855 -2901
- sphinx/locale/sr/LC_MESSAGES/sphinx.js +5 -1
- sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr/LC_MESSAGES/sphinx.po +2203 -2248
- sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.po +2423 -2469
- sphinx/locale/te/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/te/LC_MESSAGES/sphinx.po +2113 -2159
- sphinx/locale/tr/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/tr/LC_MESSAGES/sphinx.po +2443 -2489
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.js +6 -1
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2329 -2373
- sphinx/locale/ur/LC_MESSAGES/sphinx.js +4 -1
- sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ur/LC_MESSAGES/sphinx.po +2113 -2159
- sphinx/locale/vi/LC_MESSAGES/sphinx.js +3 -1
- sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/vi/LC_MESSAGES/sphinx.po +2199 -2246
- sphinx/locale/yue/LC_MESSAGES/sphinx.js +3 -1
- sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/yue/LC_MESSAGES/sphinx.po +2112 -2159
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.js +3 -1
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2112 -2159
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.js +3 -1
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2845 -2892
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.js +3 -1
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2112 -2159
- sphinx/parsers.py +3 -1
- sphinx/project.py +6 -2
- sphinx/pycode/__init__.py +11 -4
- sphinx/pycode/ast.py +58 -58
- sphinx/pycode/parser.py +49 -28
- sphinx/pygments_styles.py +49 -49
- sphinx/registry.py +8 -3
- sphinx/roles.py +133 -13
- sphinx/search/__init__.py +146 -87
- sphinx/search/da.py +2 -4
- sphinx/search/de.py +2 -4
- sphinx/search/en.py +4 -4
- sphinx/search/es.py +2 -4
- sphinx/search/fi.py +2 -4
- sphinx/search/fr.py +2 -4
- sphinx/search/hu.py +2 -4
- sphinx/search/it.py +2 -4
- sphinx/search/ja.py +55 -32
- sphinx/search/nl.py +2 -4
- sphinx/search/no.py +2 -4
- sphinx/search/pt.py +2 -4
- sphinx/search/ro.py +0 -2
- sphinx/search/ru.py +2 -4
- sphinx/search/sv.py +2 -4
- sphinx/search/tr.py +0 -2
- sphinx/search/zh.py +18 -13
- sphinx/templates/graphviz/graphviz.css +0 -7
- sphinx/testing/fixtures.py +6 -5
- sphinx/testing/path.py +7 -5
- sphinx/testing/util.py +63 -29
- sphinx/texinputs/sphinx.sty +107 -39
- sphinx/texinputs/sphinxlatexadmonitions.sty +51 -35
- sphinx/texinputs/sphinxlatexcontainers.sty +1 -1
- sphinx/texinputs/sphinxlatexgraphics.sty +3 -2
- sphinx/texinputs/sphinxlatexindbibtoc.sty +1 -1
- sphinx/texinputs/sphinxlatexlists.sty +1 -1
- sphinx/texinputs/sphinxlatexliterals.sty +4 -1
- sphinx/texinputs/sphinxlatexnumfig.sty +22 -9
- sphinx/texinputs/sphinxlatexobjects.sty +1 -1
- sphinx/texinputs/sphinxlatexshadowbox.sty +72 -10
- sphinx/texinputs/sphinxlatexstyleheadings.sty +7 -2
- sphinx/texinputs/sphinxlatexstylepage.sty +2 -8
- sphinx/texinputs/sphinxlatexstyletext.sty +2 -4
- sphinx/texinputs/sphinxlatextables.sty +1 -1
- sphinx/texinputs/sphinxoptionsgeometry.sty +1 -1
- sphinx/texinputs/sphinxoptionshyperref.sty +1 -1
- sphinx/themes/agogo/layout.html +1 -10
- sphinx/themes/agogo/static/agogo.css.jinja +0 -7
- sphinx/themes/basic/defindex.html +1 -8
- sphinx/themes/basic/domainindex.html +1 -9
- sphinx/themes/basic/genindex-single.html +1 -9
- sphinx/themes/basic/genindex-split.html +1 -9
- sphinx/themes/basic/genindex.html +1 -9
- sphinx/themes/basic/globaltoc.html +1 -9
- sphinx/themes/basic/layout.html +1 -9
- sphinx/themes/basic/localtoc.html +1 -9
- sphinx/themes/basic/page.html +1 -9
- sphinx/themes/basic/relations.html +1 -9
- sphinx/themes/basic/search.html +1 -9
- sphinx/themes/basic/searchbox.html +1 -9
- sphinx/themes/basic/searchfield.html +4 -10
- sphinx/themes/basic/sourcelink.html +1 -9
- sphinx/themes/basic/static/basic.css.jinja +2 -13
- sphinx/themes/basic/static/doctools.js +0 -7
- sphinx/themes/basic/static/language_data.js.jinja +0 -7
- sphinx/themes/basic/static/searchtools.js +25 -13
- sphinx/themes/bizstyle/layout.html +1 -9
- sphinx/themes/bizstyle/static/bizstyle.css.jinja +0 -7
- sphinx/themes/bizstyle/static/bizstyle.js.jinja +5 -11
- sphinx/themes/classic/layout.html +1 -9
- sphinx/themes/classic/static/classic.css.jinja +0 -7
- sphinx/themes/classic/static/sidebar.js.jinja +0 -6
- sphinx/themes/epub/epub-cover.html +1 -9
- sphinx/themes/epub/layout.html +1 -9
- sphinx/themes/epub/static/epub.css.jinja +0 -7
- sphinx/themes/haiku/layout.html +1 -9
- sphinx/themes/haiku/static/haiku.css.jinja +0 -6
- sphinx/themes/nature/static/nature.css.jinja +0 -7
- sphinx/themes/nonav/layout.html +1 -9
- sphinx/themes/nonav/static/nonav.css.jinja +0 -7
- sphinx/themes/pyramid/static/epub.css.jinja +0 -7
- sphinx/themes/pyramid/static/pyramid.css.jinja +0 -7
- sphinx/themes/scrolls/layout.html +1 -10
- sphinx/themes/scrolls/static/scrolls.css.jinja +0 -7
- sphinx/themes/sphinxdoc/static/sphinxdoc.css.jinja +2 -7
- sphinx/themes/traditional/static/traditional.css.jinja +0 -7
- sphinx/theming.py +18 -6
- sphinx/transforms/__init__.py +56 -35
- sphinx/transforms/compact_bullet_list.py +3 -2
- sphinx/transforms/i18n.py +132 -50
- sphinx/transforms/post_transforms/__init__.py +94 -43
- sphinx/transforms/post_transforms/code.py +7 -6
- sphinx/transforms/post_transforms/images.py +71 -54
- sphinx/transforms/references.py +1 -2
- sphinx/util/__init__.py +23 -194
- sphinx/util/_files.py +80 -0
- sphinx/util/_importer.py +27 -0
- sphinx/util/_io.py +1 -2
- sphinx/util/_lines.py +26 -0
- sphinx/util/_pathlib.py +5 -2
- sphinx/util/_serialise.py +53 -0
- sphinx/util/_timestamps.py +2 -1
- sphinx/util/_uri.py +16 -0
- sphinx/util/cfamily.py +48 -25
- sphinx/util/console.py +1 -0
- sphinx/util/display.py +1 -1
- sphinx/util/docfields.py +125 -45
- sphinx/util/docstrings.py +1 -1
- sphinx/util/docutils.py +118 -44
- sphinx/util/exceptions.py +11 -5
- sphinx/util/fileutil.py +53 -32
- sphinx/util/http_date.py +9 -7
- sphinx/util/i18n.py +49 -16
- sphinx/util/images.py +7 -6
- sphinx/util/inspect.py +29 -12
- sphinx/util/inventory.py +47 -29
- sphinx/util/logging.py +58 -85
- sphinx/util/matching.py +3 -3
- sphinx/util/math.py +1 -1
- sphinx/util/nodes.py +176 -108
- sphinx/util/osutil.py +13 -10
- sphinx/util/parallel.py +5 -4
- sphinx/util/parsing.py +5 -3
- sphinx/util/png.py +3 -3
- sphinx/util/requests.py +8 -4
- sphinx/util/rst.py +5 -3
- sphinx/util/tags.py +5 -2
- sphinx/util/template.py +26 -11
- sphinx/util/texescape.py +2 -2
- sphinx/util/typing.py +89 -38
- sphinx/versioning.py +3 -1
- sphinx/writers/html.py +22 -7
- sphinx/writers/html5.py +113 -64
- sphinx/writers/latex.py +408 -221
- sphinx/writers/manpage.py +25 -15
- sphinx/writers/texinfo.py +94 -82
- sphinx/writers/text.py +87 -53
- sphinx/writers/xml.py +5 -4
- sphinx-8.1.0.dist-info/LICENSE.rst +31 -0
- {sphinx-8.0.1.dist-info → sphinx-8.1.0.dist-info}/METADATA +13 -11
- sphinx-8.1.0.dist-info/RECORD +598 -0
- sphinx-8.0.1.dist-info/LICENSE.rst +0 -67
- sphinx-8.0.1.dist-info/RECORD +0 -590
- {sphinx-8.0.1.dist-info → sphinx-8.1.0.dist-info}/WHEEL +0 -0
- {sphinx-8.0.1.dist-info → sphinx-8.1.0.dist-info}/entry_points.txt +0 -0
sphinx/builders/linkcheck.py
CHANGED
|
@@ -21,7 +21,8 @@ from requests.exceptions import Timeout as RequestTimeout
|
|
|
21
21
|
from sphinx.builders.dummy import DummyBuilder
|
|
22
22
|
from sphinx.locale import __
|
|
23
23
|
from sphinx.transforms.post_transforms import SphinxPostTransform
|
|
24
|
-
from sphinx.util import
|
|
24
|
+
from sphinx.util import logging, requests
|
|
25
|
+
from sphinx.util._uri import encode_uri
|
|
25
26
|
from sphinx.util.console import darkgray, darkgreen, purple, red, turquoise
|
|
26
27
|
from sphinx.util.http_date import rfc1123_to_epoch
|
|
27
28
|
from sphinx.util.nodes import get_node_line
|
|
@@ -39,7 +40,8 @@ if TYPE_CHECKING:
|
|
|
39
40
|
|
|
40
41
|
logger = logging.getLogger(__name__)
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
# matches to foo:// and // (a protocol relative URL)
|
|
44
|
+
uri_re = re.compile('([a-z]+:)?//')
|
|
43
45
|
|
|
44
46
|
DEFAULT_REQUEST_HEADERS = {
|
|
45
47
|
'Accept': 'text/html,application/xhtml+xml;q=0.9,*/*;q=0.8',
|
|
@@ -55,8 +57,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
|
|
55
57
|
"""
|
|
56
58
|
|
|
57
59
|
name = 'linkcheck'
|
|
58
|
-
epilog = __('Look for any errors in the above output or in '
|
|
59
|
-
'%(outdir)s/output.txt')
|
|
60
|
+
epilog = __('Look for any errors in the above output or in %(outdir)s/output.txt')
|
|
60
61
|
|
|
61
62
|
def init(self) -> None:
|
|
62
63
|
self.broken_hyperlinks = 0
|
|
@@ -71,8 +72,10 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
|
|
71
72
|
|
|
72
73
|
output_text = path.join(self.outdir, 'output.txt')
|
|
73
74
|
output_json = path.join(self.outdir, 'output.json')
|
|
74
|
-
with
|
|
75
|
-
|
|
75
|
+
with (
|
|
76
|
+
open(output_text, 'w', encoding='utf-8') as self.txt_outfile,
|
|
77
|
+
open(output_json, 'w', encoding='utf-8') as self.json_outfile,
|
|
78
|
+
):
|
|
76
79
|
for result in checker.check(self.hyperlinks):
|
|
77
80
|
self.process_result(result)
|
|
78
81
|
|
|
@@ -83,9 +86,12 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
|
|
83
86
|
filename = self.env.doc2path(result.docname, False)
|
|
84
87
|
|
|
85
88
|
linkstat: dict[str, str | int] = {
|
|
86
|
-
'filename': str(filename),
|
|
87
|
-
'
|
|
88
|
-
'
|
|
89
|
+
'filename': str(filename),
|
|
90
|
+
'lineno': result.lineno,
|
|
91
|
+
'status': result.status,
|
|
92
|
+
'code': result.code,
|
|
93
|
+
'uri': result.uri,
|
|
94
|
+
'info': result.message,
|
|
89
95
|
}
|
|
90
96
|
self.write_linkstat(linkstat)
|
|
91
97
|
|
|
@@ -102,26 +108,48 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
|
|
102
108
|
logger.info(darkgray('-ignored- ') + result.uri)
|
|
103
109
|
elif result.status == 'local':
|
|
104
110
|
logger.info(darkgray('-local- ') + result.uri)
|
|
105
|
-
self.write_entry(
|
|
111
|
+
self.write_entry(
|
|
112
|
+
'local', result.docname, filename, result.lineno, result.uri
|
|
113
|
+
)
|
|
106
114
|
elif result.status == 'working':
|
|
107
115
|
logger.info(darkgreen('ok ') + result.uri + result.message)
|
|
108
116
|
elif result.status == 'timeout':
|
|
109
|
-
if self.app.quiet
|
|
110
|
-
logger.warning(
|
|
111
|
-
|
|
117
|
+
if self.app.quiet:
|
|
118
|
+
logger.warning(
|
|
119
|
+
'timeout ' + result.uri + result.message,
|
|
120
|
+
location=(result.docname, result.lineno),
|
|
121
|
+
)
|
|
112
122
|
else:
|
|
113
|
-
logger.info(
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
logger.info(
|
|
124
|
+
red('timeout ') + result.uri + red(' - ' + result.message)
|
|
125
|
+
)
|
|
126
|
+
self.write_entry(
|
|
127
|
+
'timeout',
|
|
128
|
+
result.docname,
|
|
129
|
+
filename,
|
|
130
|
+
result.lineno,
|
|
131
|
+
result.uri + ': ' + result.message,
|
|
132
|
+
)
|
|
116
133
|
self.timed_out_hyperlinks += 1
|
|
117
134
|
elif result.status == 'broken':
|
|
118
|
-
if self.app.quiet
|
|
119
|
-
logger.warning(
|
|
120
|
-
|
|
135
|
+
if self.app.quiet:
|
|
136
|
+
logger.warning(
|
|
137
|
+
__('broken link: %s (%s)'),
|
|
138
|
+
result.uri,
|
|
139
|
+
result.message,
|
|
140
|
+
location=(result.docname, result.lineno),
|
|
141
|
+
)
|
|
121
142
|
else:
|
|
122
|
-
logger.info(
|
|
123
|
-
|
|
124
|
-
|
|
143
|
+
logger.info(
|
|
144
|
+
red('broken ') + result.uri + red(' - ' + result.message)
|
|
145
|
+
)
|
|
146
|
+
self.write_entry(
|
|
147
|
+
'broken',
|
|
148
|
+
result.docname,
|
|
149
|
+
filename,
|
|
150
|
+
result.lineno,
|
|
151
|
+
result.uri + ': ' + result.message,
|
|
152
|
+
)
|
|
125
153
|
self.broken_hyperlinks += 1
|
|
126
154
|
elif result.status == 'redirected':
|
|
127
155
|
try:
|
|
@@ -136,13 +164,23 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
|
|
136
164
|
text, color = ('with unknown code', purple)
|
|
137
165
|
linkstat['text'] = text
|
|
138
166
|
if self.config.linkcheck_allowed_redirects:
|
|
139
|
-
logger.warning(
|
|
140
|
-
|
|
167
|
+
logger.warning(
|
|
168
|
+
'redirect ' + result.uri + ' - ' + text + ' to ' + result.message,
|
|
169
|
+
location=(result.docname, result.lineno),
|
|
170
|
+
)
|
|
141
171
|
else:
|
|
142
|
-
logger.info(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
172
|
+
logger.info(
|
|
173
|
+
color('redirect ')
|
|
174
|
+
+ result.uri
|
|
175
|
+
+ color(' - ' + text + ' to ' + result.message)
|
|
176
|
+
)
|
|
177
|
+
self.write_entry(
|
|
178
|
+
'redirected ' + text,
|
|
179
|
+
result.docname,
|
|
180
|
+
filename,
|
|
181
|
+
result.lineno,
|
|
182
|
+
result.uri + ' to ' + result.message,
|
|
183
|
+
)
|
|
146
184
|
else:
|
|
147
185
|
raise ValueError('Unknown status %s.' % result.status)
|
|
148
186
|
|
|
@@ -150,8 +188,9 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
|
|
150
188
|
self.json_outfile.write(json.dumps(data))
|
|
151
189
|
self.json_outfile.write('\n')
|
|
152
190
|
|
|
153
|
-
def write_entry(
|
|
154
|
-
|
|
191
|
+
def write_entry(
|
|
192
|
+
self, what: str, docname: str, filename: _StrPath, line: int, uri: str
|
|
193
|
+
) -> None:
|
|
155
194
|
self.txt_outfile.write(f'{filename}:{line}: [{what}] {uri}\n')
|
|
156
195
|
|
|
157
196
|
|
|
@@ -220,7 +259,9 @@ class HyperlinkCollector(SphinxPostTransform):
|
|
|
220
259
|
lineno = -1
|
|
221
260
|
|
|
222
261
|
if uri not in hyperlinks:
|
|
223
|
-
hyperlinks[uri] = Hyperlink(
|
|
262
|
+
hyperlinks[uri] = Hyperlink(
|
|
263
|
+
uri, docname, self.env.doc2path(docname), lineno
|
|
264
|
+
)
|
|
224
265
|
|
|
225
266
|
|
|
226
267
|
class Hyperlink(NamedTuple):
|
|
@@ -239,8 +280,9 @@ class HyperlinkAvailabilityChecker:
|
|
|
239
280
|
self.wqueue: PriorityQueue[CheckRequest] = PriorityQueue()
|
|
240
281
|
self.num_workers: int = config.linkcheck_workers
|
|
241
282
|
|
|
242
|
-
self.to_ignore: list[re.Pattern[str]] = list(
|
|
243
|
-
|
|
283
|
+
self.to_ignore: list[re.Pattern[str]] = list(
|
|
284
|
+
map(re.compile, self.config.linkcheck_ignore)
|
|
285
|
+
)
|
|
244
286
|
|
|
245
287
|
def check(self, hyperlinks: dict[str, Hyperlink]) -> Iterator[CheckResult]:
|
|
246
288
|
self.invoke_threads()
|
|
@@ -248,8 +290,9 @@ class HyperlinkAvailabilityChecker:
|
|
|
248
290
|
total_links = 0
|
|
249
291
|
for hyperlink in hyperlinks.values():
|
|
250
292
|
if self.is_ignored_uri(hyperlink.uri):
|
|
251
|
-
yield CheckResult(
|
|
252
|
-
|
|
293
|
+
yield CheckResult(
|
|
294
|
+
hyperlink.uri, hyperlink.docname, hyperlink.lineno, 'ignored', '', 0
|
|
295
|
+
)
|
|
253
296
|
else:
|
|
254
297
|
self.wqueue.put(CheckRequest(CHECK_IMMEDIATELY, hyperlink), False)
|
|
255
298
|
total_links += 1
|
|
@@ -263,9 +306,9 @@ class HyperlinkAvailabilityChecker:
|
|
|
263
306
|
|
|
264
307
|
def invoke_threads(self) -> None:
|
|
265
308
|
for _i in range(self.num_workers):
|
|
266
|
-
thread = HyperlinkAvailabilityCheckWorker(
|
|
267
|
-
|
|
268
|
-
|
|
309
|
+
thread = HyperlinkAvailabilityCheckWorker(
|
|
310
|
+
self.config, self.rqueue, self.wqueue, self.rate_limits
|
|
311
|
+
)
|
|
269
312
|
thread.start()
|
|
270
313
|
self.workers.append(thread)
|
|
271
314
|
|
|
@@ -295,25 +338,35 @@ class CheckResult(NamedTuple):
|
|
|
295
338
|
class HyperlinkAvailabilityCheckWorker(Thread):
|
|
296
339
|
"""A worker class for checking the availability of hyperlinks."""
|
|
297
340
|
|
|
298
|
-
def __init__(
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
341
|
+
def __init__(
|
|
342
|
+
self,
|
|
343
|
+
config: Config,
|
|
344
|
+
rqueue: Queue[CheckResult],
|
|
345
|
+
wqueue: Queue[CheckRequest],
|
|
346
|
+
rate_limits: dict[str, RateLimit],
|
|
347
|
+
) -> None:
|
|
302
348
|
self.rate_limits = rate_limits
|
|
303
349
|
self.rqueue = rqueue
|
|
304
350
|
self.wqueue = wqueue
|
|
305
351
|
|
|
306
352
|
self.anchors_ignore: list[re.Pattern[str]] = list(
|
|
307
|
-
map(re.compile, config.linkcheck_anchors_ignore)
|
|
353
|
+
map(re.compile, config.linkcheck_anchors_ignore)
|
|
354
|
+
)
|
|
308
355
|
self.anchors_ignore_for_url: list[re.Pattern[str]] = list(
|
|
309
|
-
map(re.compile, config.linkcheck_anchors_ignore_for_url)
|
|
356
|
+
map(re.compile, config.linkcheck_anchors_ignore_for_url)
|
|
357
|
+
)
|
|
310
358
|
self.documents_exclude: list[re.Pattern[str]] = list(
|
|
311
|
-
map(re.compile, config.linkcheck_exclude_documents)
|
|
312
|
-
|
|
313
|
-
|
|
359
|
+
map(re.compile, config.linkcheck_exclude_documents)
|
|
360
|
+
)
|
|
361
|
+
self.auth = [
|
|
362
|
+
(re.compile(pattern), auth_info)
|
|
363
|
+
for pattern, auth_info in config.linkcheck_auth
|
|
364
|
+
]
|
|
314
365
|
|
|
315
366
|
self.timeout: int | float | None = config.linkcheck_timeout
|
|
316
|
-
self.request_headers: dict[str, dict[str, str]] =
|
|
367
|
+
self.request_headers: dict[str, dict[str, str]] = (
|
|
368
|
+
config.linkcheck_request_headers
|
|
369
|
+
)
|
|
317
370
|
self.check_anchors: bool = config.linkcheck_anchors
|
|
318
371
|
self.allowed_redirects: dict[re.Pattern[str], re.Pattern[str]]
|
|
319
372
|
self.allowed_redirects = config.linkcheck_allowed_redirects
|
|
@@ -361,12 +414,16 @@ class HyperlinkAvailabilityCheckWorker(Thread):
|
|
|
361
414
|
continue
|
|
362
415
|
status, info, code = self._check(docname, uri, hyperlink)
|
|
363
416
|
if status == 'rate-limited':
|
|
364
|
-
logger.info(
|
|
417
|
+
logger.info(
|
|
418
|
+
darkgray('-rate limited- ') + uri + darkgray(' | sleeping...')
|
|
419
|
+
)
|
|
365
420
|
else:
|
|
366
421
|
self.rqueue.put(CheckResult(uri, docname, lineno, status, info, code))
|
|
367
422
|
self.wqueue.task_done()
|
|
368
423
|
|
|
369
|
-
def _check(
|
|
424
|
+
def _check(
|
|
425
|
+
self, docname: str, uri: str, hyperlink: Hyperlink
|
|
426
|
+
) -> tuple[str, str, int]:
|
|
370
427
|
# check for various conditions without bothering the network
|
|
371
428
|
|
|
372
429
|
for doc_matcher in self.documents_exclude:
|
|
@@ -445,10 +502,13 @@ class HyperlinkAvailabilityCheckWorker(Thread):
|
|
|
445
502
|
error_message = ''
|
|
446
503
|
status_code = -1
|
|
447
504
|
response_url = retry_after = ''
|
|
448
|
-
for retrieval_method, kwargs in self._retrieval_methods(
|
|
505
|
+
for retrieval_method, kwargs in self._retrieval_methods(
|
|
506
|
+
self.check_anchors, anchor
|
|
507
|
+
):
|
|
449
508
|
try:
|
|
450
509
|
with retrieval_method(
|
|
451
|
-
url=req_url,
|
|
510
|
+
url=req_url,
|
|
511
|
+
auth=auth_info,
|
|
452
512
|
headers=headers,
|
|
453
513
|
timeout=self.timeout,
|
|
454
514
|
**kwargs,
|
|
@@ -461,11 +521,17 @@ class HyperlinkAvailabilityCheckWorker(Thread):
|
|
|
461
521
|
except UnicodeDecodeError:
|
|
462
522
|
return 'ignored', 'unable to decode response content', 0
|
|
463
523
|
if not found:
|
|
464
|
-
return
|
|
524
|
+
return (
|
|
525
|
+
'broken',
|
|
526
|
+
__("Anchor '%s' not found") % quote(anchor),
|
|
527
|
+
0,
|
|
528
|
+
)
|
|
465
529
|
|
|
466
530
|
# Copy data we need from the (closed) response
|
|
467
531
|
status_code = response.status_code
|
|
468
|
-
redirect_status_code =
|
|
532
|
+
redirect_status_code = (
|
|
533
|
+
response.history[-1].status_code if response.history else None
|
|
534
|
+
) # NoQA: E501
|
|
469
535
|
retry_after = response.headers.get('Retry-After', '')
|
|
470
536
|
response_url = f'{response.url}'
|
|
471
537
|
response.raise_for_status()
|
|
@@ -521,9 +587,10 @@ class HyperlinkAvailabilityCheckWorker(Thread):
|
|
|
521
587
|
netloc = urlsplit(req_url).netloc
|
|
522
588
|
self.rate_limits.pop(netloc, None)
|
|
523
589
|
|
|
524
|
-
if (
|
|
525
|
-
|
|
526
|
-
|
|
590
|
+
if (
|
|
591
|
+
(response_url.rstrip('/') == req_url.rstrip('/'))
|
|
592
|
+
or _allowed_redirect(req_url, response_url, self.allowed_redirects)
|
|
593
|
+
): # fmt: skip
|
|
527
594
|
return 'working', '', 0
|
|
528
595
|
elif redirect_status_code is not None:
|
|
529
596
|
return 'redirected', response_url, redirect_status_code
|
|
@@ -573,10 +640,12 @@ def _get_request_headers(
|
|
|
573
640
|
request_headers: dict[str, dict[str, str]],
|
|
574
641
|
) -> dict[str, str]:
|
|
575
642
|
url = urlsplit(uri)
|
|
576
|
-
candidates = (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
643
|
+
candidates = (
|
|
644
|
+
f'{url.scheme}://{url.netloc}',
|
|
645
|
+
f'{url.scheme}://{url.netloc}/',
|
|
646
|
+
uri,
|
|
647
|
+
'*',
|
|
648
|
+
)
|
|
580
649
|
|
|
581
650
|
for u in candidates:
|
|
582
651
|
if u in request_headers:
|
|
@@ -590,8 +659,9 @@ def contains_anchor(response: Response, anchor: str) -> bool:
|
|
|
590
659
|
# Read file in chunks. If we find a matching anchor, we break
|
|
591
660
|
# the loop early in hopes not to have to download the whole thing.
|
|
592
661
|
for chunk in response.iter_content(chunk_size=4096, decode_unicode=True):
|
|
593
|
-
if isinstance(chunk, bytes):
|
|
594
|
-
|
|
662
|
+
if isinstance(chunk, bytes):
|
|
663
|
+
# requests failed to decode, manually try to decode it
|
|
664
|
+
chunk = chunk.decode()
|
|
595
665
|
|
|
596
666
|
parser.feed(chunk)
|
|
597
667
|
if parser.found:
|
|
@@ -616,12 +686,12 @@ class AnchorCheckParser(HTMLParser):
|
|
|
616
686
|
break
|
|
617
687
|
|
|
618
688
|
|
|
619
|
-
def _allowed_redirect(
|
|
620
|
-
|
|
689
|
+
def _allowed_redirect(
|
|
690
|
+
url: str, new_url: str, allowed_redirects: dict[re.Pattern[str], re.Pattern[str]]
|
|
691
|
+
) -> bool:
|
|
621
692
|
return any(
|
|
622
693
|
from_url.match(url) and to_url.match(new_url)
|
|
623
|
-
for from_url, to_url
|
|
624
|
-
in allowed_redirects.items()
|
|
694
|
+
for from_url, to_url in allowed_redirects.items()
|
|
625
695
|
)
|
|
626
696
|
|
|
627
697
|
|
|
@@ -647,15 +717,19 @@ def rewrite_github_anchor(app: Sphinx, uri: str) -> str | None:
|
|
|
647
717
|
|
|
648
718
|
def compile_linkcheck_allowed_redirects(app: Sphinx, config: Config) -> None:
|
|
649
719
|
"""Compile patterns in linkcheck_allowed_redirects to the regexp objects."""
|
|
650
|
-
|
|
720
|
+
linkcheck_allowed_redirects = app.config.linkcheck_allowed_redirects
|
|
721
|
+
for url, pattern in list(linkcheck_allowed_redirects.items()):
|
|
651
722
|
try:
|
|
652
|
-
|
|
723
|
+
linkcheck_allowed_redirects[re.compile(url)] = re.compile(pattern)
|
|
653
724
|
except re.error as exc:
|
|
654
|
-
logger.warning(
|
|
655
|
-
|
|
725
|
+
logger.warning(
|
|
726
|
+
__('Failed to compile regex in linkcheck_allowed_redirects: %r %s'),
|
|
727
|
+
exc.pattern,
|
|
728
|
+
exc.msg,
|
|
729
|
+
)
|
|
656
730
|
finally:
|
|
657
731
|
# Remove the original regexp-string
|
|
658
|
-
|
|
732
|
+
linkcheck_allowed_redirects.pop(url)
|
|
659
733
|
|
|
660
734
|
|
|
661
735
|
def setup(app: Sphinx) -> ExtensionMetadata:
|
sphinx/builders/manpage.py
CHANGED
|
@@ -20,6 +20,8 @@ from sphinx.util.osutil import ensuredir, make_filename_from_project
|
|
|
20
20
|
from sphinx.writers.manpage import ManualPageTranslator, ManualPageWriter
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
|
+
from collections.abc import Set
|
|
24
|
+
|
|
23
25
|
from sphinx.application import Sphinx
|
|
24
26
|
from sphinx.config import Config
|
|
25
27
|
from sphinx.util.typing import ExtensionMetadata
|
|
@@ -41,8 +43,12 @@ class ManualPageBuilder(Builder):
|
|
|
41
43
|
|
|
42
44
|
def init(self) -> None:
|
|
43
45
|
if not self.config.man_pages:
|
|
44
|
-
logger.warning(
|
|
45
|
-
|
|
46
|
+
logger.warning(
|
|
47
|
+
__(
|
|
48
|
+
'no "man_pages" config value found; no manual pages '
|
|
49
|
+
'will be written'
|
|
50
|
+
)
|
|
51
|
+
)
|
|
46
52
|
|
|
47
53
|
def get_outdated_docs(self) -> str | list[str]:
|
|
48
54
|
return 'all manpages' # for now
|
|
@@ -51,7 +57,7 @@ class ManualPageBuilder(Builder):
|
|
|
51
57
|
return ''
|
|
52
58
|
|
|
53
59
|
@progress_message(__('writing'))
|
|
54
|
-
def
|
|
60
|
+
def write_documents(self, _docnames: Set[str]) -> None:
|
|
55
61
|
docwriter = ManualPageWriter(self)
|
|
56
62
|
with warnings.catch_warnings():
|
|
57
63
|
warnings.filterwarnings('ignore', category=DeprecationWarning)
|
|
@@ -60,13 +66,16 @@ class ManualPageBuilder(Builder):
|
|
|
60
66
|
docsettings: Any = OptionParser(
|
|
61
67
|
defaults=self.env.settings,
|
|
62
68
|
components=(docwriter,),
|
|
63
|
-
read_config_files=True
|
|
69
|
+
read_config_files=True,
|
|
70
|
+
).get_default_values()
|
|
64
71
|
|
|
65
72
|
for info in self.config.man_pages:
|
|
66
73
|
docname, name, description, authors, section = info
|
|
67
74
|
if docname not in self.env.all_docs:
|
|
68
|
-
logger.warning(
|
|
69
|
-
|
|
75
|
+
logger.warning(
|
|
76
|
+
__('"man_pages" config value references unknown ' 'document %s'),
|
|
77
|
+
docname,
|
|
78
|
+
)
|
|
70
79
|
continue
|
|
71
80
|
if isinstance(authors, str):
|
|
72
81
|
if authors:
|
|
@@ -86,15 +95,16 @@ class ManualPageBuilder(Builder):
|
|
|
86
95
|
else:
|
|
87
96
|
targetname = f'{name}.{section}'
|
|
88
97
|
|
|
89
|
-
logger.info(darkgreen(targetname) + ' { '
|
|
98
|
+
logger.info(darkgreen(targetname) + ' { ')
|
|
90
99
|
destination = FileOutput(
|
|
91
|
-
destination_path=path.join(self.outdir, targetname),
|
|
92
|
-
|
|
100
|
+
destination_path=path.join(self.outdir, targetname), encoding='utf-8'
|
|
101
|
+
)
|
|
93
102
|
|
|
94
103
|
tree = self.env.get_doctree(docname)
|
|
95
104
|
docnames: set[str] = set()
|
|
96
|
-
largetree = inline_all_toctrees(
|
|
97
|
-
|
|
105
|
+
largetree = inline_all_toctrees(
|
|
106
|
+
self, docnames, docname, tree, darkgreen, [docname]
|
|
107
|
+
)
|
|
98
108
|
largetree.settings = docsettings
|
|
99
109
|
logger.info('} ', nonl=True)
|
|
100
110
|
self.env.resolve_references(largetree, docname, self)
|
|
@@ -111,8 +121,15 @@ class ManualPageBuilder(Builder):
|
|
|
111
121
|
def default_man_pages(config: Config) -> list[tuple[str, str, str, list[str], int]]:
|
|
112
122
|
"""Better default man_pages settings."""
|
|
113
123
|
filename = make_filename_from_project(config.project)
|
|
114
|
-
return [
|
|
115
|
-
|
|
124
|
+
return [
|
|
125
|
+
(
|
|
126
|
+
config.root_doc,
|
|
127
|
+
filename,
|
|
128
|
+
f'{config.project} {config.release}',
|
|
129
|
+
[config.author],
|
|
130
|
+
1,
|
|
131
|
+
)
|
|
132
|
+
]
|
|
116
133
|
|
|
117
134
|
|
|
118
135
|
def setup(app: Sphinx) -> ExtensionMetadata:
|
sphinx/builders/singlehtml.py
CHANGED
|
@@ -16,6 +16,8 @@ from sphinx.util.display import progress_message
|
|
|
16
16
|
from sphinx.util.nodes import inline_all_toctrees
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
|
+
from collections.abc import Set
|
|
20
|
+
|
|
19
21
|
from docutils.nodes import Node
|
|
20
22
|
|
|
21
23
|
from sphinx.application import Sphinx
|
|
@@ -52,7 +54,6 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
|
|
52
54
|
|
|
53
55
|
def fix_refuris(self, tree: Node) -> None:
|
|
54
56
|
# fix refuris with double anchor
|
|
55
|
-
fname = self.config.root_doc + self.out_suffix
|
|
56
57
|
for refnode in tree.findall(nodes.reference):
|
|
57
58
|
if 'refuri' not in refnode:
|
|
58
59
|
continue
|
|
@@ -62,9 +63,12 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
|
|
62
63
|
continue
|
|
63
64
|
hashindex = refuri.find('#', hashindex + 1)
|
|
64
65
|
if hashindex >= 0:
|
|
65
|
-
|
|
66
|
+
# all references are on the same page...
|
|
67
|
+
refnode['refuri'] = refuri[hashindex:]
|
|
66
68
|
|
|
67
|
-
def _get_local_toctree(
|
|
69
|
+
def _get_local_toctree(
|
|
70
|
+
self, docname: str, collapse: bool = True, **kwargs: Any
|
|
71
|
+
) -> str:
|
|
68
72
|
if isinstance(includehidden := kwargs.get('includehidden'), str):
|
|
69
73
|
if includehidden.lower() == 'false':
|
|
70
74
|
kwargs['includehidden'] = False
|
|
@@ -72,7 +76,9 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
|
|
72
76
|
kwargs['includehidden'] = True
|
|
73
77
|
if kwargs.get('maxdepth') == '':
|
|
74
78
|
kwargs.pop('maxdepth')
|
|
75
|
-
toctree = global_toctree_for_doc(
|
|
79
|
+
toctree = global_toctree_for_doc(
|
|
80
|
+
self.env, docname, self, collapse=collapse, **kwargs
|
|
81
|
+
)
|
|
76
82
|
if toctree is not None:
|
|
77
83
|
self.fix_refuris(toctree)
|
|
78
84
|
return self.render_partial(toctree)['fragment']
|
|
@@ -80,6 +86,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
|
|
80
86
|
def assemble_doctree(self) -> nodes.document:
|
|
81
87
|
master = self.config.root_doc
|
|
82
88
|
tree = self.env.get_doctree(master)
|
|
89
|
+
logger.info(darkgreen(master))
|
|
83
90
|
tree = inline_all_toctrees(self, set(), master, tree, darkgreen, [master])
|
|
84
91
|
tree['docname'] = master
|
|
85
92
|
self.env.resolve_references(tree, master, self)
|
|
@@ -99,12 +106,14 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
|
|
99
106
|
new_secnumbers: dict[str, tuple[int, ...]] = {}
|
|
100
107
|
for docname, secnums in self.env.toc_secnumbers.items():
|
|
101
108
|
for id, secnum in secnums.items():
|
|
102
|
-
alias = f
|
|
109
|
+
alias = f'{docname}/{id}'
|
|
103
110
|
new_secnumbers[alias] = secnum
|
|
104
111
|
|
|
105
112
|
return {self.config.root_doc: new_secnumbers}
|
|
106
113
|
|
|
107
|
-
def assemble_toc_fignumbers(
|
|
114
|
+
def assemble_toc_fignumbers(
|
|
115
|
+
self,
|
|
116
|
+
) -> dict[str, dict[str, dict[str, tuple[int, ...]]]]:
|
|
108
117
|
# Assemble toc_fignumbers to resolve figure numbers on SingleHTML.
|
|
109
118
|
# Merge all fignumbers to single fignumber.
|
|
110
119
|
#
|
|
@@ -118,7 +127,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
|
|
118
127
|
# {'foo': {'figure': {'id2': (2,), 'id1': (1,)}}, 'bar': {'figure': {'id1': (3,)}}}
|
|
119
128
|
for docname, fignumlist in self.env.toc_fignumbers.items():
|
|
120
129
|
for figtype, fignums in fignumlist.items():
|
|
121
|
-
alias = f
|
|
130
|
+
alias = f'{docname}/{figtype}'
|
|
122
131
|
new_fignumbers.setdefault(alias, {})
|
|
123
132
|
for id, fignum in fignums.items():
|
|
124
133
|
new_fignumbers[alias][id] = fignum
|
|
@@ -127,7 +136,9 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
|
|
127
136
|
|
|
128
137
|
def get_doc_context(self, docname: str, body: str, metatags: str) -> dict[str, Any]:
|
|
129
138
|
# no relation links...
|
|
130
|
-
toctree = global_toctree_for_doc(
|
|
139
|
+
toctree = global_toctree_for_doc(
|
|
140
|
+
self.env, self.config.root_doc, self, collapse=False
|
|
141
|
+
)
|
|
131
142
|
# if there is no toctree, toc is None
|
|
132
143
|
if toctree:
|
|
133
144
|
self.fix_refuris(toctree)
|
|
@@ -151,13 +162,10 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
|
|
151
162
|
'display_toc': display_toc,
|
|
152
163
|
}
|
|
153
164
|
|
|
154
|
-
def
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
with progress_message(__('preparing documents')):
|
|
158
|
-
self.prepare_writing(docnames) # type: ignore[arg-type]
|
|
165
|
+
def write_documents(self, _docnames: Set[str]) -> None:
|
|
166
|
+
self.prepare_writing(self.env.all_docs.keys())
|
|
159
167
|
|
|
160
|
-
with progress_message(__('assembling single document')):
|
|
168
|
+
with progress_message(__('assembling single document'), nonl=False):
|
|
161
169
|
doctree = self.assemble_doctree()
|
|
162
170
|
self.env.toc_secnumbers = self.assemble_toc_secnumbers()
|
|
163
171
|
self.env.toc_fignumbers = self.assemble_toc_fignumbers()
|