diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:20:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:20:58 +0000 |
commit | 5bb0bb4be543fd5eca41673696a62ed80d493591 (patch) | |
tree | ad2c464f140e86c7f178a6276d7ea4a93e3e6c92 /sphinx/builders/html | |
parent | Adding upstream version 7.2.6. (diff) | |
download | sphinx-upstream.tar.xz sphinx-upstream.zip |
Adding upstream version 7.3.7.upstream/7.3.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sphinx/builders/html')
-rw-r--r-- | sphinx/builders/html/__init__.py | 184 | ||||
-rw-r--r-- | sphinx/builders/html/_assets.py | 32 | ||||
-rw-r--r-- | sphinx/builders/html/transforms.py | 6 |
3 files changed, 108 insertions, 114 deletions
diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 85067be..75b0a39 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -10,6 +10,7 @@ import posixpath import re import sys import time +import types import warnings from os import path from typing import IO, TYPE_CHECKING, Any @@ -49,13 +50,16 @@ from sphinx.writers.html import HTMLWriter from sphinx.writers.html5 import HTML5Translator if TYPE_CHECKING: - from collections.abc import Iterable, Iterator, Sequence + from collections.abc import Iterable, Iterator, Set from docutils.nodes import Node + from docutils.readers import Reader from sphinx.application import Sphinx + from sphinx.config import _ConfigRebuild from sphinx.environment import BuildEnvironment from sphinx.util.tags import Tags + from sphinx.util.typing import ExtensionMetadata #: the filename for the inventory of objects INVENTORY_FILENAME = 'objects.inv' @@ -75,16 +79,20 @@ DOMAIN_INDEX_TYPE = tuple[ ] -def get_stable_hash(obj: Any) -> str: - """ - Return a stable hash for a Python data structure. We can't just use - the md5 of str(obj) since for example dictionary items are enumerated - in unpredictable order due to hash randomization in newer Pythons. +def _stable_hash(obj: Any) -> str: + """Return a stable hash for a Python data structure. + + We can't just use the md5 of str(obj) as the order of collections + may be random. """ if isinstance(obj, dict): - return get_stable_hash(list(obj.items())) - elif isinstance(obj, (list, tuple)): - obj = sorted(get_stable_hash(o) for o in obj) + obj = sorted(map(_stable_hash, obj.items())) + if isinstance(obj, (list, tuple, set, frozenset)): + obj = sorted(map(_stable_hash, obj)) + elif isinstance(obj, (type, types.FunctionType)): + # The default repr() of functions includes the ID, which is not ideal. + # We use the fully qualified name instead. + obj = f'{obj.__module__}.{obj.__qualname__}' return hashlib.md5(str(obj).encode(), usedforsecurity=False).hexdigest() @@ -107,7 +115,7 @@ class BuildInfo: """ @classmethod - def load(cls, f: IO) -> BuildInfo: + def load(cls: type[BuildInfo], f: IO) -> BuildInfo: try: lines = f.readlines() assert lines[0].rstrip() == '# Sphinx build info version 1' @@ -125,17 +133,17 @@ class BuildInfo: self, config: Config | None = None, tags: Tags | None = None, - config_categories: Sequence[str] = (), + config_categories: Set[_ConfigRebuild] = frozenset(), ) -> None: self.config_hash = '' self.tags_hash = '' if config: values = {c.name: c.value for c in config.filter(config_categories)} - self.config_hash = get_stable_hash(values) + self.config_hash = _stable_hash(values) if tags: - self.tags_hash = get_stable_hash(sorted(tags)) + self.tags_hash = _stable_hash(sorted(tags)) def __eq__(self, other: BuildInfo) -> bool: # type: ignore[override] return (self.config_hash == other.config_hash and @@ -154,6 +162,7 @@ class StandaloneHTMLBuilder(Builder): """ Builds standalone HTML docs. """ + name = 'html' format = 'html' epilog = __('The HTML pages are in %(outdir)s.') @@ -192,7 +201,7 @@ class StandaloneHTMLBuilder(Builder): self._js_files: list[_JavaScript] = [] # Cached Publisher for writing doctrees to HTML - reader = docutils.readers.doctree.Reader(parser_name='restructuredtext') + reader: Reader = docutils.readers.doctree.Reader(parser_name='restructuredtext') pub = Publisher( reader=reader, parser=reader.parser, @@ -234,7 +243,7 @@ class StandaloneHTMLBuilder(Builder): self.use_index = self.get_builder_config('use_index', 'html') def create_build_info(self) -> BuildInfo: - return BuildInfo(self.config, self.tags, ['html']) + return BuildInfo(self.config, self.tags, frozenset({'html'})) def _get_translations_js(self) -> str: candidates = [path.join(dir, self.config.language, @@ -256,8 +265,7 @@ class StandaloneHTMLBuilder(Builder): elif self.config.html_style is not None: yield from self.config.html_style elif self.theme: - stylesheet = self.theme.get_config('theme', 'stylesheet') - yield from map(str.strip, stylesheet.split(',')) + yield from self.theme.stylesheets else: yield 'default.css' @@ -266,9 +274,9 @@ class StandaloneHTMLBuilder(Builder): def init_templates(self) -> None: theme_factory = HTMLThemeFactory(self.app) - themename, themeoptions = self.get_theme_config() - self.theme = theme_factory.create(themename) - self.theme_options = themeoptions.copy() + theme_name, theme_options = self.get_theme_config() + self.theme = theme_factory.create(theme_name) + self.theme_options = theme_options self.create_template_bridge() self.templates.init(self, self.theme) @@ -277,13 +285,15 @@ class StandaloneHTMLBuilder(Builder): if self.config.pygments_style is not None: style = self.config.pygments_style elif self.theme: - style = self.theme.get_config('theme', 'pygments_style', 'none') + # From the ``pygments_style`` theme setting + style = self.theme.pygments_style_default or 'none' else: style = 'sphinx' self.highlighter = PygmentsBridge('html', style) if self.theme: - dark_style = self.theme.get_config('theme', 'pygments_dark_style', None) + # From the ``pygments_dark_style`` theme setting + dark_style = self.theme.pygments_style_dark else: dark_style = None @@ -298,8 +308,7 @@ class StandaloneHTMLBuilder(Builder): @property def css_files(self) -> list[_CascadingStyleSheet]: - _deprecation_warning(__name__, f'{self.__class__.__name__}.css_files', '', - remove=(9, 0)) + _deprecation_warning(__name__, f'{self.__class__.__name__}.css_files', remove=(9, 0)) return self._css_files def init_css_files(self) -> None: @@ -325,8 +334,8 @@ class StandaloneHTMLBuilder(Builder): @property def script_files(self) -> list[_JavaScript]: - _deprecation_warning(__name__, f'{self.__class__.__name__}.script_files', '', - remove=(9, 0)) + canonical_name = f'{self.__class__.__name__}.script_files' + _deprecation_warning(__name__, canonical_name, remove=(9, 0)) return self._js_files def init_js_files(self) -> None: @@ -429,7 +438,7 @@ class StandaloneHTMLBuilder(Builder): doc.append(node) self._publisher.set_source(doc) self._publisher.publish() - return self._publisher.writer.parts # type: ignore[union-attr] + return self._publisher.writer.parts def prepare_writing(self, docnames: set[str]) -> None: # create the search indexer @@ -547,10 +556,11 @@ class StandaloneHTMLBuilder(Builder): 'html5_doctype': True, } if self.theme: - self.globalcontext.update( - ('theme_' + key, val) for (key, val) in - self.theme.get_options(self.theme_options).items()) - self.globalcontext.update(self.config.html_context) + self.globalcontext |= { + f'theme_{key}': val for key, val in + self.theme.get_options(self.theme_options).items() + } + self.globalcontext |= self.config.html_context def get_doc_context(self, docname: str, body: str, metatags: str) -> dict[str, Any]: """Collect items for the template context of a page.""" @@ -708,10 +718,8 @@ class StandaloneHTMLBuilder(Builder): # the total count of lines for each index letter, used to distribute # the entries into two columns genindex = IndexEntries(self.env).create_index(self) - indexcounts = [] - for _k, entries in genindex: - indexcounts.append(sum(1 + len(subitems) - for _, (_, subitems, _) in entries)) + indexcounts = [sum(1 + len(subitems) for _, (_, subitems, _) in entries) + for _k, entries in genindex] genindexcontext = { 'genindexentries': genindex, @@ -760,7 +768,7 @@ class StandaloneHTMLBuilder(Builder): def copy_download_files(self) -> None: def to_relpath(f: str) -> str: - return relative_path(self.srcdir, f) # type: ignore[arg-type] + return relative_path(self.srcdir, f) # copy downloadable files if self.env.dlfiles: @@ -777,7 +785,7 @@ class StandaloneHTMLBuilder(Builder): path.join(self.srcdir, src), err) def create_pygments_style_file(self) -> None: - """create a style file for pygments.""" + """Create a style file for pygments.""" with open(path.join(self.outdir, '_static', 'pygments.css'), 'w', encoding="utf-8") as f: f.write(self.highlighter.get_stylesheet()) @@ -810,7 +818,7 @@ class StandaloneHTMLBuilder(Builder): filename, error) if self.theme: - for entry in self.theme.get_theme_dirs()[::-1]: + for entry in reversed(self.theme.get_theme_dirs()): copy_asset(path.join(entry, 'static'), path.join(self.outdir, '_static'), excluded=DOTFILES, context=context, @@ -821,7 +829,7 @@ class StandaloneHTMLBuilder(Builder): logger.warning(__('Failed to copy a file in html_static_file: %s: %r'), filename, error) - excluded = Matcher(self.config.exclude_patterns + ["**/.*"]) + excluded = Matcher([*self.config.exclude_patterns, '**/.*']) for entry in self.config.html_static_path: copy_asset(path.join(self.confdir, entry), path.join(self.outdir, '_static'), @@ -858,7 +866,7 @@ class StandaloneHTMLBuilder(Builder): logger.warning(__('cannot copy static file %r'), err) def copy_extra_files(self) -> None: - """copy html_extra_path files.""" + """Copy html_extra_path files.""" try: with progress_message(__('copying extra files')): excluded = Matcher(self.config.exclude_patterns) @@ -878,7 +886,7 @@ class StandaloneHTMLBuilder(Builder): def cleanup(self) -> None: # clean up theme stuff if self.theme: - self.theme.cleanup() + self.theme._cleanup() def post_process_images(self, doctree: Node) -> None: """Pick the best candidate for an image and link down-scaled images to @@ -888,7 +896,7 @@ class StandaloneHTMLBuilder(Builder): if self.config.html_scaled_image_link and self.html_scaled_image_link: for node in doctree.findall(nodes.image): - if not any((key in node) for key in ['scale', 'width', 'height']): + if not any((key in node) for key in ('scale', 'width', 'height')): # resizing options are not given. scaled image link is available # only for resized images. continue @@ -933,7 +941,7 @@ class StandaloneHTMLBuilder(Builder): if self.indexer is not None and title: filename = self.env.doc2path(pagename, base=False) metadata = self.env.metadata.get(pagename, {}) - if 'nosearch' in metadata: + if 'no-search' in metadata or 'nosearch' in metadata: self.indexer.feed(pagename, filename, '', new_document('')) else: self.indexer.feed(pagename, filename, title, doctree) @@ -953,27 +961,11 @@ class StandaloneHTMLBuilder(Builder): def has_wildcard(pattern: str) -> bool: return any(char in pattern for char in '*?[') - sidebars = None matched = None customsidebar = None # default sidebars settings for selected theme - if self.theme.name == 'alabaster': - # provide default settings for alabaster (for compatibility) - # Note: this will be removed before Sphinx-2.0 - try: - # get default sidebars settings from alabaster (if defined) - theme_default_sidebars = self.theme.config.get('theme', 'sidebars') - if theme_default_sidebars: - sidebars = [name.strip() for name in theme_default_sidebars.split(',')] - except Exception: - # fallback to better default settings - sidebars = ['about.html', 'navigation.html', 'relations.html', - 'searchbox.html', 'donate.html'] - else: - theme_default_sidebars = self.theme.get_config('theme', 'sidebars', None) - if theme_default_sidebars: - sidebars = [name.strip() for name in theme_default_sidebars.split(',')] + sidebars = list(self.theme.sidebar_templates) # user sidebar settings html_sidebars = self.get_builder_config('sidebars', 'html') @@ -992,7 +984,7 @@ class StandaloneHTMLBuilder(Builder): matched = pattern sidebars = patsidebars - if sidebars is None: + if len(sidebars) == 0: # keep defaults pass @@ -1040,9 +1032,7 @@ class StandaloneHTMLBuilder(Builder): return True if name == 'search' and self.search: return True - if name == 'genindex' and self.get_builder_config('use_index', 'html'): - return True - return False + return name == 'genindex' and self.get_builder_config('use_index', 'html') ctx['hasdoc'] = hasdoc ctx['toctree'] = lambda **kwargs: self._get_local_toctree(pagename, **kwargs) @@ -1055,13 +1045,16 @@ class StandaloneHTMLBuilder(Builder): outdir = self.app.outdir def css_tag(css: _CascadingStyleSheet) -> str: - attrs = [] - for key, value in css.attributes.items(): - if value is not None: - attrs.append(f'{key}="{html.escape(value, quote=True)}"') + attrs = [f'{key}="{html.escape(value, quote=True)}"' + for key, value in css.attributes.items() + if value is not None] uri = pathto(os.fspath(css.filename), resource=True) - if checksum := _file_checksum(outdir, css.filename): - uri += f'?v={checksum}' + # the EPUB format does not allow the use of query components + # the Windows help compiler requires that css links + # don't have a query component + if self.name not in {'epub', 'htmlhelp'}: + if checksum := _file_checksum(outdir, css.filename): + uri += f'?v={checksum}' return f'<link {" ".join(sorted(attrs))} href="{uri}" />' ctx['css_tag'] = css_tag @@ -1071,13 +1064,10 @@ class StandaloneHTMLBuilder(Builder): # str value (old styled) return f'<script src="{pathto(js, resource=True)}"></script>' - attrs = [] body = js.attributes.get('body', '') - for key, value in js.attributes.items(): - if key == 'body': - continue - if value is not None: - attrs.append(f'{key}="{html.escape(value, quote=True)}"') + attrs = [f'{key}="{html.escape(value, quote=True)}"' + for key, value in js.attributes.items() + if key != 'body' and value is not None] if not js.filename: if attrs: @@ -1091,8 +1081,10 @@ class StandaloneHTMLBuilder(Builder): # https://docs.mathjax.org/en/v2.7-latest/configuration.html#considerations-for-using-combined-configuration-files # https://github.com/sphinx-doc/sphinx/issues/11658 pass - elif checksum := _file_checksum(outdir, js.filename): - uri += f'?v={checksum}' + # the EPUB format does not allow the use of query components + elif self.name != 'epub': + if checksum := _file_checksum(outdir, js.filename): + uri += f'?v={checksum}' if attrs: return f'<script {" ".join(sorted(attrs))} src="{uri}"></script>' return f'<script src="{uri}"></script>' @@ -1182,7 +1174,7 @@ class StandaloneHTMLBuilder(Builder): def convert_html_css_files(app: Sphinx, config: Config) -> None: - """This converts string styled html_css_files to tuple styled one.""" + """Convert string styled html_css_files to tuple styled one.""" html_css_files: list[tuple[str, dict]] = [] for entry in config.html_css_files: if isinstance(entry, str): @@ -1205,7 +1197,7 @@ def _format_modified_time(timestamp: float) -> str: def convert_html_js_files(app: Sphinx, config: Config) -> None: - """This converts string styled html_js_files to tuple styled one.""" + """Convert string styled html_js_files to tuple styled one.""" html_js_files: list[tuple[str, dict]] = [] for entry in config.html_js_files: if isinstance(entry, str): @@ -1302,7 +1294,7 @@ def error_on_html_4(_app: Sphinx, config: Config) -> None: )) -def setup(app: Sphinx) -> dict[str, Any]: +def setup(app: Sphinx) -> ExtensionMetadata: # builders app.add_builder(StandaloneHTMLBuilder) @@ -1310,21 +1302,20 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value('html_theme', 'alabaster', 'html') app.add_config_value('html_theme_path', [], 'html') app.add_config_value('html_theme_options', {}, 'html') - app.add_config_value('html_title', - lambda self: _('%s %s documentation') % (self.project, self.release), - 'html', [str]) + app.add_config_value( + 'html_title', lambda c: _('%s %s documentation') % (c.project, c.release), 'html', str) app.add_config_value('html_short_title', lambda self: self.html_title, 'html') - app.add_config_value('html_style', None, 'html', [list, str]) - app.add_config_value('html_logo', None, 'html', [str]) - app.add_config_value('html_favicon', None, 'html', [str]) + app.add_config_value('html_style', None, 'html', {list, str}) + app.add_config_value('html_logo', None, 'html', str) + app.add_config_value('html_favicon', None, 'html', str) app.add_config_value('html_css_files', [], 'html') app.add_config_value('html_js_files', [], 'html') app.add_config_value('html_static_path', [], 'html') app.add_config_value('html_extra_path', [], 'html') - app.add_config_value('html_last_updated_fmt', None, 'html', [str]) + app.add_config_value('html_last_updated_fmt', None, 'html', str) app.add_config_value('html_sidebars', {}, 'html') app.add_config_value('html_additional_pages', {}, 'html') - app.add_config_value('html_domain_indices', True, 'html', [list]) + app.add_config_value('html_domain_indices', True, 'html', list) app.add_config_value('html_permalinks', True, 'html') app.add_config_value('html_permalinks_icon', 'ΒΆ', 'html') app.add_config_value('html_use_index', True, 'html') @@ -1333,8 +1324,8 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value('html_show_sourcelink', True, 'html') app.add_config_value('html_sourcelink_suffix', '.txt', 'html') app.add_config_value('html_use_opensearch', '', 'html') - app.add_config_value('html_file_suffix', None, 'html', [str]) - app.add_config_value('html_link_suffix', None, 'html', [str]) + app.add_config_value('html_file_suffix', None, 'html', str) + app.add_config_value('html_link_suffix', None, 'html', str) app.add_config_value('html_show_copyright', True, 'html') app.add_config_value('html_show_search_summary', True, 'html') app.add_config_value('html_show_sphinx', True, 'html') @@ -1342,12 +1333,13 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value('html_output_encoding', 'utf-8', 'html') app.add_config_value('html_compact_lists', True, 'html') app.add_config_value('html_secnumber_suffix', '. ', 'html') - app.add_config_value('html_search_language', None, 'html', [str]) + app.add_config_value('html_search_language', None, 'html', str) app.add_config_value('html_search_options', {}, 'html') app.add_config_value('html_search_scorer', '', '') app.add_config_value('html_scaled_image_link', True, 'html') app.add_config_value('html_baseurl', '', 'html') - app.add_config_value('html_codeblock_linenos_style', 'inline', 'html', # RemovedInSphinx70Warning # noqa: E501 + # removal is indefinitely on hold (ref: https://github.com/sphinx-doc/sphinx/issues/10265) + app.add_config_value('html_codeblock_linenos_style', 'inline', 'html', ENUM('table', 'inline')) app.add_config_value('html_math_renderer', None, 'env') app.add_config_value('html4_writer', False, 'html') @@ -1380,14 +1372,14 @@ def setup(app: Sphinx) -> dict[str, Any]: } -# deprecated name -> (object to return, canonical path or empty string) -_DEPRECATED_OBJECTS = { +# deprecated name -> (object to return, canonical path or empty string, removal version) +_DEPRECATED_OBJECTS: dict[str, tuple[Any, str, tuple[int, int]]] = { 'Stylesheet': (_CascadingStyleSheet, 'sphinx.builders.html._assets._CascadingStyleSheet', (9, 0)), # NoQA: E501 'JavaScript': (_JavaScript, 'sphinx.builders.html._assets._JavaScript', (9, 0)), } -def __getattr__(name): +def __getattr__(name: str) -> Any: if name not in _DEPRECATED_OBJECTS: msg = f'module {__name__!r} has no attribute {name!r}' raise AttributeError(msg) diff --git a/sphinx/builders/html/_assets.py b/sphinx/builders/html/_assets.py index a72c500..699a160 100644 --- a/sphinx/builders/html/_assets.py +++ b/sphinx/builders/html/_assets.py @@ -3,7 +3,7 @@ from __future__ import annotations import os import warnings import zlib -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, NoReturn from sphinx.deprecation import RemovedInSphinx90Warning from sphinx.errors import ThemeError @@ -27,15 +27,15 @@ class _CascadingStyleSheet: ) -> None: object.__setattr__(self, 'filename', filename) object.__setattr__(self, 'priority', priority) - object.__setattr__(self, 'attributes', {'rel': rel, 'type': type, **attributes}) + object.__setattr__(self, 'attributes', {'rel': rel, 'type': type} | attributes) - def __str__(self): + def __str__(self) -> str: attr = ', '.join(f'{k}={v!r}' for k, v in self.attributes.items()) return (f'{self.__class__.__name__}({self.filename!r}, ' f'priority={self.priority}, ' f'{attr})') - def __eq__(self, other): + def __eq__(self, other: object) -> bool: if isinstance(other, str): warnings.warn('The str interface for _CascadingStyleSheet objects is deprecated. ' 'Use css.filename instead.', RemovedInSphinx90Warning, stacklevel=2) @@ -46,23 +46,23 @@ class _CascadingStyleSheet: and self.priority == other.priority and self.attributes == other.attributes) - def __hash__(self): + def __hash__(self) -> int: return hash((self.filename, self.priority, *sorted(self.attributes.items()))) - def __setattr__(self, key, value): + def __setattr__(self, key: str, value: Any) -> NoReturn: msg = f'{self.__class__.__name__} is immutable' raise AttributeError(msg) - def __delattr__(self, key): + def __delattr__(self, key: str) -> NoReturn: msg = f'{self.__class__.__name__} is immutable' raise AttributeError(msg) - def __getattr__(self, key): + def __getattr__(self, key: str) -> str: warnings.warn('The str interface for _CascadingStyleSheet objects is deprecated. ' 'Use css.filename instead.', RemovedInSphinx90Warning, stacklevel=2) return getattr(os.fspath(self.filename), key) - def __getitem__(self, key): + def __getitem__(self, key: int | slice) -> str: warnings.warn('The str interface for _CascadingStyleSheet objects is deprecated. ' 'Use css.filename instead.', RemovedInSphinx90Warning, stacklevel=2) return os.fspath(self.filename)[key] @@ -83,7 +83,7 @@ class _JavaScript: object.__setattr__(self, 'priority', priority) object.__setattr__(self, 'attributes', attributes) - def __str__(self): + def __str__(self) -> str: attr = '' if self.attributes: attr = ', ' + ', '.join(f'{k}={v!r}' for k, v in self.attributes.items()) @@ -91,7 +91,7 @@ class _JavaScript: f'priority={self.priority}' f'{attr})') - def __eq__(self, other): + def __eq__(self, other: object) -> bool: if isinstance(other, str): warnings.warn('The str interface for _JavaScript objects is deprecated. ' 'Use js.filename instead.', RemovedInSphinx90Warning, stacklevel=2) @@ -102,23 +102,23 @@ class _JavaScript: and self.priority == other.priority and self.attributes == other.attributes) - def __hash__(self): + def __hash__(self) -> int: return hash((self.filename, self.priority, *sorted(self.attributes.items()))) - def __setattr__(self, key, value): + def __setattr__(self, key: str, value: Any) -> NoReturn: msg = f'{self.__class__.__name__} is immutable' raise AttributeError(msg) - def __delattr__(self, key): + def __delattr__(self, key: str) -> NoReturn: msg = f'{self.__class__.__name__} is immutable' raise AttributeError(msg) - def __getattr__(self, key): + def __getattr__(self, key: str) -> str: warnings.warn('The str interface for _JavaScript objects is deprecated. ' 'Use js.filename instead.', RemovedInSphinx90Warning, stacklevel=2) return getattr(os.fspath(self.filename), key) - def __getitem__(self, key): + def __getitem__(self, key: int | slice) -> str: warnings.warn('The str interface for _JavaScript objects is deprecated. ' 'Use js.filename instead.', RemovedInSphinx90Warning, stacklevel=2) return os.fspath(self.filename)[key] diff --git a/sphinx/builders/html/transforms.py b/sphinx/builders/html/transforms.py index 18a8d38..a36588c 100644 --- a/sphinx/builders/html/transforms.py +++ b/sphinx/builders/html/transforms.py @@ -12,6 +12,7 @@ from sphinx.util.nodes import NodeMatcher if TYPE_CHECKING: from sphinx.application import Sphinx + from sphinx.util.typing import ExtensionMetadata class KeyboardTransform(SphinxPostTransform): @@ -31,6 +32,7 @@ class KeyboardTransform(SphinxPostTransform): <literal class="kbd"> x """ + default_priority = 400 formats = ('html',) pattern = re.compile(r'(?<=.)(-|\+|\^|\s+)(?=.)') @@ -46,7 +48,7 @@ class KeyboardTransform(SphinxPostTransform): matcher = NodeMatcher(nodes.literal, classes=["kbd"]) # this list must be pre-created as during iteration new nodes # are added which match the condition in the NodeMatcher. - for node in list(self.document.findall(matcher)): # type: nodes.literal + for node in list(matcher.findall(self.document)): parts = self.pattern.split(node[-1].astext()) if len(parts) == 1 or self.is_multiwords_key(parts): continue @@ -76,7 +78,7 @@ class KeyboardTransform(SphinxPostTransform): return False -def setup(app: Sphinx) -> dict[str, Any]: +def setup(app: Sphinx) -> ExtensionMetadata: app.add_post_transform(KeyboardTransform) return { |