Sphinx 8.1.2__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.
- sphinx/__init__.py +8 -4
- sphinx/__main__.py +2 -0
- sphinx/_cli/__init__.py +2 -5
- sphinx/_cli/util/colour.py +34 -11
- sphinx/_cli/util/errors.py +128 -61
- sphinx/addnodes.py +51 -35
- sphinx/application.py +362 -230
- sphinx/builders/__init__.py +87 -64
- sphinx/builders/_epub_base.py +65 -56
- sphinx/builders/changes.py +17 -23
- sphinx/builders/dirhtml.py +8 -13
- sphinx/builders/epub3.py +70 -38
- sphinx/builders/gettext.py +93 -73
- sphinx/builders/html/__init__.py +240 -186
- sphinx/builders/html/_assets.py +9 -2
- sphinx/builders/html/_build_info.py +3 -0
- sphinx/builders/latex/__init__.py +64 -54
- sphinx/builders/latex/constants.py +14 -11
- sphinx/builders/latex/nodes.py +2 -0
- sphinx/builders/latex/theming.py +8 -9
- sphinx/builders/latex/transforms.py +7 -5
- sphinx/builders/linkcheck.py +193 -149
- sphinx/builders/manpage.py +17 -17
- sphinx/builders/singlehtml.py +28 -16
- sphinx/builders/texinfo.py +28 -21
- sphinx/builders/text.py +10 -15
- sphinx/builders/xml.py +10 -19
- sphinx/cmd/build.py +49 -119
- sphinx/cmd/make_mode.py +35 -31
- sphinx/cmd/quickstart.py +78 -62
- sphinx/config.py +265 -163
- sphinx/directives/__init__.py +51 -54
- sphinx/directives/admonitions.py +107 -0
- sphinx/directives/code.py +24 -19
- sphinx/directives/other.py +21 -42
- sphinx/directives/patches.py +28 -16
- sphinx/domains/__init__.py +54 -31
- sphinx/domains/_domains_container.py +22 -17
- sphinx/domains/_index.py +5 -8
- sphinx/domains/c/__init__.py +366 -245
- sphinx/domains/c/_ast.py +378 -256
- sphinx/domains/c/_ids.py +89 -31
- sphinx/domains/c/_parser.py +283 -214
- sphinx/domains/c/_symbol.py +269 -198
- sphinx/domains/changeset.py +39 -24
- sphinx/domains/citation.py +54 -24
- sphinx/domains/cpp/__init__.py +517 -362
- sphinx/domains/cpp/_ast.py +999 -682
- sphinx/domains/cpp/_ids.py +133 -65
- sphinx/domains/cpp/_parser.py +746 -588
- sphinx/domains/cpp/_symbol.py +692 -489
- sphinx/domains/index.py +10 -8
- sphinx/domains/javascript.py +152 -74
- sphinx/domains/math.py +50 -40
- sphinx/domains/python/__init__.py +402 -211
- sphinx/domains/python/_annotations.py +134 -61
- sphinx/domains/python/_object.py +155 -68
- sphinx/domains/rst.py +94 -49
- sphinx/domains/std/__init__.py +510 -249
- sphinx/environment/__init__.py +345 -61
- sphinx/environment/adapters/asset.py +7 -1
- sphinx/environment/adapters/indexentries.py +15 -20
- sphinx/environment/adapters/toctree.py +19 -9
- sphinx/environment/collectors/__init__.py +3 -1
- sphinx/environment/collectors/asset.py +18 -15
- sphinx/environment/collectors/dependencies.py +8 -10
- sphinx/environment/collectors/metadata.py +6 -4
- sphinx/environment/collectors/title.py +3 -1
- sphinx/environment/collectors/toctree.py +4 -4
- sphinx/errors.py +1 -3
- sphinx/events.py +4 -4
- sphinx/ext/apidoc/__init__.py +66 -0
- sphinx/ext/apidoc/__main__.py +9 -0
- sphinx/ext/apidoc/_cli.py +356 -0
- sphinx/ext/apidoc/_extension.py +262 -0
- sphinx/ext/apidoc/_generate.py +356 -0
- sphinx/ext/apidoc/_shared.py +99 -0
- sphinx/ext/autodoc/__init__.py +837 -483
- sphinx/ext/autodoc/directive.py +57 -21
- sphinx/ext/autodoc/importer.py +184 -67
- sphinx/ext/autodoc/mock.py +25 -10
- sphinx/ext/autodoc/preserve_defaults.py +17 -9
- sphinx/ext/autodoc/type_comment.py +56 -29
- sphinx/ext/autodoc/typehints.py +49 -26
- sphinx/ext/autosectionlabel.py +28 -11
- sphinx/ext/autosummary/__init__.py +281 -142
- sphinx/ext/autosummary/generate.py +121 -51
- sphinx/ext/coverage.py +152 -91
- sphinx/ext/doctest.py +169 -101
- sphinx/ext/duration.py +12 -6
- sphinx/ext/extlinks.py +33 -21
- sphinx/ext/githubpages.py +8 -8
- sphinx/ext/graphviz.py +175 -109
- sphinx/ext/ifconfig.py +11 -6
- sphinx/ext/imgconverter.py +48 -25
- sphinx/ext/imgmath.py +127 -97
- sphinx/ext/inheritance_diagram.py +177 -103
- sphinx/ext/intersphinx/__init__.py +22 -13
- sphinx/ext/intersphinx/__main__.py +3 -1
- sphinx/ext/intersphinx/_cli.py +18 -14
- sphinx/ext/intersphinx/_load.py +91 -82
- sphinx/ext/intersphinx/_resolve.py +108 -74
- sphinx/ext/intersphinx/_shared.py +2 -2
- sphinx/ext/linkcode.py +28 -12
- sphinx/ext/mathjax.py +60 -29
- sphinx/ext/napoleon/__init__.py +19 -7
- sphinx/ext/napoleon/docstring.py +229 -231
- sphinx/ext/todo.py +44 -49
- sphinx/ext/viewcode.py +105 -57
- sphinx/extension.py +3 -1
- sphinx/highlighting.py +13 -7
- sphinx/io.py +9 -13
- sphinx/jinja2glue.py +29 -26
- sphinx/locale/__init__.py +8 -9
- sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ar/LC_MESSAGES/sphinx.po +2155 -2050
- sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bg/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bn/LC_MESSAGES/sphinx.po +2175 -2070
- sphinx/locale/ca/LC_MESSAGES/sphinx.js +3 -3
- sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca/LC_MESSAGES/sphinx.po +2690 -2585
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.js +63 -0
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.po +4216 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.po +2096 -1991
- sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cs/LC_MESSAGES/sphinx.po +2248 -2143
- sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cy/LC_MESSAGES/sphinx.po +2201 -2096
- sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/da/LC_MESSAGES/sphinx.po +2282 -2177
- sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de/LC_MESSAGES/sphinx.po +2261 -2156
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/el/LC_MESSAGES/sphinx.po +2604 -2499
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2631 -2526
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eo/LC_MESSAGES/sphinx.po +2078 -1973
- sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es/LC_MESSAGES/sphinx.po +2633 -2528
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/et/LC_MESSAGES/sphinx.po +2449 -2344
- sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eu/LC_MESSAGES/sphinx.po +2241 -2136
- sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fa/LC_MESSAGES/sphinx.po +504 -500
- sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fi/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr/LC_MESSAGES/sphinx.po +513 -509
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.po +2644 -2539
- sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi/LC_MESSAGES/sphinx.po +504 -500
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hr/LC_MESSAGES/sphinx.po +501 -497
- sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hu/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/id/LC_MESSAGES/sphinx.po +2609 -2504
- sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/is/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/it/LC_MESSAGES/sphinx.po +2265 -2160
- sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ja/LC_MESSAGES/sphinx.po +2621 -2516
- sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ka/LC_MESSAGES/sphinx.po +2567 -2462
- sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ko/LC_MESSAGES/sphinx.po +2631 -2526
- sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lt/LC_MESSAGES/sphinx.po +2214 -2109
- sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lv/LC_MESSAGES/sphinx.po +2218 -2113
- sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/mk/LC_MESSAGES/sphinx.po +2088 -1983
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2247 -2142
- sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ne/LC_MESSAGES/sphinx.po +2227 -2122
- sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nl/LC_MESSAGES/sphinx.po +2316 -2211
- sphinx/locale/pl/LC_MESSAGES/sphinx.js +2 -2
- sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pl/LC_MESSAGES/sphinx.po +2442 -2336
- sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2657 -2552
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2243 -2138
- sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ro/LC_MESSAGES/sphinx.po +2244 -2139
- sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
- sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ru/LC_MESSAGES/sphinx.po +2660 -2555
- sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/si/LC_MESSAGES/sphinx.po +2134 -2029
- sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sk/LC_MESSAGES/sphinx.po +2614 -2509
- sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sl/LC_MESSAGES/sphinx.po +2167 -2062
- sphinx/locale/sphinx.pot +2069 -1964
- sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sq/LC_MESSAGES/sphinx.po +2661 -2556
- sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr/LC_MESSAGES/sphinx.po +2213 -2108
- sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.po +2229 -2124
- sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/te/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/tr/LC_MESSAGES/sphinx.po +2608 -2503
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2167 -2062
- sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ur/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/vi/LC_MESSAGES/sphinx.po +2204 -2099
- sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/yue/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2659 -2554
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/parsers.py +8 -7
- sphinx/project.py +2 -2
- sphinx/pycode/__init__.py +31 -21
- sphinx/pycode/ast.py +6 -3
- sphinx/pycode/parser.py +14 -8
- sphinx/pygments_styles.py +4 -5
- sphinx/registry.py +192 -92
- sphinx/roles.py +58 -7
- sphinx/search/__init__.py +75 -54
- sphinx/search/en.py +11 -13
- sphinx/search/fi.py +1 -1
- sphinx/search/ja.py +8 -6
- sphinx/search/nl.py +1 -1
- sphinx/search/zh.py +19 -21
- sphinx/testing/fixtures.py +26 -29
- sphinx/testing/path.py +26 -62
- sphinx/testing/restructuredtext.py +14 -8
- sphinx/testing/util.py +21 -19
- sphinx/texinputs/make.bat.jinja +50 -50
- sphinx/texinputs/sphinx.sty +4 -3
- sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
- sphinx/texinputs/sphinxlatexobjects.sty +29 -10
- sphinx/themes/basic/static/searchtools.js +8 -5
- sphinx/theming.py +49 -61
- sphinx/transforms/__init__.py +17 -38
- sphinx/transforms/compact_bullet_list.py +5 -3
- sphinx/transforms/i18n.py +8 -21
- sphinx/transforms/post_transforms/__init__.py +142 -93
- sphinx/transforms/post_transforms/code.py +5 -5
- sphinx/transforms/post_transforms/images.py +28 -24
- sphinx/transforms/references.py +3 -1
- sphinx/util/__init__.py +109 -60
- sphinx/util/_files.py +39 -23
- sphinx/util/_importer.py +4 -1
- sphinx/util/_inventory_file_reader.py +76 -0
- sphinx/util/_io.py +2 -2
- sphinx/util/_lines.py +6 -3
- sphinx/util/_pathlib.py +40 -2
- sphinx/util/build_phase.py +2 -0
- sphinx/util/cfamily.py +19 -14
- sphinx/util/console.py +44 -179
- sphinx/util/display.py +9 -10
- sphinx/util/docfields.py +140 -122
- sphinx/util/docstrings.py +1 -1
- sphinx/util/docutils.py +118 -77
- sphinx/util/fileutil.py +25 -26
- sphinx/util/http_date.py +2 -0
- sphinx/util/i18n.py +77 -64
- sphinx/util/images.py +8 -6
- sphinx/util/inspect.py +147 -38
- sphinx/util/inventory.py +215 -116
- sphinx/util/logging.py +33 -33
- sphinx/util/matching.py +12 -4
- sphinx/util/nodes.py +18 -13
- sphinx/util/osutil.py +38 -39
- sphinx/util/parallel.py +22 -13
- sphinx/util/parsing.py +2 -1
- sphinx/util/png.py +6 -2
- sphinx/util/requests.py +33 -2
- sphinx/util/rst.py +3 -2
- sphinx/util/tags.py +1 -1
- sphinx/util/template.py +18 -10
- sphinx/util/texescape.py +8 -6
- sphinx/util/typing.py +148 -122
- sphinx/versioning.py +3 -3
- sphinx/writers/html.py +3 -1
- sphinx/writers/html5.py +63 -52
- sphinx/writers/latex.py +83 -67
- sphinx/writers/manpage.py +19 -38
- sphinx/writers/texinfo.py +47 -47
- sphinx/writers/text.py +50 -32
- sphinx/writers/xml.py +11 -8
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/LICENSE.rst +1 -1
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/METADATA +25 -15
- sphinx-8.2.0.dist-info/RECORD +606 -0
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/WHEEL +1 -1
- sphinx/builders/html/transforms.py +0 -90
- sphinx/ext/apidoc.py +0 -721
- sphinx/util/exceptions.py +0 -74
- sphinx-8.1.2.dist-info/RECORD +0 -598
- {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/entry_points.txt +0 -0
sphinx/builders/html/__init__.py
CHANGED
|
@@ -5,14 +5,15 @@ from __future__ import annotations
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import html
|
|
7
7
|
import os
|
|
8
|
+
import os.path
|
|
8
9
|
import posixpath
|
|
9
10
|
import re
|
|
10
11
|
import shutil
|
|
11
12
|
import sys
|
|
12
13
|
import warnings
|
|
13
|
-
from os import path
|
|
14
14
|
from pathlib import Path
|
|
15
|
-
from
|
|
15
|
+
from types import NoneType
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
16
17
|
from urllib.parse import quote
|
|
17
18
|
|
|
18
19
|
import docutils.readers.doctree
|
|
@@ -20,10 +21,10 @@ from docutils import nodes
|
|
|
20
21
|
from docutils.core import Publisher
|
|
21
22
|
from docutils.frontend import OptionParser
|
|
22
23
|
from docutils.io import DocTreeInput, StringOutput
|
|
23
|
-
from docutils.utils import relative_path
|
|
24
24
|
|
|
25
25
|
from sphinx import __display_version__, package_dir
|
|
26
26
|
from sphinx import version_info as sphinx_version
|
|
27
|
+
from sphinx._cli.util.colour import bold
|
|
27
28
|
from sphinx.builders import Builder
|
|
28
29
|
from sphinx.builders.html._assets import (
|
|
29
30
|
_CascadingStyleSheet,
|
|
@@ -31,7 +32,7 @@ from sphinx.builders.html._assets import (
|
|
|
31
32
|
_JavaScript,
|
|
32
33
|
)
|
|
33
34
|
from sphinx.builders.html._build_info import BuildInfo
|
|
34
|
-
from sphinx.config import ENUM
|
|
35
|
+
from sphinx.config import ENUM
|
|
35
36
|
from sphinx.deprecation import _deprecation_warning
|
|
36
37
|
from sphinx.domains import Index, IndexEntry
|
|
37
38
|
from sphinx.environment.adapters.asset import ImageAdapter
|
|
@@ -43,9 +44,9 @@ from sphinx.locale import _, __
|
|
|
43
44
|
from sphinx.search import js_index
|
|
44
45
|
from sphinx.theming import HTMLThemeFactory
|
|
45
46
|
from sphinx.util import logging
|
|
47
|
+
from sphinx.util._pathlib import _StrPath
|
|
46
48
|
from sphinx.util._timestamps import _format_rfc3339_microseconds
|
|
47
49
|
from sphinx.util._uri import is_url
|
|
48
|
-
from sphinx.util.console import bold
|
|
49
50
|
from sphinx.util.display import progress_message, status_iterator
|
|
50
51
|
from sphinx.util.docutils import new_document
|
|
51
52
|
from sphinx.util.fileutil import copy_asset
|
|
@@ -55,9 +56,9 @@ from sphinx.util.matching import DOTFILES, Matcher, patmatch
|
|
|
55
56
|
from sphinx.util.osutil import (
|
|
56
57
|
SEP,
|
|
57
58
|
_last_modified_time,
|
|
59
|
+
_relative_path,
|
|
58
60
|
copyfile,
|
|
59
61
|
ensuredir,
|
|
60
|
-
os_path,
|
|
61
62
|
relative_uri,
|
|
62
63
|
)
|
|
63
64
|
from sphinx.writers.html import HTMLWriter
|
|
@@ -65,12 +66,13 @@ from sphinx.writers.html5 import HTML5Translator
|
|
|
65
66
|
|
|
66
67
|
if TYPE_CHECKING:
|
|
67
68
|
from collections.abc import Iterator, Set
|
|
68
|
-
from typing import TypeAlias
|
|
69
|
+
from typing import Any, TypeAlias
|
|
69
70
|
|
|
70
71
|
from docutils.nodes import Node
|
|
71
72
|
from docutils.readers import Reader
|
|
72
73
|
|
|
73
74
|
from sphinx.application import Sphinx
|
|
75
|
+
from sphinx.config import Config
|
|
74
76
|
from sphinx.environment import BuildEnvironment
|
|
75
77
|
from sphinx.util.typing import ExtensionMetadata
|
|
76
78
|
|
|
@@ -104,9 +106,7 @@ def convert_locale_to_language_tag(locale: str | None) -> str | None:
|
|
|
104
106
|
|
|
105
107
|
|
|
106
108
|
class StandaloneHTMLBuilder(Builder):
|
|
107
|
-
"""
|
|
108
|
-
Builds standalone HTML docs.
|
|
109
|
-
"""
|
|
109
|
+
"""Builds standalone HTML docs."""
|
|
110
110
|
|
|
111
111
|
name = 'html'
|
|
112
112
|
format = 'html'
|
|
@@ -138,6 +138,12 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
138
138
|
def __init__(self, app: Sphinx, env: BuildEnvironment) -> None:
|
|
139
139
|
super().__init__(app, env)
|
|
140
140
|
|
|
141
|
+
# Static and asset directories
|
|
142
|
+
self._static_dir = Path(self.outdir / '_static')
|
|
143
|
+
self._sources_dir = Path(self.outdir / '_sources')
|
|
144
|
+
self._downloads_dir = Path(self.outdir / '_downloads')
|
|
145
|
+
self._images_dir = Path(self.outdir / '_images')
|
|
146
|
+
|
|
141
147
|
# CSS files
|
|
142
148
|
self._css_files: list[_CascadingStyleSheet] = []
|
|
143
149
|
|
|
@@ -187,23 +193,25 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
187
193
|
def create_build_info(self) -> BuildInfo:
|
|
188
194
|
return BuildInfo(self.config, self.tags, frozenset({'html'}))
|
|
189
195
|
|
|
190
|
-
def _get_translations_js(self) ->
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
path.join(
|
|
196
|
-
package_dir, 'locale', self.config.language, 'LC_MESSAGES', 'sphinx.js'
|
|
197
|
-
),
|
|
198
|
-
path.join(
|
|
199
|
-
sys.prefix, 'share/sphinx/locale', self.config.language, 'sphinx.js'
|
|
200
|
-
),
|
|
201
|
-
]
|
|
196
|
+
def _get_translations_js(self) -> Path | None:
|
|
197
|
+
for dir_ in self.config.locale_dirs:
|
|
198
|
+
js_file = Path(dir_, self.config.language, 'LC_MESSAGES', 'sphinx.js')
|
|
199
|
+
if js_file.is_file():
|
|
200
|
+
return js_file
|
|
202
201
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
202
|
+
js_file = package_dir.joinpath(
|
|
203
|
+
'locale', self.config.language, 'LC_MESSAGES', 'sphinx.js'
|
|
204
|
+
)
|
|
205
|
+
if js_file.is_file():
|
|
206
|
+
return js_file
|
|
207
|
+
|
|
208
|
+
js_file = Path(
|
|
209
|
+
sys.prefix, 'share', 'sphinx', 'locale', self.config.language, 'sphinx.js'
|
|
210
|
+
)
|
|
211
|
+
if js_file.is_file():
|
|
212
|
+
return js_file
|
|
213
|
+
|
|
214
|
+
return None
|
|
207
215
|
|
|
208
216
|
def _get_style_filenames(self) -> Iterator[str]:
|
|
209
217
|
if isinstance(self.config.html_style, str):
|
|
@@ -268,7 +276,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
268
276
|
for filename in self._get_style_filenames():
|
|
269
277
|
self.add_css_file(filename, priority=200)
|
|
270
278
|
|
|
271
|
-
for filename, attrs in self.
|
|
279
|
+
for filename, attrs in self.env._registry.css_files:
|
|
272
280
|
self.add_css_file(filename, **attrs)
|
|
273
281
|
|
|
274
282
|
for filename, attrs in self.get_builder_config('css_files', 'html'):
|
|
@@ -295,7 +303,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
295
303
|
self.add_js_file('doctools.js', priority=200)
|
|
296
304
|
self.add_js_file('sphinx_highlight.js', priority=200)
|
|
297
305
|
|
|
298
|
-
for filename, attrs in self.
|
|
306
|
+
for filename, attrs in self.env._registry.js_files:
|
|
299
307
|
self.add_js_file(filename or '', **attrs)
|
|
300
308
|
|
|
301
309
|
for filename, attrs in self.get_builder_config('js_files', 'html'):
|
|
@@ -320,7 +328,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
320
328
|
return name
|
|
321
329
|
else:
|
|
322
330
|
# not given: choose a math_renderer from registered ones as possible
|
|
323
|
-
renderers = list(self.
|
|
331
|
+
renderers = list(self.env._registry.html_inline_math_renderers)
|
|
324
332
|
if len(renderers) == 1:
|
|
325
333
|
# only default math_renderer (mathjax) is registered
|
|
326
334
|
return renderers[0]
|
|
@@ -333,9 +341,9 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
333
341
|
return None
|
|
334
342
|
|
|
335
343
|
def get_outdated_docs(self) -> Iterator[str]:
|
|
336
|
-
|
|
344
|
+
build_info_path = self.outdir / '.buildinfo'
|
|
337
345
|
try:
|
|
338
|
-
build_info = BuildInfo.load(
|
|
346
|
+
build_info = BuildInfo.load(build_info_path)
|
|
339
347
|
except ValueError as exc:
|
|
340
348
|
logger.warning(__('Failed to read build info file: %r'), exc)
|
|
341
349
|
except OSError:
|
|
@@ -344,10 +352,10 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
344
352
|
else:
|
|
345
353
|
if self.build_info != build_info:
|
|
346
354
|
# log the mismatch and backup the old build info
|
|
347
|
-
build_info_backup =
|
|
355
|
+
build_info_backup = build_info_path.with_name('.buildinfo.bak')
|
|
348
356
|
try:
|
|
349
|
-
shutil.move(
|
|
350
|
-
self.build_info.dump(
|
|
357
|
+
shutil.move(build_info_path, build_info_backup)
|
|
358
|
+
self.build_info.dump(build_info_path)
|
|
351
359
|
except OSError:
|
|
352
360
|
pass # ignore errors
|
|
353
361
|
else:
|
|
@@ -355,7 +363,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
355
363
|
msg = __(
|
|
356
364
|
'build_info mismatch, copying .buildinfo to .buildinfo.bak'
|
|
357
365
|
)
|
|
358
|
-
logger.info(bold(__('building [html]: ')) + msg)
|
|
366
|
+
logger.info(bold(__('building [html]: ')) + msg) # NoQA: G003
|
|
359
367
|
|
|
360
368
|
yield from self.env.found_docs
|
|
361
369
|
return
|
|
@@ -363,14 +371,14 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
363
371
|
if self.templates:
|
|
364
372
|
template_mtime = int(self.templates.newest_template_mtime() * 10**6)
|
|
365
373
|
try:
|
|
366
|
-
old_mtime = _last_modified_time(
|
|
374
|
+
old_mtime = _last_modified_time(build_info_path)
|
|
367
375
|
except Exception:
|
|
368
376
|
pass
|
|
369
377
|
else:
|
|
370
378
|
# Let users know they have a newer template
|
|
371
379
|
if template_mtime > old_mtime:
|
|
372
380
|
logger.info(
|
|
373
|
-
bold('building [html]: ')
|
|
381
|
+
bold('building [html]: ') # NoQA: G003
|
|
374
382
|
+ __(
|
|
375
383
|
'template %s has been changed since the previous build, '
|
|
376
384
|
'all docs will be rebuilt'
|
|
@@ -384,19 +392,19 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
384
392
|
logger.debug('[build target] did not in env: %r', docname)
|
|
385
393
|
yield docname
|
|
386
394
|
continue
|
|
387
|
-
|
|
395
|
+
target_name = self.get_output_path(docname)
|
|
388
396
|
try:
|
|
389
|
-
|
|
390
|
-
except
|
|
391
|
-
|
|
397
|
+
target_mtime = _last_modified_time(target_name)
|
|
398
|
+
except OSError:
|
|
399
|
+
target_mtime = 0
|
|
392
400
|
try:
|
|
393
401
|
doc_mtime = _last_modified_time(self.env.doc2path(docname))
|
|
394
402
|
srcmtime = max(doc_mtime, template_mtime)
|
|
395
|
-
if srcmtime >
|
|
403
|
+
if srcmtime > target_mtime:
|
|
396
404
|
logger.debug(
|
|
397
|
-
'[build target]
|
|
398
|
-
|
|
399
|
-
_format_rfc3339_microseconds(
|
|
405
|
+
'[build target] target_name %r(%s), template(%s), docname %r(%s)',
|
|
406
|
+
target_name,
|
|
407
|
+
_format_rfc3339_microseconds(target_mtime),
|
|
400
408
|
_format_rfc3339_microseconds(template_mtime),
|
|
401
409
|
docname,
|
|
402
410
|
_format_rfc3339_microseconds(doc_mtime),
|
|
@@ -488,24 +496,29 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
488
496
|
favicon = self.config.html_favicon or ''
|
|
489
497
|
|
|
490
498
|
if not is_url(logo):
|
|
491
|
-
logo = path.basename(logo)
|
|
499
|
+
logo = os.path.basename(logo)
|
|
492
500
|
if not is_url(favicon):
|
|
493
|
-
favicon = path.basename(favicon)
|
|
501
|
+
favicon = os.path.basename(favicon)
|
|
494
502
|
|
|
495
503
|
self.relations = self.env.collect_relations()
|
|
496
504
|
|
|
497
505
|
rellinks: list[tuple[str, str, str, str]] = []
|
|
498
506
|
if self.use_index:
|
|
499
507
|
rellinks.append(('genindex', _('General Index'), 'I', _('index')))
|
|
500
|
-
for
|
|
508
|
+
for index_name, index_cls, _content, _collapse in self.domain_indices:
|
|
501
509
|
# if it has a short name
|
|
502
|
-
if
|
|
503
|
-
rellinks.append((
|
|
510
|
+
if index_cls.shortname:
|
|
511
|
+
rellinks.append((
|
|
512
|
+
index_name,
|
|
513
|
+
index_cls.localname,
|
|
514
|
+
'',
|
|
515
|
+
index_cls.shortname,
|
|
516
|
+
))
|
|
504
517
|
|
|
505
518
|
# add assets registered after ``Builder.init()``.
|
|
506
|
-
for css_filename, attrs in self.
|
|
519
|
+
for css_filename, attrs in self.env._registry.css_files:
|
|
507
520
|
self.add_css_file(css_filename, **attrs)
|
|
508
|
-
for js_filename, attrs in self.
|
|
521
|
+
for js_filename, attrs in self.env._registry.js_files:
|
|
509
522
|
self.add_js_file(js_filename or '', **attrs)
|
|
510
523
|
|
|
511
524
|
# back up _css_files and _js_files to allow adding CSS/JS files to a specific page.
|
|
@@ -555,6 +568,10 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
555
568
|
}
|
|
556
569
|
self.globalcontext |= self.config.html_context
|
|
557
570
|
|
|
571
|
+
if self.copysource:
|
|
572
|
+
# Create _sources
|
|
573
|
+
ensuredir(self._sources_dir)
|
|
574
|
+
|
|
558
575
|
def get_doc_context(self, docname: str, body: str, metatags: str) -> dict[str, Any]:
|
|
559
576
|
"""Collect items for the template context of a page."""
|
|
560
577
|
# find out relations
|
|
@@ -656,6 +673,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
656
673
|
metatags = self.docwriter.clean_meta
|
|
657
674
|
|
|
658
675
|
ctx = self.get_doc_context(docname, body, metatags)
|
|
676
|
+
ctx['has_maths_elements'] = self.docwriter._has_maths_elements
|
|
659
677
|
self.handle_page(docname, ctx, event_arg=doctree)
|
|
660
678
|
|
|
661
679
|
def write_doc_serialized(self, docname: str, doctree: nodes.document) -> None:
|
|
@@ -694,7 +712,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
694
712
|
def gen_additional_pages(self) -> None:
|
|
695
713
|
# additional pages from conf.py
|
|
696
714
|
for pagename, template in self.config.html_additional_pages.items():
|
|
697
|
-
logger.info(
|
|
715
|
+
logger.info('%s ', pagename, nonl=True)
|
|
698
716
|
self.handle_page(pagename, {}, template)
|
|
699
717
|
|
|
700
718
|
# the search page
|
|
@@ -705,8 +723,12 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
705
723
|
# the opensearch xml file
|
|
706
724
|
if self.config.html_use_opensearch and self.search:
|
|
707
725
|
logger.info('opensearch ', nonl=True)
|
|
708
|
-
|
|
709
|
-
|
|
726
|
+
self.handle_page(
|
|
727
|
+
'opensearch',
|
|
728
|
+
{},
|
|
729
|
+
'opensearch.xml',
|
|
730
|
+
outfilename=self._static_dir / 'opensearch.xml',
|
|
731
|
+
)
|
|
710
732
|
|
|
711
733
|
def write_genindex(self) -> None:
|
|
712
734
|
# the total count of lines for each index letter, used to distribute
|
|
@@ -739,19 +761,19 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
739
761
|
self.handle_page('genindex', genindexcontext, 'genindex.html')
|
|
740
762
|
|
|
741
763
|
def write_domain_indices(self) -> None:
|
|
742
|
-
for
|
|
743
|
-
|
|
744
|
-
'indextitle':
|
|
764
|
+
for index_name, index_cls, content, collapse in self.domain_indices:
|
|
765
|
+
index_context = {
|
|
766
|
+
'indextitle': index_cls.localname,
|
|
745
767
|
'content': content,
|
|
746
768
|
'collapse_index': collapse,
|
|
747
769
|
}
|
|
748
|
-
logger.info(
|
|
749
|
-
self.handle_page(
|
|
770
|
+
logger.info('%s ', index_name, nonl=True)
|
|
771
|
+
self.handle_page(index_name, index_context, 'domainindex.html')
|
|
750
772
|
|
|
751
773
|
def copy_image_files(self) -> None:
|
|
752
774
|
if self.images:
|
|
753
|
-
stringify_func = ImageAdapter(self.
|
|
754
|
-
ensuredir(self.
|
|
775
|
+
stringify_func = ImageAdapter(self.env).get_original_image_uri
|
|
776
|
+
ensuredir(self._images_dir)
|
|
755
777
|
for src in status_iterator(
|
|
756
778
|
self.images,
|
|
757
779
|
__('copying images... '),
|
|
@@ -764,7 +786,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
764
786
|
try:
|
|
765
787
|
copyfile(
|
|
766
788
|
self.srcdir / src,
|
|
767
|
-
self.
|
|
789
|
+
self._images_dir / dest,
|
|
768
790
|
force=True,
|
|
769
791
|
)
|
|
770
792
|
except Exception as err:
|
|
@@ -774,11 +796,11 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
774
796
|
|
|
775
797
|
def copy_download_files(self) -> None:
|
|
776
798
|
def to_relpath(f: str) -> str:
|
|
777
|
-
return
|
|
799
|
+
return _relative_path(Path(f), self.srcdir).as_posix()
|
|
778
800
|
|
|
779
801
|
# copy downloadable files
|
|
780
802
|
if self.env.dlfiles:
|
|
781
|
-
ensuredir(self.
|
|
803
|
+
ensuredir(self._downloads_dir)
|
|
782
804
|
for src in status_iterator(
|
|
783
805
|
self.env.dlfiles,
|
|
784
806
|
__('copying downloadable files... '),
|
|
@@ -788,7 +810,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
788
810
|
stringify_func=to_relpath,
|
|
789
811
|
):
|
|
790
812
|
try:
|
|
791
|
-
dest = self.
|
|
813
|
+
dest = self._downloads_dir / self.env.dlfiles[src][1]
|
|
792
814
|
ensuredir(dest.parent)
|
|
793
815
|
copyfile(self.srcdir / src, dest, force=True)
|
|
794
816
|
except OSError as err:
|
|
@@ -800,22 +822,21 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
800
822
|
|
|
801
823
|
def create_pygments_style_file(self) -> None:
|
|
802
824
|
"""Create a style file for pygments."""
|
|
803
|
-
pyg_path =
|
|
825
|
+
pyg_path = self._static_dir / 'pygments.css'
|
|
804
826
|
with open(pyg_path, 'w', encoding='utf-8') as f:
|
|
805
827
|
f.write(self.highlighter.get_stylesheet())
|
|
806
828
|
|
|
807
829
|
if self.dark_highlighter:
|
|
808
|
-
dark_path =
|
|
830
|
+
dark_path = self._static_dir / 'pygments_dark.css'
|
|
809
831
|
with open(dark_path, 'w', encoding='utf-8') as f:
|
|
810
832
|
f.write(self.dark_highlighter.get_stylesheet())
|
|
811
833
|
|
|
812
834
|
def copy_translation_js(self) -> None:
|
|
813
835
|
"""Copy a JavaScript file for translations."""
|
|
814
|
-
|
|
815
|
-
if jsfile:
|
|
836
|
+
if js_file := self._get_translations_js():
|
|
816
837
|
copyfile(
|
|
817
|
-
|
|
818
|
-
self.
|
|
838
|
+
js_file,
|
|
839
|
+
self._static_dir / 'translations.js',
|
|
819
840
|
force=True,
|
|
820
841
|
)
|
|
821
842
|
|
|
@@ -827,14 +848,14 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
827
848
|
js_path = Path(jsfile)
|
|
828
849
|
copyfile(
|
|
829
850
|
js_path,
|
|
830
|
-
self.
|
|
851
|
+
self._static_dir / js_path.name,
|
|
831
852
|
force=True,
|
|
832
853
|
)
|
|
833
854
|
else:
|
|
834
855
|
if js_stemmer_rawcode := self.indexer.get_js_stemmer_rawcode():
|
|
835
856
|
copyfile(
|
|
836
857
|
js_stemmer_rawcode,
|
|
837
|
-
self.
|
|
858
|
+
self._static_dir / '_stemmer.js',
|
|
838
859
|
force=True,
|
|
839
860
|
)
|
|
840
861
|
|
|
@@ -846,8 +867,8 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
846
867
|
if self.theme:
|
|
847
868
|
for entry in reversed(self.theme.get_theme_dirs()):
|
|
848
869
|
copy_asset(
|
|
849
|
-
Path(entry
|
|
850
|
-
self.
|
|
870
|
+
Path(entry, 'static'),
|
|
871
|
+
self._static_dir,
|
|
851
872
|
excluded=DOTFILES,
|
|
852
873
|
context=context,
|
|
853
874
|
renderer=self.templates,
|
|
@@ -865,7 +886,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
865
886
|
for entry in self.config.html_static_path:
|
|
866
887
|
copy_asset(
|
|
867
888
|
self.confdir / entry,
|
|
868
|
-
self.
|
|
889
|
+
self._static_dir,
|
|
869
890
|
excluded=excluded,
|
|
870
891
|
context=context,
|
|
871
892
|
renderer=self.templates,
|
|
@@ -878,7 +899,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
878
899
|
source_path = self.confdir / self.config.html_logo
|
|
879
900
|
copyfile(
|
|
880
901
|
source_path,
|
|
881
|
-
self.
|
|
902
|
+
self._static_dir / source_path.name,
|
|
882
903
|
force=True,
|
|
883
904
|
)
|
|
884
905
|
|
|
@@ -887,14 +908,15 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
887
908
|
source_path = self.confdir / self.config.html_favicon
|
|
888
909
|
copyfile(
|
|
889
910
|
source_path,
|
|
890
|
-
self.
|
|
911
|
+
self._static_dir / source_path.name,
|
|
891
912
|
force=True,
|
|
892
913
|
)
|
|
893
914
|
|
|
894
915
|
def copy_static_files(self) -> None:
|
|
895
916
|
try:
|
|
896
917
|
with progress_message(__('copying static files'), nonl=False):
|
|
897
|
-
|
|
918
|
+
# Ensure that the static directory exists
|
|
919
|
+
self._static_dir.mkdir(parents=True, exist_ok=True)
|
|
898
920
|
|
|
899
921
|
# prepare context for templates
|
|
900
922
|
context = self.globalcontext.copy()
|
|
@@ -969,12 +991,12 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
969
991
|
assert self.indexer is not None
|
|
970
992
|
keep = set(self.env.all_docs).difference(docnames)
|
|
971
993
|
try:
|
|
972
|
-
|
|
994
|
+
search_index_path = self.outdir / self.searchindex_filename
|
|
973
995
|
if self.indexer_dumps_unicode:
|
|
974
|
-
with open(
|
|
996
|
+
with open(search_index_path, encoding='utf-8') as ft:
|
|
975
997
|
self.indexer.load(ft, self.indexer_format)
|
|
976
998
|
else:
|
|
977
|
-
with open(
|
|
999
|
+
with open(search_index_path, 'rb') as fb:
|
|
978
1000
|
self.indexer.load(fb, self.indexer_format)
|
|
979
1001
|
except (OSError, ValueError):
|
|
980
1002
|
if keep:
|
|
@@ -991,7 +1013,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
991
1013
|
def index_page(self, pagename: str, doctree: nodes.document, title: str) -> None:
|
|
992
1014
|
# only index pages with title
|
|
993
1015
|
if self.indexer is not None and title:
|
|
994
|
-
filename =
|
|
1016
|
+
filename = self.env.doc2path(pagename, base=False)
|
|
995
1017
|
metadata = self.env.metadata.get(pagename, {})
|
|
996
1018
|
if 'no-search' in metadata or 'nosearch' in metadata:
|
|
997
1019
|
self.indexer.feed(pagename, filename, '', new_document(''))
|
|
@@ -1003,15 +1025,18 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1003
1025
|
) -> str:
|
|
1004
1026
|
if 'includehidden' not in kwargs:
|
|
1005
1027
|
kwargs['includehidden'] = False
|
|
1006
|
-
if kwargs.get('maxdepth') == '':
|
|
1028
|
+
if kwargs.get('maxdepth') == '': # NoQA: PLC1901
|
|
1007
1029
|
kwargs.pop('maxdepth')
|
|
1008
1030
|
toctree = global_toctree_for_doc(
|
|
1009
1031
|
self.env, docname, self, collapse=collapse, **kwargs
|
|
1010
1032
|
)
|
|
1011
1033
|
return self.render_partial(toctree)['fragment']
|
|
1012
1034
|
|
|
1013
|
-
def
|
|
1014
|
-
return
|
|
1035
|
+
def get_output_path(self, page_name: str, /) -> Path:
|
|
1036
|
+
return Path(self.outdir, page_name + self.out_suffix)
|
|
1037
|
+
|
|
1038
|
+
def get_outfilename(self, pagename: str) -> _StrPath:
|
|
1039
|
+
return _StrPath(self.get_output_path(pagename))
|
|
1015
1040
|
|
|
1016
1041
|
def add_sidebars(self, pagename: str, ctx: dict[str, Any]) -> None:
|
|
1017
1042
|
def has_wildcard(pattern: str) -> bool:
|
|
@@ -1030,7 +1055,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1030
1055
|
if matched and has_wildcard(pattern):
|
|
1031
1056
|
# warn if both patterns contain wildcards
|
|
1032
1057
|
if has_wildcard(matched):
|
|
1033
|
-
logger.warning(msg, pagename, matched)
|
|
1058
|
+
logger.warning(msg, pagename, matched, pattern)
|
|
1034
1059
|
# else the already matched pattern is more specific
|
|
1035
1060
|
# than the present one, because it contains no wildcard
|
|
1036
1061
|
continue
|
|
@@ -1049,7 +1074,8 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1049
1074
|
pagename: str,
|
|
1050
1075
|
addctx: dict[str, Any],
|
|
1051
1076
|
templatename: str = 'page.html',
|
|
1052
|
-
|
|
1077
|
+
*,
|
|
1078
|
+
outfilename: Path | None = None,
|
|
1053
1079
|
event_arg: Any = None,
|
|
1054
1080
|
) -> None:
|
|
1055
1081
|
ctx = self.globalcontext.copy()
|
|
@@ -1137,8 +1163,9 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1137
1163
|
return f'<script {" ".join(sorted(attrs))}>{body}</script>'
|
|
1138
1164
|
return f'<script>{body}</script>'
|
|
1139
1165
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1166
|
+
js_filename_str = os.fspath(js.filename)
|
|
1167
|
+
uri = pathto(js_filename_str, resource=True)
|
|
1168
|
+
if 'MathJax.js?' in js_filename_str:
|
|
1142
1169
|
# MathJax v2 reads a ``?config=...`` query parameter,
|
|
1143
1170
|
# special case this and just skip adding the checksum.
|
|
1144
1171
|
# https://docs.mathjax.org/en/v2.7-latest/configuration.html#considerations-for-using-combined-configuration-files
|
|
@@ -1159,21 +1186,21 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1159
1186
|
self._js_files[:] = self._orig_js_files
|
|
1160
1187
|
|
|
1161
1188
|
self.update_page_context(pagename, templatename, ctx, event_arg)
|
|
1162
|
-
|
|
1189
|
+
if new_template := self.events.emit_firstresult(
|
|
1163
1190
|
'html-page-context', pagename, templatename, ctx, event_arg
|
|
1164
|
-
)
|
|
1165
|
-
|
|
1166
|
-
templatename = newtmpl
|
|
1191
|
+
):
|
|
1192
|
+
templatename = new_template
|
|
1167
1193
|
|
|
1168
1194
|
# sort JS/CSS before rendering HTML
|
|
1169
1195
|
try: # NoQA: SIM105
|
|
1170
|
-
# Convert script_files to list to support non-list script_files
|
|
1196
|
+
# Convert script_files to list to support non-list script_files
|
|
1197
|
+
# See: https://github.com/sphinx-doc/sphinx/issues/8889
|
|
1171
1198
|
ctx['script_files'] = sorted(
|
|
1172
1199
|
ctx['script_files'], key=lambda js: js.priority
|
|
1173
1200
|
)
|
|
1174
1201
|
except AttributeError:
|
|
1175
1202
|
# Skip sorting if users modifies script_files directly (maybe via `html_context`).
|
|
1176
|
-
#
|
|
1203
|
+
# See: https://github.com/sphinx-doc/sphinx/issues/8885
|
|
1177
1204
|
#
|
|
1178
1205
|
# Note: priority sorting feature will not work in this case.
|
|
1179
1206
|
pass
|
|
@@ -1200,22 +1227,23 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1200
1227
|
)
|
|
1201
1228
|
raise ThemeError(msg) from exc
|
|
1202
1229
|
|
|
1203
|
-
if
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1230
|
+
if outfilename:
|
|
1231
|
+
output_path = Path(outfilename)
|
|
1232
|
+
else:
|
|
1233
|
+
output_path = self.get_output_path(pagename)
|
|
1234
|
+
# The output path is in general different from self.outdir
|
|
1235
|
+
ensuredir(output_path.parent)
|
|
1207
1236
|
try:
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
)
|
|
1211
|
-
f.write(output)
|
|
1237
|
+
output_path.write_text(
|
|
1238
|
+
output, encoding=ctx['encoding'], errors='xmlcharrefreplace'
|
|
1239
|
+
)
|
|
1212
1240
|
except OSError as err:
|
|
1213
|
-
logger.warning(__('error writing file %s: %s'),
|
|
1241
|
+
logger.warning(__('error writing file %s: %s'), output_path, err)
|
|
1214
1242
|
if self.copysource and ctx.get('sourcename'):
|
|
1215
1243
|
# copy the source file for the "show source" link
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
copyfile(self.env.doc2path(pagename),
|
|
1244
|
+
source_file_path = self._sources_dir / ctx['sourcename']
|
|
1245
|
+
source_file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1246
|
+
copyfile(self.env.doc2path(pagename), source_file_path, force=True)
|
|
1219
1247
|
|
|
1220
1248
|
def update_page_context(
|
|
1221
1249
|
self, pagename: str, templatename: str, ctx: dict[str, Any], event_arg: Any
|
|
@@ -1228,7 +1256,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1228
1256
|
|
|
1229
1257
|
@progress_message(__('dumping object inventory'))
|
|
1230
1258
|
def dump_inventory(self) -> None:
|
|
1231
|
-
InventoryFile.dump(
|
|
1259
|
+
InventoryFile.dump(self.outdir / INVENTORY_FILENAME, self.env, self)
|
|
1232
1260
|
|
|
1233
1261
|
def dump_search_index(self) -> None:
|
|
1234
1262
|
if self.indexer is None:
|
|
@@ -1236,16 +1264,17 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1236
1264
|
|
|
1237
1265
|
with progress_message(__('dumping search index in %s') % self.indexer.label()):
|
|
1238
1266
|
self.indexer.prune(self.env.all_docs)
|
|
1239
|
-
|
|
1267
|
+
search_index_path = self.outdir / self.searchindex_filename
|
|
1268
|
+
search_index_tmp = self.outdir / f'{self.searchindex_filename}.tmp'
|
|
1240
1269
|
# first write to a temporary file, so that if dumping fails,
|
|
1241
1270
|
# the existing index won't be overwritten
|
|
1242
1271
|
if self.indexer_dumps_unicode:
|
|
1243
|
-
with open(
|
|
1272
|
+
with open(search_index_tmp, 'w', encoding='utf-8') as ft:
|
|
1244
1273
|
self.indexer.dump(ft, self.indexer_format)
|
|
1245
1274
|
else:
|
|
1246
|
-
with open(
|
|
1275
|
+
with open(search_index_tmp, 'wb') as fb:
|
|
1247
1276
|
self.indexer.dump(fb, self.indexer_format)
|
|
1248
|
-
|
|
1277
|
+
Path(search_index_tmp).replace(search_index_path)
|
|
1249
1278
|
|
|
1250
1279
|
|
|
1251
1280
|
def convert_html_css_files(app: Sphinx, config: Config) -> None:
|
|
@@ -1319,43 +1348,49 @@ def validate_math_renderer(app: Sphinx) -> None:
|
|
|
1319
1348
|
|
|
1320
1349
|
def validate_html_extra_path(app: Sphinx, config: Config) -> None:
|
|
1321
1350
|
"""Check html_extra_paths setting."""
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1351
|
+
html_extra_path = []
|
|
1352
|
+
for entry in config.html_extra_path:
|
|
1353
|
+
extra_path = (app.confdir / entry).resolve()
|
|
1354
|
+
if extra_path.exists():
|
|
1355
|
+
if (
|
|
1356
|
+
app.outdir.drive == extra_path.drive
|
|
1357
|
+
and extra_path.is_relative_to(app.outdir)
|
|
1358
|
+
): # fmt: skip
|
|
1359
|
+
logger.warning(
|
|
1360
|
+
__('html_extra_path entry %r is placed inside outdir'), entry
|
|
1361
|
+
)
|
|
1362
|
+
else:
|
|
1363
|
+
html_extra_path.append(entry)
|
|
1364
|
+
else:
|
|
1325
1365
|
logger.warning(__('html_extra_path entry %r does not exist'), entry)
|
|
1326
|
-
|
|
1327
|
-
elif (
|
|
1328
|
-
path.splitdrive(app.outdir)[0] == path.splitdrive(extra_path)[0]
|
|
1329
|
-
and path.commonpath((app.outdir, extra_path)) == path.normpath(app.outdir)
|
|
1330
|
-
): # fmt: skip
|
|
1331
|
-
logger.warning(
|
|
1332
|
-
__('html_extra_path entry %r is placed inside outdir'), entry
|
|
1333
|
-
)
|
|
1334
|
-
config.html_extra_path.remove(entry)
|
|
1366
|
+
config.html_extra_path = html_extra_path
|
|
1335
1367
|
|
|
1336
1368
|
|
|
1337
1369
|
def validate_html_static_path(app: Sphinx, config: Config) -> None:
|
|
1338
1370
|
"""Check html_static_paths setting."""
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1371
|
+
html_static_path = []
|
|
1372
|
+
for entry in config.html_static_path:
|
|
1373
|
+
static_path = (app.confdir / entry).resolve()
|
|
1374
|
+
if static_path.exists():
|
|
1375
|
+
if (
|
|
1376
|
+
app.outdir.drive == static_path.drive
|
|
1377
|
+
and static_path.is_relative_to(app.outdir)
|
|
1378
|
+
): # fmt: skip
|
|
1379
|
+
logger.warning(
|
|
1380
|
+
__('html_static_path entry %r is placed inside outdir'), entry
|
|
1381
|
+
)
|
|
1382
|
+
else:
|
|
1383
|
+
html_static_path.append(entry)
|
|
1384
|
+
else:
|
|
1342
1385
|
logger.warning(__('html_static_path entry %r does not exist'), entry)
|
|
1343
|
-
|
|
1344
|
-
elif (
|
|
1345
|
-
path.splitdrive(app.outdir)[0] == path.splitdrive(static_path)[0]
|
|
1346
|
-
and path.commonpath((app.outdir, static_path)) == path.normpath(app.outdir)
|
|
1347
|
-
): # fmt: skip
|
|
1348
|
-
logger.warning(
|
|
1349
|
-
__('html_static_path entry %r is placed inside outdir'), entry
|
|
1350
|
-
)
|
|
1351
|
-
config.html_static_path.remove(entry)
|
|
1386
|
+
config.html_static_path = html_static_path
|
|
1352
1387
|
|
|
1353
1388
|
|
|
1354
1389
|
def validate_html_logo(app: Sphinx, config: Config) -> None:
|
|
1355
1390
|
"""Check html_logo setting."""
|
|
1356
1391
|
if (
|
|
1357
1392
|
config.html_logo
|
|
1358
|
-
and not
|
|
1393
|
+
and not (app.confdir / config.html_logo).is_file()
|
|
1359
1394
|
and not is_url(config.html_logo)
|
|
1360
1395
|
):
|
|
1361
1396
|
logger.warning(__('logo file %r does not exist'), config.html_logo)
|
|
@@ -1366,7 +1401,7 @@ def validate_html_favicon(app: Sphinx, config: Config) -> None:
|
|
|
1366
1401
|
"""Check html_favicon setting."""
|
|
1367
1402
|
if (
|
|
1368
1403
|
config.html_favicon
|
|
1369
|
-
and not
|
|
1404
|
+
and not (app.confdir / config.html_favicon).is_file()
|
|
1370
1405
|
and not is_url(config.html_favicon)
|
|
1371
1406
|
):
|
|
1372
1407
|
logger.warning(__('favicon file %r does not exist'), config.html_favicon)
|
|
@@ -1406,56 +1441,78 @@ def setup(app: Sphinx) -> ExtensionMetadata:
|
|
|
1406
1441
|
app.add_builder(StandaloneHTMLBuilder)
|
|
1407
1442
|
|
|
1408
1443
|
# config values
|
|
1409
|
-
app.add_config_value('html_theme', 'alabaster', 'html')
|
|
1410
|
-
app.add_config_value('html_theme_path', [], 'html')
|
|
1411
|
-
app.add_config_value('html_theme_options', {}, 'html')
|
|
1444
|
+
app.add_config_value('html_theme', 'alabaster', 'html', types=frozenset({str}))
|
|
1445
|
+
app.add_config_value('html_theme_path', [], 'html', types=frozenset({list, tuple}))
|
|
1446
|
+
app.add_config_value('html_theme_options', {}, 'html', types=frozenset({dict}))
|
|
1412
1447
|
app.add_config_value(
|
|
1413
1448
|
'html_title',
|
|
1414
1449
|
lambda c: _('%s %s documentation') % (c.project, c.release),
|
|
1415
1450
|
'html',
|
|
1416
|
-
str,
|
|
1451
|
+
types=frozenset({str}),
|
|
1452
|
+
)
|
|
1453
|
+
app.add_config_value(
|
|
1454
|
+
'html_short_title', lambda self: self.html_title, 'html', types=frozenset({str})
|
|
1455
|
+
)
|
|
1456
|
+
app.add_config_value(
|
|
1457
|
+
'html_style', None, 'html', types=frozenset({list, str, tuple})
|
|
1458
|
+
)
|
|
1459
|
+
app.add_config_value('html_logo', None, 'html', types=frozenset({str}))
|
|
1460
|
+
app.add_config_value('html_favicon', None, 'html', types=frozenset({str}))
|
|
1461
|
+
app.add_config_value('html_css_files', [], 'html', types=frozenset({list, tuple}))
|
|
1462
|
+
app.add_config_value('html_js_files', [], 'html', types=frozenset({list, tuple}))
|
|
1463
|
+
app.add_config_value('html_static_path', [], 'html', types=frozenset({list, tuple}))
|
|
1464
|
+
app.add_config_value('html_extra_path', [], 'html', types=frozenset({list, tuple}))
|
|
1465
|
+
app.add_config_value('html_last_updated_fmt', None, 'html', types=frozenset({str}))
|
|
1466
|
+
app.add_config_value(
|
|
1467
|
+
'html_last_updated_use_utc', False, 'html', types=frozenset({bool})
|
|
1468
|
+
)
|
|
1469
|
+
app.add_config_value('html_sidebars', {}, 'html', types=frozenset({dict}))
|
|
1470
|
+
app.add_config_value('html_additional_pages', {}, 'html', types=frozenset({dict}))
|
|
1471
|
+
app.add_config_value(
|
|
1472
|
+
'html_domain_indices',
|
|
1473
|
+
True,
|
|
1474
|
+
'html',
|
|
1475
|
+
types=frozenset({frozenset, list, set, tuple}),
|
|
1476
|
+
)
|
|
1477
|
+
app.add_config_value('html_permalinks', True, 'html', types=frozenset({bool}))
|
|
1478
|
+
app.add_config_value('html_permalinks_icon', '¶', 'html', types=frozenset({str}))
|
|
1479
|
+
app.add_config_value('html_use_index', True, 'html', types=frozenset({bool}))
|
|
1480
|
+
app.add_config_value('html_split_index', False, 'html', types=frozenset({bool}))
|
|
1481
|
+
app.add_config_value('html_copy_source', True, 'html', types=frozenset({bool}))
|
|
1482
|
+
app.add_config_value('html_show_sourcelink', True, 'html', types=frozenset({bool}))
|
|
1483
|
+
app.add_config_value(
|
|
1484
|
+
'html_sourcelink_suffix', '.txt', 'html', types=frozenset({str})
|
|
1485
|
+
)
|
|
1486
|
+
app.add_config_value('html_use_opensearch', '', 'html', types=frozenset({str}))
|
|
1487
|
+
app.add_config_value('html_file_suffix', None, 'html', types=frozenset({str}))
|
|
1488
|
+
app.add_config_value('html_link_suffix', None, 'html', types=frozenset({str}))
|
|
1489
|
+
app.add_config_value('html_show_copyright', True, 'html', types=frozenset({bool}))
|
|
1490
|
+
app.add_config_value(
|
|
1491
|
+
'html_show_search_summary', True, 'html', types=frozenset({bool})
|
|
1492
|
+
)
|
|
1493
|
+
app.add_config_value('html_show_sphinx', True, 'html', types=frozenset({bool}))
|
|
1494
|
+
app.add_config_value('html_context', {}, 'html', types=frozenset({dict}))
|
|
1495
|
+
app.add_config_value(
|
|
1496
|
+
'html_output_encoding', 'utf-8', 'html', types=frozenset({str})
|
|
1497
|
+
)
|
|
1498
|
+
app.add_config_value('html_compact_lists', True, 'html', types=frozenset({bool}))
|
|
1499
|
+
app.add_config_value('html_secnumber_suffix', '. ', 'html', types=frozenset({str}))
|
|
1500
|
+
app.add_config_value('html_search_language', None, 'html', types=frozenset({str}))
|
|
1501
|
+
app.add_config_value('html_search_options', {}, 'html', types=frozenset({dict}))
|
|
1502
|
+
app.add_config_value('html_search_scorer', '', '', types=frozenset({str}))
|
|
1503
|
+
app.add_config_value(
|
|
1504
|
+
'html_scaled_image_link', True, 'html', types=frozenset({bool})
|
|
1417
1505
|
)
|
|
1418
|
-
app.add_config_value('
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
app.add_config_value('html_favicon', None, 'html', str)
|
|
1422
|
-
app.add_config_value('html_css_files', [], 'html')
|
|
1423
|
-
app.add_config_value('html_js_files', [], 'html')
|
|
1424
|
-
app.add_config_value('html_static_path', [], 'html')
|
|
1425
|
-
app.add_config_value('html_extra_path', [], 'html')
|
|
1426
|
-
app.add_config_value('html_last_updated_fmt', None, 'html', str)
|
|
1427
|
-
app.add_config_value('html_last_updated_use_utc', False, 'html', types={bool})
|
|
1428
|
-
app.add_config_value('html_sidebars', {}, 'html')
|
|
1429
|
-
app.add_config_value('html_additional_pages', {}, 'html')
|
|
1430
|
-
app.add_config_value('html_domain_indices', True, 'html', types={set, list})
|
|
1431
|
-
app.add_config_value('html_permalinks', True, 'html')
|
|
1432
|
-
app.add_config_value('html_permalinks_icon', '¶', 'html')
|
|
1433
|
-
app.add_config_value('html_use_index', True, 'html')
|
|
1434
|
-
app.add_config_value('html_split_index', False, 'html')
|
|
1435
|
-
app.add_config_value('html_copy_source', True, 'html')
|
|
1436
|
-
app.add_config_value('html_show_sourcelink', True, 'html')
|
|
1437
|
-
app.add_config_value('html_sourcelink_suffix', '.txt', 'html')
|
|
1438
|
-
app.add_config_value('html_use_opensearch', '', 'html')
|
|
1439
|
-
app.add_config_value('html_file_suffix', None, 'html', str)
|
|
1440
|
-
app.add_config_value('html_link_suffix', None, 'html', str)
|
|
1441
|
-
app.add_config_value('html_show_copyright', True, 'html')
|
|
1442
|
-
app.add_config_value('html_show_search_summary', True, 'html')
|
|
1443
|
-
app.add_config_value('html_show_sphinx', True, 'html')
|
|
1444
|
-
app.add_config_value('html_context', {}, 'html')
|
|
1445
|
-
app.add_config_value('html_output_encoding', 'utf-8', 'html')
|
|
1446
|
-
app.add_config_value('html_compact_lists', True, 'html')
|
|
1447
|
-
app.add_config_value('html_secnumber_suffix', '. ', 'html')
|
|
1448
|
-
app.add_config_value('html_search_language', None, 'html', str)
|
|
1449
|
-
app.add_config_value('html_search_options', {}, 'html')
|
|
1450
|
-
app.add_config_value('html_search_scorer', '', '')
|
|
1451
|
-
app.add_config_value('html_scaled_image_link', True, 'html')
|
|
1452
|
-
app.add_config_value('html_baseurl', '', 'html')
|
|
1453
|
-
# removal is indefinitely on hold (ref: https://github.com/sphinx-doc/sphinx/issues/10265)
|
|
1506
|
+
app.add_config_value('html_baseurl', '', 'html', types=frozenset({str}))
|
|
1507
|
+
# removal is indefinitely on hold
|
|
1508
|
+
# See: https://github.com/sphinx-doc/sphinx/issues/10265
|
|
1454
1509
|
app.add_config_value(
|
|
1455
|
-
'html_codeblock_linenos_style', 'inline', 'html', ENUM('table', 'inline')
|
|
1510
|
+
'html_codeblock_linenos_style', 'inline', 'html', types=ENUM('table', 'inline')
|
|
1456
1511
|
)
|
|
1457
|
-
app.add_config_value(
|
|
1458
|
-
|
|
1512
|
+
app.add_config_value(
|
|
1513
|
+
'html_math_renderer', None, 'env', types=frozenset({str, NoneType})
|
|
1514
|
+
)
|
|
1515
|
+
app.add_config_value('html4_writer', False, 'html', types=frozenset({bool}))
|
|
1459
1516
|
|
|
1460
1517
|
# events
|
|
1461
1518
|
app.add_event('html-collect-pages')
|
|
@@ -1476,9 +1533,6 @@ def setup(app: Sphinx) -> ExtensionMetadata:
|
|
|
1476
1533
|
# load default math renderer
|
|
1477
1534
|
app.setup_extension('sphinx.ext.mathjax')
|
|
1478
1535
|
|
|
1479
|
-
# load transforms for HTML builder
|
|
1480
|
-
app.setup_extension('sphinx.builders.html.transforms')
|
|
1481
|
-
|
|
1482
1536
|
return {
|
|
1483
1537
|
'version': 'builtin',
|
|
1484
1538
|
'parallel_read_safe': True,
|