Sphinx 7.1.1__py3-none-any.whl → 7.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 +6 -6
- sphinx/__main__.py +3 -1
- sphinx/addnodes.py +35 -22
- sphinx/application.py +40 -38
- sphinx/builders/__init__.py +16 -12
- sphinx/builders/_epub_base.py +15 -11
- sphinx/builders/changes.py +6 -4
- sphinx/builders/dirhtml.py +4 -2
- sphinx/builders/dummy.py +6 -4
- sphinx/builders/epub3.py +16 -8
- sphinx/builders/gettext.py +40 -43
- sphinx/builders/html/__init__.py +166 -196
- sphinx/builders/html/_assets.py +116 -0
- sphinx/builders/html/transforms.py +4 -2
- sphinx/builders/latex/__init__.py +12 -7
- sphinx/builders/latex/theming.py +5 -2
- sphinx/builders/latex/transforms.py +6 -3
- sphinx/builders/linkcheck.py +21 -13
- sphinx/builders/manpage.py +6 -4
- sphinx/builders/singlehtml.py +16 -9
- sphinx/builders/texinfo.py +11 -6
- sphinx/builders/text.py +8 -3
- sphinx/builders/xml.py +9 -4
- sphinx/cmd/build.py +27 -14
- sphinx/cmd/make_mode.py +13 -4
- sphinx/cmd/quickstart.py +13 -4
- sphinx/config.py +17 -14
- sphinx/deprecation.py +4 -2
- sphinx/directives/__init__.py +44 -12
- sphinx/directives/code.py +5 -4
- sphinx/directives/other.py +92 -44
- sphinx/directives/patches.py +1 -1
- sphinx/domains/__init__.py +11 -8
- sphinx/domains/c.py +67 -57
- sphinx/domains/changeset.py +3 -2
- sphinx/domains/citation.py +2 -1
- sphinx/domains/cpp.py +136 -93
- sphinx/domains/index.py +9 -5
- sphinx/domains/javascript.py +32 -19
- sphinx/domains/math.py +5 -3
- sphinx/domains/python.py +69 -57
- sphinx/domains/rst.py +20 -11
- sphinx/domains/std.py +21 -15
- sphinx/environment/__init__.py +97 -65
- sphinx/environment/adapters/indexentries.py +13 -10
- sphinx/environment/adapters/toctree.py +485 -308
- sphinx/environment/collectors/__init__.py +3 -4
- sphinx/environment/collectors/asset.py +10 -4
- sphinx/environment/collectors/dependencies.py +7 -4
- sphinx/environment/collectors/metadata.py +7 -5
- sphinx/environment/collectors/title.py +5 -3
- sphinx/environment/collectors/toctree.py +13 -8
- sphinx/errors.py +1 -1
- sphinx/events.py +5 -5
- sphinx/ext/apidoc.py +49 -27
- sphinx/ext/autodoc/__init__.py +179 -161
- sphinx/ext/autodoc/directive.py +10 -6
- sphinx/ext/autodoc/importer.py +22 -13
- sphinx/ext/autodoc/mock.py +4 -1
- sphinx/ext/autodoc/preserve_defaults.py +80 -12
- sphinx/ext/autodoc/type_comment.py +14 -10
- sphinx/ext/autodoc/typehints.py +7 -3
- sphinx/ext/autosectionlabel.py +6 -3
- sphinx/ext/autosummary/__init__.py +21 -15
- sphinx/ext/autosummary/generate.py +176 -126
- sphinx/ext/coverage.py +93 -8
- sphinx/ext/doctest.py +28 -17
- sphinx/ext/duration.py +19 -17
- sphinx/ext/extlinks.py +11 -6
- sphinx/ext/githubpages.py +8 -7
- sphinx/ext/graphviz.py +61 -17
- sphinx/ext/ifconfig.py +7 -4
- sphinx/ext/imgconverter.py +4 -2
- sphinx/ext/imgmath.py +29 -23
- sphinx/ext/inheritance_diagram.py +41 -27
- sphinx/ext/intersphinx.py +45 -38
- sphinx/ext/linkcode.py +8 -5
- sphinx/ext/mathjax.py +13 -9
- sphinx/ext/napoleon/__init__.py +3 -3
- sphinx/ext/napoleon/docstring.py +40 -31
- sphinx/ext/todo.py +10 -7
- sphinx/ext/viewcode.py +46 -25
- sphinx/extension.py +1 -1
- sphinx/highlighting.py +20 -12
- sphinx/io.py +5 -4
- sphinx/jinja2glue.py +24 -19
- sphinx/locale/__init__.py +8 -2
- sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ar/LC_MESSAGES/sphinx.po +756 -740
- sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bg/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bn/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cs/LC_MESSAGES/sphinx.po +758 -742
- sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cy/LC_MESSAGES/sphinx.po +759 -743
- sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/da/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de/LC_MESSAGES/sphinx.po +759 -743
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/el/LC_MESSAGES/sphinx.po +763 -747
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eo/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es/LC_MESSAGES/sphinx.po +767 -751
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/et/LC_MESSAGES/sphinx.po +762 -746
- sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eu/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fa/LC_MESSAGES/sphinx.po +766 -750
- sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fi/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/gl/LC_MESSAGES/sphinx.js +60 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.po +3695 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi/LC_MESSAGES/sphinx.po +763 -747
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hr/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hu/LC_MESSAGES/sphinx.po +759 -743
- sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/id/LC_MESSAGES/sphinx.po +765 -749
- sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/is/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/it/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ja/LC_MESSAGES/sphinx.po +767 -751
- sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ka/LC_MESSAGES/sphinx.po +759 -743
- sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ko/LC_MESSAGES/sphinx.po +767 -751
- sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lt/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lv/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/mk/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ne/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nl/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pl/LC_MESSAGES/sphinx.po +762 -745
- sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ro/LC_MESSAGES/sphinx.po +759 -743
- sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ru/LC_MESSAGES/sphinx.po +760 -744
- sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/si/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sk/LC_MESSAGES/sphinx.po +765 -749
- sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sl/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/sphinx.pot +748 -740
- sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sq/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.po +755 -739
- sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ta/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/te/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/tr/LC_MESSAGES/sphinx.po +763 -747
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +760 -749
- sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ur/LC_MESSAGES/sphinx.po +759 -748
- sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/vi/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/yue/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +768 -752
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +754 -738
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +767 -751
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +754 -738
- sphinx/parsers.py +5 -4
- sphinx/project.py +52 -34
- sphinx/pycode/__init__.py +2 -1
- sphinx/pycode/ast.py +7 -13
- sphinx/pycode/parser.py +42 -38
- sphinx/registry.py +35 -29
- sphinx/roles.py +9 -4
- sphinx/search/__init__.py +5 -17
- sphinx/search/da.py +1 -1
- sphinx/search/de.py +1 -1
- sphinx/search/en.py +1 -1
- sphinx/search/es.py +1 -1
- sphinx/search/fi.py +1 -1
- sphinx/search/fr.py +1 -1
- sphinx/search/hu.py +1 -1
- sphinx/search/it.py +1 -1
- sphinx/search/ja.py +1 -1
- sphinx/search/nl.py +1 -1
- sphinx/search/no.py +1 -1
- sphinx/search/pt.py +1 -1
- sphinx/search/ro.py +1 -1
- sphinx/search/ru.py +1 -1
- sphinx/search/sv.py +1 -1
- sphinx/search/tr.py +1 -1
- sphinx/search/zh.py +1 -1
- sphinx/testing/fixtures.py +23 -30
- sphinx/testing/path.py +9 -0
- sphinx/testing/restructuredtext.py +13 -5
- sphinx/testing/util.py +20 -63
- sphinx/texinputs/sphinxlatexobjects.sty +15 -15
- sphinx/themes/agogo/static/agogo.css_t +10 -4
- sphinx/themes/basic/layout.html +1 -1
- sphinx/themes/basic/static/basic.css_t +4 -0
- sphinx/themes/basic/static/documentation_options.js_t +1 -2
- sphinx/themes/basic/static/searchtools.js +17 -9
- sphinx/themes/basic/static/sphinx_highlight.js +13 -3
- sphinx/themes/bizstyle/static/bizstyle.css_t +4 -0
- sphinx/themes/classic/theme.conf +1 -1
- sphinx/themes/epub/static/epub.css_t +6 -1
- sphinx/themes/haiku/theme.conf +1 -1
- sphinx/themes/nature/static/nature.css_t +4 -0
- sphinx/themes/nonav/static/nonav.css_t +6 -1
- sphinx/themes/pyramid/static/pyramid.css_t +4 -0
- sphinx/themes/scrolls/static/scrolls.css_t +4 -0
- sphinx/themes/scrolls/theme.conf +1 -1
- sphinx/themes/sphinxdoc/static/sphinxdoc.css_t +4 -0
- sphinx/theming.py +9 -7
- sphinx/transforms/__init__.py +79 -3
- sphinx/transforms/compact_bullet_list.py +6 -3
- sphinx/transforms/i18n.py +26 -10
- sphinx/transforms/post_transforms/__init__.py +21 -8
- sphinx/transforms/post_transforms/code.py +6 -3
- sphinx/transforms/post_transforms/images.py +13 -9
- sphinx/util/__init__.py +21 -92
- sphinx/util/cfamily.py +7 -4
- sphinx/util/display.py +3 -2
- sphinx/util/docfields.py +7 -6
- sphinx/util/docstrings.py +1 -1
- sphinx/util/docutils.py +41 -31
- sphinx/util/fileutil.py +9 -6
- sphinx/util/i18n.py +21 -18
- sphinx/util/images.py +2 -1
- sphinx/util/index_entries.py +27 -0
- sphinx/util/inspect.py +83 -67
- sphinx/util/inventory.py +4 -2
- sphinx/util/logging.py +9 -6
- sphinx/util/matching.py +5 -2
- sphinx/util/math.py +6 -3
- sphinx/util/nodes.py +70 -31
- sphinx/util/osutil.py +22 -40
- sphinx/util/parallel.py +4 -1
- sphinx/util/rst.py +7 -3
- sphinx/util/tags.py +11 -4
- sphinx/util/template.py +17 -14
- sphinx/util/typing.py +61 -20
- sphinx/versioning.py +6 -4
- sphinx/writers/html.py +1 -1
- sphinx/writers/html5.py +32 -24
- sphinx/writers/latex.py +67 -53
- sphinx/writers/manpage.py +9 -5
- sphinx/writers/texinfo.py +11 -9
- sphinx/writers/text.py +14 -9
- sphinx/writers/xml.py +3 -2
- {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/METADATA +7 -5
- sphinx-7.2.0.dist-info/RECORD +568 -0
- sphinx/testing/comparer.py +0 -97
- sphinx-7.1.1.dist-info/RECORD +0 -564
- {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/LICENSE +0 -0
- {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/WHEEL +0 -0
- {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/entry_points.txt +0 -0
sphinx/builders/html/__init__.py
CHANGED
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import contextlib
|
|
6
|
+
import hashlib
|
|
5
7
|
import html
|
|
6
8
|
import os
|
|
7
9
|
import posixpath
|
|
8
10
|
import re
|
|
9
11
|
import sys
|
|
12
|
+
import time
|
|
10
13
|
import warnings
|
|
11
|
-
import zlib
|
|
12
|
-
from datetime import datetime, timezone
|
|
13
14
|
from os import path
|
|
14
|
-
from typing import IO,
|
|
15
|
+
from typing import IO, TYPE_CHECKING, Any
|
|
15
16
|
from urllib.parse import quote
|
|
16
17
|
|
|
17
18
|
import docutils.readers.doctree
|
|
@@ -19,49 +20,56 @@ from docutils import nodes
|
|
|
19
20
|
from docutils.core import Publisher
|
|
20
21
|
from docutils.frontend import OptionParser
|
|
21
22
|
from docutils.io import DocTreeInput, StringOutput
|
|
22
|
-
from docutils.nodes import Node
|
|
23
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.application import Sphinx
|
|
28
27
|
from sphinx.builders import Builder
|
|
28
|
+
from sphinx.builders.html._assets import _CascadingStyleSheet, _file_checksum, _JavaScript
|
|
29
29
|
from sphinx.config import ENUM, Config
|
|
30
|
+
from sphinx.deprecation import _deprecation_warning
|
|
30
31
|
from sphinx.domains import Domain, Index, IndexEntry
|
|
31
|
-
from sphinx.environment import BuildEnvironment
|
|
32
32
|
from sphinx.environment.adapters.asset import ImageAdapter
|
|
33
33
|
from sphinx.environment.adapters.indexentries import IndexEntries
|
|
34
|
-
from sphinx.environment.adapters.toctree import
|
|
34
|
+
from sphinx.environment.adapters.toctree import document_toc, global_toctree_for_doc
|
|
35
35
|
from sphinx.errors import ConfigError, ThemeError
|
|
36
36
|
from sphinx.highlighting import PygmentsBridge
|
|
37
37
|
from sphinx.locale import _, __
|
|
38
38
|
from sphinx.search import js_index
|
|
39
39
|
from sphinx.theming import HTMLThemeFactory
|
|
40
|
-
from sphinx.util import isurl, logging
|
|
40
|
+
from sphinx.util import isurl, logging
|
|
41
41
|
from sphinx.util.display import progress_message, status_iterator
|
|
42
42
|
from sphinx.util.docutils import new_document
|
|
43
43
|
from sphinx.util.fileutil import copy_asset
|
|
44
44
|
from sphinx.util.i18n import format_date
|
|
45
45
|
from sphinx.util.inventory import InventoryFile
|
|
46
46
|
from sphinx.util.matching import DOTFILES, Matcher, patmatch
|
|
47
|
-
from sphinx.util.osutil import copyfile, ensuredir, os_path, relative_uri
|
|
48
|
-
from sphinx.util.tags import Tags
|
|
47
|
+
from sphinx.util.osutil import SEP, copyfile, ensuredir, os_path, relative_uri
|
|
49
48
|
from sphinx.writers.html import HTMLWriter
|
|
50
49
|
from sphinx.writers.html5 import HTML5Translator
|
|
51
50
|
|
|
51
|
+
if TYPE_CHECKING:
|
|
52
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
53
|
+
|
|
54
|
+
from docutils.nodes import Node
|
|
55
|
+
|
|
56
|
+
from sphinx.application import Sphinx
|
|
57
|
+
from sphinx.environment import BuildEnvironment
|
|
58
|
+
from sphinx.util.tags import Tags
|
|
59
|
+
|
|
52
60
|
#: the filename for the inventory of objects
|
|
53
61
|
INVENTORY_FILENAME = 'objects.inv'
|
|
54
62
|
|
|
55
63
|
logger = logging.getLogger(__name__)
|
|
56
64
|
return_codes_re = re.compile('[\r\n]+')
|
|
57
65
|
|
|
58
|
-
DOMAIN_INDEX_TYPE =
|
|
66
|
+
DOMAIN_INDEX_TYPE = tuple[
|
|
59
67
|
# Index name (e.g. py-modindex)
|
|
60
68
|
str,
|
|
61
69
|
# Index class
|
|
62
|
-
|
|
70
|
+
type[Index],
|
|
63
71
|
# list of (heading string, list of index entries) pairs.
|
|
64
|
-
|
|
72
|
+
list[tuple[str, list[IndexEntry]]],
|
|
65
73
|
# whether sub-entries should start collapsed
|
|
66
74
|
bool,
|
|
67
75
|
]
|
|
@@ -77,7 +85,7 @@ def get_stable_hash(obj: Any) -> str:
|
|
|
77
85
|
return get_stable_hash(list(obj.items()))
|
|
78
86
|
elif isinstance(obj, (list, tuple)):
|
|
79
87
|
obj = sorted(get_stable_hash(o) for o in obj)
|
|
80
|
-
return md5(str(obj).encode()).hexdigest()
|
|
88
|
+
return hashlib.md5(str(obj).encode(), usedforsecurity=False).hexdigest()
|
|
81
89
|
|
|
82
90
|
|
|
83
91
|
def convert_locale_to_language_tag(locale: str | None) -> str | None:
|
|
@@ -91,52 +99,6 @@ def convert_locale_to_language_tag(locale: str | None) -> str | None:
|
|
|
91
99
|
return None
|
|
92
100
|
|
|
93
101
|
|
|
94
|
-
class Stylesheet(str):
|
|
95
|
-
"""A metadata of stylesheet.
|
|
96
|
-
|
|
97
|
-
To keep compatibility with old themes, an instance of stylesheet behaves as
|
|
98
|
-
its filename (str).
|
|
99
|
-
"""
|
|
100
|
-
|
|
101
|
-
attributes: dict[str, str] = None
|
|
102
|
-
filename: str = None
|
|
103
|
-
priority: int = None
|
|
104
|
-
|
|
105
|
-
def __new__(cls, filename: str, *args: str, priority: int = 500, **attributes: Any,
|
|
106
|
-
) -> Stylesheet:
|
|
107
|
-
self = str.__new__(cls, filename)
|
|
108
|
-
self.filename = filename
|
|
109
|
-
self.priority = priority
|
|
110
|
-
self.attributes = attributes
|
|
111
|
-
self.attributes.setdefault('rel', 'stylesheet')
|
|
112
|
-
self.attributes.setdefault('type', 'text/css')
|
|
113
|
-
if args: # old style arguments (rel, title)
|
|
114
|
-
self.attributes['rel'] = args[0]
|
|
115
|
-
self.attributes['title'] = args[1]
|
|
116
|
-
|
|
117
|
-
return self
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
class JavaScript(str):
|
|
121
|
-
"""A metadata of javascript file.
|
|
122
|
-
|
|
123
|
-
To keep compatibility with old themes, an instance of javascript behaves as
|
|
124
|
-
its filename (str).
|
|
125
|
-
"""
|
|
126
|
-
|
|
127
|
-
attributes: dict[str, str] = None
|
|
128
|
-
filename: str = None
|
|
129
|
-
priority: int = None
|
|
130
|
-
|
|
131
|
-
def __new__(cls, filename: str, priority: int = 500, **attributes: str) -> JavaScript:
|
|
132
|
-
self = str.__new__(cls, filename)
|
|
133
|
-
self.filename = filename
|
|
134
|
-
self.priority = priority
|
|
135
|
-
self.attributes = attributes
|
|
136
|
-
|
|
137
|
-
return self
|
|
138
|
-
|
|
139
|
-
|
|
140
102
|
class BuildInfo:
|
|
141
103
|
"""buildinfo file manipulator.
|
|
142
104
|
|
|
@@ -163,7 +125,7 @@ class BuildInfo:
|
|
|
163
125
|
self,
|
|
164
126
|
config: Config | None = None,
|
|
165
127
|
tags: Tags | None = None,
|
|
166
|
-
config_categories:
|
|
128
|
+
config_categories: Sequence[str] = (),
|
|
167
129
|
) -> None:
|
|
168
130
|
self.config_hash = ''
|
|
169
131
|
self.tags_hash = ''
|
|
@@ -175,7 +137,7 @@ class BuildInfo:
|
|
|
175
137
|
if tags:
|
|
176
138
|
self.tags_hash = get_stable_hash(sorted(tags))
|
|
177
139
|
|
|
178
|
-
def __eq__(self, other: BuildInfo) -> bool: # type: ignore
|
|
140
|
+
def __eq__(self, other: BuildInfo) -> bool: # type: ignore[override]
|
|
179
141
|
return (self.config_hash == other.config_hash and
|
|
180
142
|
self.tags_hash == other.tags_hash)
|
|
181
143
|
|
|
@@ -217,17 +179,17 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
217
179
|
use_index = False
|
|
218
180
|
download_support = True # enable download role
|
|
219
181
|
|
|
220
|
-
imgpath: str =
|
|
182
|
+
imgpath: str = ''
|
|
221
183
|
domain_indices: list[DOMAIN_INDEX_TYPE] = []
|
|
222
184
|
|
|
223
|
-
def __init__(self, app: Sphinx, env: BuildEnvironment
|
|
185
|
+
def __init__(self, app: Sphinx, env: BuildEnvironment) -> None:
|
|
224
186
|
super().__init__(app, env)
|
|
225
187
|
|
|
226
188
|
# CSS files
|
|
227
|
-
self.
|
|
189
|
+
self._css_files: list[_CascadingStyleSheet] = []
|
|
228
190
|
|
|
229
191
|
# JS files
|
|
230
|
-
self.
|
|
192
|
+
self._js_files: list[_JavaScript] = []
|
|
231
193
|
|
|
232
194
|
# Cached Publisher for writing doctrees to HTML
|
|
233
195
|
reader = docutils.readers.doctree.Reader(parser_name='restructuredtext')
|
|
@@ -252,7 +214,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
252
214
|
# section numbers for headings in the currently visited document
|
|
253
215
|
self.secnumbers: dict[str, tuple[int, ...]] = {}
|
|
254
216
|
# currently written docname
|
|
255
|
-
self.current_docname: str =
|
|
217
|
+
self.current_docname: str = ''
|
|
256
218
|
|
|
257
219
|
self.init_templates()
|
|
258
220
|
self.init_highlighter()
|
|
@@ -286,7 +248,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
286
248
|
for jsfile in candidates:
|
|
287
249
|
if path.isfile(jsfile):
|
|
288
250
|
return jsfile
|
|
289
|
-
return
|
|
251
|
+
return ''
|
|
290
252
|
|
|
291
253
|
def _get_style_filenames(self) -> Iterator[str]:
|
|
292
254
|
if isinstance(self.config.html_style, str):
|
|
@@ -325,6 +287,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
325
287
|
else:
|
|
326
288
|
dark_style = None
|
|
327
289
|
|
|
290
|
+
self.dark_highlighter: PygmentsBridge | None
|
|
328
291
|
if dark_style is not None:
|
|
329
292
|
self.dark_highlighter = PygmentsBridge('html', dark_style)
|
|
330
293
|
self.app.add_css_file('pygments_dark.css',
|
|
@@ -333,8 +296,14 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
333
296
|
else:
|
|
334
297
|
self.dark_highlighter = None
|
|
335
298
|
|
|
299
|
+
@property
|
|
300
|
+
def css_files(self) -> list[_CascadingStyleSheet]:
|
|
301
|
+
_deprecation_warning(__name__, f'{self.__class__.__name__}.css_files', '',
|
|
302
|
+
remove=(9, 0))
|
|
303
|
+
return self._css_files
|
|
304
|
+
|
|
336
305
|
def init_css_files(self) -> None:
|
|
337
|
-
self.
|
|
306
|
+
self._css_files = []
|
|
338
307
|
self.add_css_file('pygments.css', priority=200)
|
|
339
308
|
|
|
340
309
|
for filename in self._get_style_filenames():
|
|
@@ -351,21 +320,26 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
351
320
|
if '://' not in filename:
|
|
352
321
|
filename = posixpath.join('_static', filename)
|
|
353
322
|
|
|
354
|
-
self.
|
|
323
|
+
self._css_files.append(_CascadingStyleSheet(filename, **kwargs))
|
|
324
|
+
|
|
325
|
+
@property
|
|
326
|
+
def script_files(self) -> list[_JavaScript]:
|
|
327
|
+
_deprecation_warning(__name__, f'{self.__class__.__name__}.script_files', '',
|
|
328
|
+
remove=(9, 0))
|
|
329
|
+
return self._js_files
|
|
355
330
|
|
|
356
331
|
def init_js_files(self) -> None:
|
|
357
|
-
self.
|
|
358
|
-
self.add_js_file('documentation_options.js',
|
|
359
|
-
data_url_root='', priority=200)
|
|
332
|
+
self._js_files = []
|
|
333
|
+
self.add_js_file('documentation_options.js', priority=200)
|
|
360
334
|
self.add_js_file('doctools.js', priority=200)
|
|
361
335
|
self.add_js_file('sphinx_highlight.js', priority=200)
|
|
362
336
|
|
|
363
337
|
for filename, attrs in self.app.registry.js_files:
|
|
364
|
-
self.add_js_file(filename, **attrs)
|
|
338
|
+
self.add_js_file(filename or '', **attrs)
|
|
365
339
|
|
|
366
340
|
for filename, attrs in self.get_builder_config('js_files', 'html'):
|
|
367
341
|
attrs.setdefault('priority', 800) # User's JSs are loaded after extensions'
|
|
368
|
-
self.add_js_file(filename, **attrs)
|
|
342
|
+
self.add_js_file(filename or '', **attrs)
|
|
369
343
|
|
|
370
344
|
if self._get_translations_js():
|
|
371
345
|
self.add_js_file('translations.js')
|
|
@@ -374,10 +348,10 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
374
348
|
if filename and '://' not in filename:
|
|
375
349
|
filename = posixpath.join('_static', filename)
|
|
376
350
|
|
|
377
|
-
self.
|
|
351
|
+
self._js_files.append(_JavaScript(filename, **kwargs))
|
|
378
352
|
|
|
379
353
|
@property
|
|
380
|
-
def math_renderer_name(self) -> str:
|
|
354
|
+
def math_renderer_name(self) -> str | None:
|
|
381
355
|
name = self.get_builder_config('math_renderer', 'html')
|
|
382
356
|
if name is not None:
|
|
383
357
|
# use given name
|
|
@@ -426,17 +400,15 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
426
400
|
except Exception:
|
|
427
401
|
targetmtime = 0
|
|
428
402
|
try:
|
|
429
|
-
srcmtime = max(path.getmtime(self.env.doc2path(docname)),
|
|
430
|
-
template_mtime)
|
|
403
|
+
srcmtime = max(path.getmtime(self.env.doc2path(docname)), template_mtime)
|
|
431
404
|
if srcmtime > targetmtime:
|
|
432
405
|
logger.debug(
|
|
433
406
|
'[build target] targetname %r(%s), template(%s), docname %r(%s)',
|
|
434
407
|
targetname,
|
|
435
|
-
|
|
436
|
-
|
|
408
|
+
_format_modified_time(targetmtime),
|
|
409
|
+
_format_modified_time(template_mtime),
|
|
437
410
|
docname,
|
|
438
|
-
|
|
439
|
-
tz=timezone.utc),
|
|
411
|
+
_format_modified_time(path.getmtime(self.env.doc2path(docname))),
|
|
440
412
|
)
|
|
441
413
|
yield docname
|
|
442
414
|
except OSError:
|
|
@@ -455,7 +427,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
455
427
|
doc.append(node)
|
|
456
428
|
self._publisher.set_source(doc)
|
|
457
429
|
self._publisher.publish()
|
|
458
|
-
return self._publisher.writer.parts
|
|
430
|
+
return self._publisher.writer.parts # type: ignore[union-attr]
|
|
459
431
|
|
|
460
432
|
def prepare_writing(self, docnames: set[str]) -> None:
|
|
461
433
|
# create the search indexer
|
|
@@ -498,6 +470,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
498
470
|
|
|
499
471
|
# format the "last updated on" string, only once is enough since it
|
|
500
472
|
# typically doesn't include the time of day
|
|
473
|
+
self.last_updated: str | None
|
|
501
474
|
lufmt = self.config.html_last_updated_fmt
|
|
502
475
|
if lufmt is not None:
|
|
503
476
|
self.last_updated = format_date(lufmt or _('%b %d, %Y'),
|
|
@@ -526,9 +499,15 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
526
499
|
rellinks.append((indexname, indexcls.localname,
|
|
527
500
|
'', indexcls.shortname))
|
|
528
501
|
|
|
529
|
-
#
|
|
530
|
-
|
|
531
|
-
|
|
502
|
+
# add assets registered after ``Builder.init()``.
|
|
503
|
+
for css_filename, attrs in self.app.registry.css_files:
|
|
504
|
+
self.add_css_file(css_filename, **attrs)
|
|
505
|
+
for js_filename, attrs in self.app.registry.js_files:
|
|
506
|
+
self.add_js_file(js_filename or '', **attrs)
|
|
507
|
+
|
|
508
|
+
# back up _css_files and _js_files to allow adding CSS/JS files to a specific page.
|
|
509
|
+
self._orig_css_files = list(dict.fromkeys(self._css_files))
|
|
510
|
+
self._orig_js_files = list(dict.fromkeys(self._js_files))
|
|
532
511
|
styles = list(self._get_style_filenames())
|
|
533
512
|
|
|
534
513
|
self.globalcontext = {
|
|
@@ -551,9 +530,9 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
551
530
|
'sourcelink_suffix': self.config.html_sourcelink_suffix,
|
|
552
531
|
'file_suffix': self.out_suffix,
|
|
553
532
|
'link_suffix': self.link_suffix,
|
|
554
|
-
'script_files': self.
|
|
533
|
+
'script_files': self._js_files,
|
|
555
534
|
'language': convert_locale_to_language_tag(self.config.language),
|
|
556
|
-
'css_files': self.
|
|
535
|
+
'css_files': self._css_files,
|
|
557
536
|
'sphinx_version': __display_version__,
|
|
558
537
|
'sphinx_version_tuple': sphinx_version,
|
|
559
538
|
'docutils_version_info': docutils.__version_info__[:5],
|
|
@@ -600,12 +579,11 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
600
579
|
# that gracefully
|
|
601
580
|
prev = None
|
|
602
581
|
while related and related[0]:
|
|
603
|
-
|
|
582
|
+
with contextlib.suppress(KeyError):
|
|
604
583
|
parents.append(
|
|
605
584
|
{'link': self.get_relative_uri(docname, related[0]),
|
|
606
585
|
'title': self.render_partial(titles[related[0]])['title']})
|
|
607
|
-
|
|
608
|
-
pass
|
|
586
|
+
|
|
609
587
|
related = self.relations.get(related[0])
|
|
610
588
|
if parents:
|
|
611
589
|
# remove link to the master file; we have a generic
|
|
@@ -632,7 +610,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
632
610
|
meta = self.env.metadata.get(docname)
|
|
633
611
|
|
|
634
612
|
# local TOC and global TOC tree
|
|
635
|
-
self_toc =
|
|
613
|
+
self_toc = document_toc(self.env, docname, self.tags)
|
|
636
614
|
toc = self.render_partial(self_toc)['fragment']
|
|
637
615
|
|
|
638
616
|
return {
|
|
@@ -780,7 +758,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
780
758
|
|
|
781
759
|
def copy_download_files(self) -> None:
|
|
782
760
|
def to_relpath(f: str) -> str:
|
|
783
|
-
return relative_path(self.srcdir, f)
|
|
761
|
+
return relative_path(self.srcdir, f) # type: ignore[arg-type]
|
|
784
762
|
|
|
785
763
|
# copy downloadable files
|
|
786
764
|
if self.env.dlfiles:
|
|
@@ -820,9 +798,9 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
820
798
|
for jsfile in self.indexer.get_js_stemmer_rawcodes():
|
|
821
799
|
copyfile(jsfile, path.join(self.outdir, '_static', path.basename(jsfile)))
|
|
822
800
|
else:
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
801
|
+
if js_stemmer_rawcode := self.indexer.get_js_stemmer_rawcode():
|
|
802
|
+
copyfile(js_stemmer_rawcode,
|
|
803
|
+
path.join(self.outdir, '_static', '_stemmer.js'))
|
|
826
804
|
|
|
827
805
|
def copy_theme_static_files(self, context: dict[str, Any]) -> None:
|
|
828
806
|
def onerror(filename: str, error: Exception) -> None:
|
|
@@ -930,6 +908,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
930
908
|
reference.append(node)
|
|
931
909
|
|
|
932
910
|
def load_indexer(self, docnames: Iterable[str]) -> None:
|
|
911
|
+
assert self.indexer is not None
|
|
933
912
|
keep = set(self.env.all_docs) - set(docnames)
|
|
934
913
|
try:
|
|
935
914
|
searchindexfn = path.join(self.outdir, self.searchindex_filename)
|
|
@@ -950,7 +929,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
950
929
|
def index_page(self, pagename: str, doctree: nodes.document, title: str) -> None:
|
|
951
930
|
# only index pages with title
|
|
952
931
|
if self.indexer is not None and title:
|
|
953
|
-
filename = self.env.doc2path(pagename, base=
|
|
932
|
+
filename = self.env.doc2path(pagename, base=False)
|
|
954
933
|
metadata = self.env.metadata.get(pagename, {})
|
|
955
934
|
if 'nosearch' in metadata:
|
|
956
935
|
self.indexer.feed(pagename, filename, '', new_document(''))
|
|
@@ -962,8 +941,8 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
962
941
|
kwargs['includehidden'] = False
|
|
963
942
|
if kwargs.get('maxdepth') == '':
|
|
964
943
|
kwargs.pop('maxdepth')
|
|
965
|
-
|
|
966
|
-
|
|
944
|
+
toctree = global_toctree_for_doc(self.env, docname, self, collapse=collapse, **kwargs)
|
|
945
|
+
return self.render_partial(toctree)['fragment']
|
|
967
946
|
|
|
968
947
|
def get_outfilename(self, pagename: str) -> str:
|
|
969
948
|
return path.join(self.outdir, os_path(pagename) + self.out_suffix)
|
|
@@ -1068,9 +1047,53 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1068
1047
|
self.add_sidebars(pagename, ctx)
|
|
1069
1048
|
ctx.update(addctx)
|
|
1070
1049
|
|
|
1071
|
-
#
|
|
1072
|
-
|
|
1073
|
-
|
|
1050
|
+
# 'blah.html' should have content_root = './' not ''.
|
|
1051
|
+
ctx['content_root'] = (f'..{SEP}' * default_baseuri.count(SEP)) or f'.{SEP}'
|
|
1052
|
+
|
|
1053
|
+
outdir = self.app.outdir
|
|
1054
|
+
|
|
1055
|
+
def css_tag(css: _CascadingStyleSheet) -> str:
|
|
1056
|
+
attrs = []
|
|
1057
|
+
for key, value in css.attributes.items():
|
|
1058
|
+
if value is not None:
|
|
1059
|
+
attrs.append(f'{key}="{html.escape(value, quote=True)}"')
|
|
1060
|
+
uri = pathto(os.fspath(css.filename), resource=True)
|
|
1061
|
+
if checksum := _file_checksum(outdir, css.filename):
|
|
1062
|
+
uri += f'?v={checksum}'
|
|
1063
|
+
return f'<link {" ".join(sorted(attrs))} href="{uri}" />'
|
|
1064
|
+
|
|
1065
|
+
ctx['css_tag'] = css_tag
|
|
1066
|
+
|
|
1067
|
+
def js_tag(js: _JavaScript | str) -> str:
|
|
1068
|
+
if not isinstance(js, _JavaScript):
|
|
1069
|
+
# str value (old styled)
|
|
1070
|
+
return f'<script src="{pathto(js, resource=True)}"></script>'
|
|
1071
|
+
|
|
1072
|
+
attrs = []
|
|
1073
|
+
body = js.attributes.get('body', '')
|
|
1074
|
+
for key, value in js.attributes.items():
|
|
1075
|
+
if key == 'body':
|
|
1076
|
+
continue
|
|
1077
|
+
if value is not None:
|
|
1078
|
+
attrs.append(f'{key}="{html.escape(value, quote=True)}"')
|
|
1079
|
+
|
|
1080
|
+
if not js.filename:
|
|
1081
|
+
if attrs:
|
|
1082
|
+
return f'<script {" ".join(sorted(attrs))}>{body}</script>'
|
|
1083
|
+
return f'<script>{body}</script>'
|
|
1084
|
+
|
|
1085
|
+
uri = pathto(os.fspath(js.filename), resource=True)
|
|
1086
|
+
if checksum := _file_checksum(outdir, js.filename):
|
|
1087
|
+
uri += f'?v={checksum}'
|
|
1088
|
+
if attrs:
|
|
1089
|
+
return f'<script {" ".join(sorted(attrs))} src="{uri}"></script>'
|
|
1090
|
+
return f'<script src="{uri}"></script>'
|
|
1091
|
+
|
|
1092
|
+
ctx['js_tag'] = js_tag
|
|
1093
|
+
|
|
1094
|
+
# revert _css_files and _js_files
|
|
1095
|
+
self._css_files[:] = self._orig_css_files
|
|
1096
|
+
self._js_files[:] = self._orig_js_files
|
|
1074
1097
|
|
|
1075
1098
|
self.update_page_context(pagename, templatename, ctx, event_arg)
|
|
1076
1099
|
newtmpl = self.app.emit_firstresult('html-page-context', pagename,
|
|
@@ -1079,7 +1102,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1079
1102
|
templatename = newtmpl
|
|
1080
1103
|
|
|
1081
1104
|
# sort JS/CSS before rendering HTML
|
|
1082
|
-
try:
|
|
1105
|
+
try: # NoQA: SIM105
|
|
1083
1106
|
# Convert script_files to list to support non-list script_files (refs: #8889)
|
|
1084
1107
|
ctx['script_files'] = sorted(ctx['script_files'], key=lambda js: js.priority)
|
|
1085
1108
|
except AttributeError:
|
|
@@ -1089,10 +1112,8 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1089
1112
|
# Note: priority sorting feature will not work in this case.
|
|
1090
1113
|
pass
|
|
1091
1114
|
|
|
1092
|
-
|
|
1115
|
+
with contextlib.suppress(AttributeError):
|
|
1093
1116
|
ctx['css_files'] = sorted(ctx['css_files'], key=lambda css: css.priority)
|
|
1094
|
-
except AttributeError:
|
|
1095
|
-
pass
|
|
1096
1117
|
|
|
1097
1118
|
try:
|
|
1098
1119
|
output = self.templates.render(templatename, ctx)
|
|
@@ -1127,8 +1148,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1127
1148
|
pass
|
|
1128
1149
|
|
|
1129
1150
|
def handle_finish(self) -> None:
|
|
1130
|
-
|
|
1131
|
-
self.finish_tasks.add_task(self.dump_search_index)
|
|
1151
|
+
self.finish_tasks.add_task(self.dump_search_index)
|
|
1132
1152
|
self.finish_tasks.add_task(self.dump_inventory)
|
|
1133
1153
|
|
|
1134
1154
|
@progress_message(__('dumping object inventory'))
|
|
@@ -1136,6 +1156,9 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1136
1156
|
InventoryFile.dump(path.join(self.outdir, INVENTORY_FILENAME), self.env, self)
|
|
1137
1157
|
|
|
1138
1158
|
def dump_search_index(self) -> None:
|
|
1159
|
+
if self.indexer is None:
|
|
1160
|
+
return
|
|
1161
|
+
|
|
1139
1162
|
with progress_message(__('dumping search index in %s') % self.indexer.label()):
|
|
1140
1163
|
self.indexer.prune(self.env.all_docs)
|
|
1141
1164
|
searchindexfn = path.join(self.outdir, self.searchindex_filename)
|
|
@@ -1164,7 +1187,13 @@ def convert_html_css_files(app: Sphinx, config: Config) -> None:
|
|
|
1164
1187
|
logger.warning(__('invalid css_file: %r, ignored'), entry)
|
|
1165
1188
|
continue
|
|
1166
1189
|
|
|
1167
|
-
config.html_css_files = html_css_files # type: ignore
|
|
1190
|
+
config.html_css_files = html_css_files # type: ignore[attr-defined]
|
|
1191
|
+
|
|
1192
|
+
|
|
1193
|
+
def _format_modified_time(timestamp: float) -> str:
|
|
1194
|
+
"""Return an RFC 3339 formatted string representing the given timestamp."""
|
|
1195
|
+
seconds, fraction = divmod(timestamp, 1)
|
|
1196
|
+
return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(seconds)) + f'.{fraction:.3f}'
|
|
1168
1197
|
|
|
1169
1198
|
|
|
1170
1199
|
def convert_html_js_files(app: Sphinx, config: Config) -> None:
|
|
@@ -1181,89 +1210,13 @@ def convert_html_js_files(app: Sphinx, config: Config) -> None:
|
|
|
1181
1210
|
logger.warning(__('invalid js_file: %r, ignored'), entry)
|
|
1182
1211
|
continue
|
|
1183
1212
|
|
|
1184
|
-
config.html_js_files = html_js_files # type: ignore
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
def setup_css_tag_helper(app: Sphinx, pagename: str, templatename: str,
|
|
1188
|
-
context: dict, doctree: Node) -> None:
|
|
1189
|
-
"""Set up css_tag() template helper.
|
|
1190
|
-
|
|
1191
|
-
.. note:: This set up function is added to keep compatibility with webhelper.
|
|
1192
|
-
"""
|
|
1193
|
-
pathto = context.get('pathto')
|
|
1194
|
-
|
|
1195
|
-
def css_tag(css: Stylesheet) -> str:
|
|
1196
|
-
attrs = []
|
|
1197
|
-
for key in sorted(css.attributes):
|
|
1198
|
-
value = css.attributes[key]
|
|
1199
|
-
if value is not None:
|
|
1200
|
-
attrs.append(f'{key}="{html.escape(value, True)}"')
|
|
1201
|
-
uri = pathto(css.filename, resource=True)
|
|
1202
|
-
if checksum := _file_checksum(app.outdir, css.filename):
|
|
1203
|
-
uri += f'?v={checksum}'
|
|
1204
|
-
attrs.append(f'href="{uri}"')
|
|
1205
|
-
return f'<link {" ".join(attrs)} />'
|
|
1206
|
-
|
|
1207
|
-
context['css_tag'] = css_tag
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
def setup_js_tag_helper(app: Sphinx, pagename: str, templatename: str,
|
|
1211
|
-
context: dict, doctree: Node) -> None:
|
|
1212
|
-
"""Set up js_tag() template helper.
|
|
1213
|
-
|
|
1214
|
-
.. note:: This set up function is added to keep compatibility with webhelper.
|
|
1215
|
-
"""
|
|
1216
|
-
pathto = context.get('pathto')
|
|
1217
|
-
|
|
1218
|
-
def js_tag(js: JavaScript) -> str:
|
|
1219
|
-
attrs = []
|
|
1220
|
-
body = ''
|
|
1221
|
-
if isinstance(js, JavaScript):
|
|
1222
|
-
for key in sorted(js.attributes):
|
|
1223
|
-
value = js.attributes[key]
|
|
1224
|
-
if value is not None:
|
|
1225
|
-
if key == 'body':
|
|
1226
|
-
body = value
|
|
1227
|
-
elif key == 'data_url_root':
|
|
1228
|
-
attrs.append(f'data-url_root="{pathto("", resource=True)}"')
|
|
1229
|
-
else:
|
|
1230
|
-
attrs.append(f'{key}="{html.escape(value, True)}"')
|
|
1231
|
-
if js.filename:
|
|
1232
|
-
uri = pathto(js.filename, resource=True)
|
|
1233
|
-
if checksum := _file_checksum(app.outdir, js.filename):
|
|
1234
|
-
uri += f'?v={checksum}'
|
|
1235
|
-
attrs.append(f'src="{uri}"')
|
|
1236
|
-
else:
|
|
1237
|
-
# str value (old styled)
|
|
1238
|
-
attrs.append(f'src="{pathto(js, resource=True)}"')
|
|
1239
|
-
|
|
1240
|
-
if attrs:
|
|
1241
|
-
return f'<script {" ".join(attrs)}>{body}</script>'
|
|
1242
|
-
else:
|
|
1243
|
-
return f'<script>{body}</script>'
|
|
1244
|
-
|
|
1245
|
-
context['js_tag'] = js_tag
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
def _file_checksum(outdir: str, filename: str) -> str:
|
|
1249
|
-
# Don't generate checksums for HTTP URIs
|
|
1250
|
-
if '://' in filename:
|
|
1251
|
-
return ''
|
|
1252
|
-
try:
|
|
1253
|
-
# Ensure universal newline mode is used to avoid checksum differences
|
|
1254
|
-
with open(path.join(outdir, filename), encoding='utf-8') as f:
|
|
1255
|
-
content = f.read().encode(encoding='utf-8')
|
|
1256
|
-
except FileNotFoundError:
|
|
1257
|
-
return ''
|
|
1258
|
-
if not content:
|
|
1259
|
-
return ''
|
|
1260
|
-
return f'{zlib.crc32(content):08x}'
|
|
1213
|
+
config.html_js_files = html_js_files # type: ignore[attr-defined]
|
|
1261
1214
|
|
|
1262
1215
|
|
|
1263
1216
|
def setup_resource_paths(app: Sphinx, pagename: str, templatename: str,
|
|
1264
1217
|
context: dict, doctree: Node) -> None:
|
|
1265
1218
|
"""Set up relative resource paths."""
|
|
1266
|
-
pathto = context
|
|
1219
|
+
pathto = context['pathto']
|
|
1267
1220
|
|
|
1268
1221
|
# favicon_url
|
|
1269
1222
|
favicon_url = context.get('favicon_url')
|
|
@@ -1280,7 +1233,7 @@ def validate_math_renderer(app: Sphinx) -> None:
|
|
|
1280
1233
|
if app.builder.format != 'html':
|
|
1281
1234
|
return
|
|
1282
1235
|
|
|
1283
|
-
name = app.builder.math_renderer_name # type: ignore
|
|
1236
|
+
name = app.builder.math_renderer_name # type: ignore[attr-defined]
|
|
1284
1237
|
if name is None:
|
|
1285
1238
|
raise ConfigError(__('Many math_renderers are registered. '
|
|
1286
1239
|
'But no math_renderer is selected.'))
|
|
@@ -1296,7 +1249,7 @@ def validate_html_extra_path(app: Sphinx, config: Config) -> None:
|
|
|
1296
1249
|
logger.warning(__('html_extra_path entry %r does not exist'), entry)
|
|
1297
1250
|
config.html_extra_path.remove(entry)
|
|
1298
1251
|
elif (path.splitdrive(app.outdir)[0] == path.splitdrive(extra_path)[0] and
|
|
1299
|
-
path.commonpath(
|
|
1252
|
+
path.commonpath((app.outdir, extra_path)) == path.normpath(app.outdir)):
|
|
1300
1253
|
logger.warning(__('html_extra_path entry %r is placed inside outdir'), entry)
|
|
1301
1254
|
config.html_extra_path.remove(entry)
|
|
1302
1255
|
|
|
@@ -1309,7 +1262,7 @@ def validate_html_static_path(app: Sphinx, config: Config) -> None:
|
|
|
1309
1262
|
logger.warning(__('html_static_path entry %r does not exist'), entry)
|
|
1310
1263
|
config.html_static_path.remove(entry)
|
|
1311
1264
|
elif (path.splitdrive(app.outdir)[0] == path.splitdrive(static_path)[0] and
|
|
1312
|
-
path.commonpath(
|
|
1265
|
+
path.commonpath((app.outdir, static_path)) == path.normpath(app.outdir)):
|
|
1313
1266
|
logger.warning(__('html_static_path entry %r is placed inside outdir'), entry)
|
|
1314
1267
|
config.html_static_path.remove(entry)
|
|
1315
1268
|
|
|
@@ -1320,7 +1273,7 @@ def validate_html_logo(app: Sphinx, config: Config) -> None:
|
|
|
1320
1273
|
not path.isfile(path.join(app.confdir, config.html_logo)) and
|
|
1321
1274
|
not isurl(config.html_logo)):
|
|
1322
1275
|
logger.warning(__('logo file %r does not exist'), config.html_logo)
|
|
1323
|
-
config.html_logo = None # type: ignore
|
|
1276
|
+
config.html_logo = None # type: ignore[attr-defined]
|
|
1324
1277
|
|
|
1325
1278
|
|
|
1326
1279
|
def validate_html_favicon(app: Sphinx, config: Config) -> None:
|
|
@@ -1329,7 +1282,7 @@ def validate_html_favicon(app: Sphinx, config: Config) -> None:
|
|
|
1329
1282
|
not path.isfile(path.join(app.confdir, config.html_favicon)) and
|
|
1330
1283
|
not isurl(config.html_favicon)):
|
|
1331
1284
|
logger.warning(__('favicon file %r does not exist'), config.html_favicon)
|
|
1332
|
-
config.html_favicon = None # type: ignore
|
|
1285
|
+
config.html_favicon = None # type: ignore[attr-defined]
|
|
1333
1286
|
|
|
1334
1287
|
|
|
1335
1288
|
def error_on_html_4(_app: Sphinx, config: Config) -> None:
|
|
@@ -1383,7 +1336,7 @@ def setup(app: Sphinx) -> dict[str, Any]:
|
|
|
1383
1336
|
app.add_config_value('html_secnumber_suffix', '. ', 'html')
|
|
1384
1337
|
app.add_config_value('html_search_language', None, 'html', [str])
|
|
1385
1338
|
app.add_config_value('html_search_options', {}, 'html')
|
|
1386
|
-
app.add_config_value('html_search_scorer', '',
|
|
1339
|
+
app.add_config_value('html_search_scorer', '', '')
|
|
1387
1340
|
app.add_config_value('html_scaled_image_link', True, 'html')
|
|
1388
1341
|
app.add_config_value('html_baseurl', '', 'html')
|
|
1389
1342
|
app.add_config_value('html_codeblock_linenos_style', 'inline', 'html', # RemovedInSphinx70Warning # noqa: E501
|
|
@@ -1404,8 +1357,6 @@ def setup(app: Sphinx) -> dict[str, Any]:
|
|
|
1404
1357
|
app.connect('config-inited', validate_html_favicon, priority=800)
|
|
1405
1358
|
app.connect('config-inited', error_on_html_4, priority=800)
|
|
1406
1359
|
app.connect('builder-inited', validate_math_renderer)
|
|
1407
|
-
app.connect('html-page-context', setup_css_tag_helper)
|
|
1408
|
-
app.connect('html-page-context', setup_js_tag_helper)
|
|
1409
1360
|
app.connect('html-page-context', setup_resource_paths)
|
|
1410
1361
|
|
|
1411
1362
|
# load default math renderer
|
|
@@ -1419,3 +1370,22 @@ def setup(app: Sphinx) -> dict[str, Any]:
|
|
|
1419
1370
|
'parallel_read_safe': True,
|
|
1420
1371
|
'parallel_write_safe': True,
|
|
1421
1372
|
}
|
|
1373
|
+
|
|
1374
|
+
|
|
1375
|
+
# deprecated name -> (object to return, canonical path or empty string)
|
|
1376
|
+
_DEPRECATED_OBJECTS = {
|
|
1377
|
+
'Stylesheet': (_CascadingStyleSheet, 'sphinx.builders.html._assets._CascadingStyleSheet', (9, 0)), # NoQA: E501
|
|
1378
|
+
'JavaScript': (_JavaScript, 'sphinx.builders.html._assets._JavaScript', (9, 0)),
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
|
|
1382
|
+
def __getattr__(name):
|
|
1383
|
+
if name not in _DEPRECATED_OBJECTS:
|
|
1384
|
+
msg = f'module {__name__!r} has no attribute {name!r}'
|
|
1385
|
+
raise AttributeError(msg)
|
|
1386
|
+
|
|
1387
|
+
from sphinx.deprecation import _deprecation_warning
|
|
1388
|
+
|
|
1389
|
+
deprecated_object, canonical_name, remove = _DEPRECATED_OBJECTS[name]
|
|
1390
|
+
_deprecation_warning(__name__, name, canonical_name, remove=remove)
|
|
1391
|
+
return deprecated_object
|