Add `_DomainsContainer` for better static typing (#12783)

Co-authored-by: Daniel Eades <danieleades@hotmail.com>
This commit is contained in:
Adam Turner
2024-09-18 03:50:27 +01:00
committed by GitHub
parent 92f024e2bf
commit 76110c3ea0
55 changed files with 508 additions and 263 deletions

View File

@@ -4,11 +4,10 @@ from __future__ import annotations
import html
from os import path
from typing import TYPE_CHECKING, Any, cast
from typing import TYPE_CHECKING, Any
from sphinx import package_dir
from sphinx.builders import Builder
from sphinx.domains.changeset import ChangeSetDomain
from sphinx.locale import _, __
from sphinx.theming import HTMLThemeFactory
from sphinx.util import logging
@@ -49,7 +48,7 @@ class ChangesBuilder(Builder):
def write(self, *ignored: Any) -> None:
version = self.config.version
domain = cast(ChangeSetDomain, self.env.get_domain('changeset'))
domain = self.env.domains.changeset_domain
libchanges: dict[str, list[tuple[str, str, int]]] = {}
apichanges: list[tuple[str, str, int]] = []
otherchanges: dict[tuple[str, str], list[tuple[str, str, int]]] = {}

View File

@@ -33,7 +33,7 @@ from sphinx.builders.html._assets import (
from sphinx.builders.html._build_info import BuildInfo
from sphinx.config import ENUM, Config
from sphinx.deprecation import _deprecation_warning
from sphinx.domains import Domain, Index, IndexEntry
from sphinx.domains import Index, IndexEntry
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import document_toc, global_toctree_for_doc
@@ -439,8 +439,7 @@ class StandaloneHTMLBuilder(Builder):
indices_config = frozenset(indices_config)
else:
check_names = False
for domain_name in sorted(self.env.domains):
domain: Domain = self.env.domains[domain_name]
for domain in self.env.domains.sorted():
for index_cls in domain.indices:
index_name = f'{domain.name}-{index_cls.name}'
if check_names and index_name not in indices_config:

View File

@@ -15,7 +15,6 @@ from sphinx.builders.latex.nodes import (
math_reference,
thebibliography,
)
from sphinx.domains.citation import CitationDomain
from sphinx.locale import __
from sphinx.transforms import SphinxTransform
from sphinx.transforms.post_transforms import SphinxPostTransform
@@ -536,7 +535,7 @@ class CitationReferenceTransform(SphinxPostTransform):
formats = ('latex',)
def run(self, **kwargs: Any) -> None:
domain = cast(CitationDomain, self.env.get_domain('citation'))
domain = self.env.domains.citation_domain
matcher = NodeMatcher(addnodes.pending_xref, refdomain='citation', reftype='ref')
for node in matcher.findall(self.document):
docname, labelid, _ = domain.citations.get(node['reftarget'], ('', '', 0))
@@ -557,7 +556,7 @@ class MathReferenceTransform(SphinxPostTransform):
formats = ('latex',)
def run(self, **kwargs: Any) -> None:
equations = self.env.get_domain('math').data['objects']
equations = self.env.domains.math_domain.data['objects']
for node in self.document.findall(addnodes.pending_xref):
if node['refdomain'] == 'math' and node['reftype'] in ('eq', 'numref'):
docname, _ = equations.get(node['reftarget'], (None, None))

View File

@@ -75,9 +75,8 @@ class XMLBuilder(Builder):
# work around multiple string % tuple issues in docutils;
# replace tuples in attribute values with lists
doctree = doctree.deepcopy()
for domain in self.env.domains.values():
xmlns = "xmlns:" + domain.name
doctree[xmlns] = "https://www.sphinx-doc.org/"
for domain in self.env.domains.sorted():
doctree[f'xmlns:{domain.name}'] = 'https://www.sphinx-doc.org/'
for node in doctree.findall(nodes.Element):
for att, value in node.attributes.items():
if isinstance(value, tuple):

View File

@@ -351,7 +351,7 @@ class DefaultDomain(SphinxDirective):
domain_name = self.arguments[0].lower()
# if domain_name not in env.domains:
# # try searching by label
# for domain in env.domains.values():
# for domain in env.domains.sorted():
# if domain.label.lower() == domain_name:
# domain_name = domain.name
# break

View File

@@ -12,7 +12,6 @@ from docutils.parsers.rst.directives.misc import Meta
from docutils.parsers.rst.roles import set_classes
from sphinx.directives import optional_int
from sphinx.domains.math import MathDomain
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective
@@ -165,7 +164,7 @@ class MathDirective(SphinxDirective):
return
# register label to domain
domain = cast(MathDomain, self.env.get_domain('math'))
domain = self.env.domains.math_domain
domain.note_equation(self.env.docname, node['label'], location=node)
node['number'] = domain.get_equation_number_for(node['label'])

View File

@@ -7,13 +7,14 @@ and roles describing e.g. constructs of one programming language.
from __future__ import annotations
import copy
from typing import TYPE_CHECKING, Any, cast
from typing import TYPE_CHECKING
from sphinx.domains._index import Index, IndexEntry
from sphinx.locale import _
if TYPE_CHECKING:
from collections.abc import Callable, Iterable, Sequence
from collections.abc import Callable, Iterable, Sequence, Set
from typing import Any
from docutils import nodes
from docutils.nodes import Element, Node
@@ -136,10 +137,8 @@ class Domain:
def setup(self) -> None:
"""Set up domain object."""
from sphinx.domains.std import StandardDomain
# Add special hyperlink target for index pages (ex. py-modindex)
std = cast(StandardDomain, self.env.get_domain('std'))
std = self.env.domains.standard_domain
for index in self.indices:
if index.name and index.localname:
docname = f"{self.name}-{index.name}"
@@ -199,7 +198,7 @@ class Domain:
"""Remove traces of a document in the domain-specific inventories."""
pass
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
"""Merge in data regarding *docnames* from a different domaindata
inventory (coming from a subprocess in parallel builds).
"""

View File

@@ -0,0 +1,293 @@
from __future__ import annotations
from typing import TYPE_CHECKING, overload
if TYPE_CHECKING:
from collections.abc import Iterable, Iterator, Mapping, Set
from typing import Any, Final, Literal, NoReturn
from docutils import nodes
from typing_extensions import Self
from sphinx.domains import Domain
from sphinx.domains.c import CDomain
from sphinx.domains.changeset import ChangeSetDomain
from sphinx.domains.citation import CitationDomain
from sphinx.domains.cpp import CPPDomain
from sphinx.domains.index import IndexDomain
from sphinx.domains.javascript import JavaScriptDomain
from sphinx.domains.math import MathDomain
from sphinx.domains.python import PythonDomain
from sphinx.domains.rst import ReSTDomain
from sphinx.domains.std import StandardDomain
from sphinx.environment import BuildEnvironment
from sphinx.ext.duration import DurationDomain
from sphinx.ext.todo import TodoDomain
class _DomainsContainer:
"""Container for domain instances.
This class is private, including its name, constructor, and all methods.
Any or all of these will change without notice or warning in any release.
The public interface is restricted to:
* the``domains.['<domain-name>']`` mapping interface
* the ``domains.<core-domain-name>`` attributes for core domains.
* the `.get()``, ``.keys()``, ``.items()``, and ``.values()`` methods.
Additionally, this class supports ``iter`` and ``len``,
and provides membership testing via the ``in`` operator.
"""
__slots__ = (
'_domain_instances',
'c_domain',
'changeset_domain',
'citation_domain',
'cpp_domain',
'index_domain',
'javascript_domain',
'math_domain',
'python_domain',
'restructuredtext_domain',
'standard_domain',
)
#: First-party domains in :mod:`sphinx.domains`
_core_domains: Final = frozenset({
'std',
# Language-specific domains
'c',
'cpp',
'js',
'py',
'rst',
# Other core domains
'changeset',
'citation',
'index',
'math',
})
@classmethod
def _from_environment(cls, env: BuildEnvironment, /) -> Self:
create_domains = env.app.registry.create_domains
# Initialise domains
if domains := {domain.name: domain for domain in create_domains(env)}:
return cls(**domains) # type: ignore[arg-type]
return cls._from_environment_default(env=env)
@classmethod
def _from_environment_default(cls, *, env: BuildEnvironment) -> Self:
"""Return a default instance with every domain we require."""
from sphinx.domains.c import CDomain
from sphinx.domains.changeset import ChangeSetDomain
from sphinx.domains.citation import CitationDomain
from sphinx.domains.cpp import CPPDomain
from sphinx.domains.index import IndexDomain
from sphinx.domains.javascript import JavaScriptDomain
from sphinx.domains.math import MathDomain
from sphinx.domains.python import PythonDomain
from sphinx.domains.rst import ReSTDomain
from sphinx.domains.std import StandardDomain
return cls(
c=CDomain(env),
changeset=ChangeSetDomain(env),
citation=CitationDomain(env),
cpp=CPPDomain(env),
index=IndexDomain(env),
js=JavaScriptDomain(env),
math=MathDomain(env),
py=PythonDomain(env),
rst=ReSTDomain(env),
std=StandardDomain(env),
)
def __init__(
self,
*,
c: CDomain,
cpp: CPPDomain,
js: JavaScriptDomain,
py: PythonDomain,
rst: ReSTDomain,
std: StandardDomain,
changeset: ChangeSetDomain,
citation: CitationDomain,
index: IndexDomain,
math: MathDomain,
**domains: Domain,
) -> None:
# All domains, including core.
# Implemented as a dict for backwards compatibility.
self._domain_instances: Mapping[str, Domain] = {
'c': c,
'changeset': changeset,
'citation': citation,
'cpp': cpp,
'index': index,
'js': js,
'math': math,
'py': py,
'rst': rst,
'std': std,
**domains,
}
# Provide typed attributes for the core domains
self.standard_domain: StandardDomain = std
self.c_domain: CDomain = c
self.cpp_domain: CPPDomain = cpp
self.javascript_domain: JavaScriptDomain = js
self.python_domain: PythonDomain = py
self.restructuredtext_domain: ReSTDomain = rst
self.changeset_domain: ChangeSetDomain = changeset
self.citation_domain: CitationDomain = citation
self.index_domain: IndexDomain = index
self.math_domain: MathDomain = math
for domain_name, domain in self._domain_instances.items():
# invariant from ``_DomainsContainer._from_environment``
if domain_name != domain.name:
msg = f'Domain name mismatch in {domain!r}: {domain_name!r} != {domain.name!r}'
raise ValueError(msg)
def _setup(self) -> None:
for domain in self._domain_instances.values():
domain.setup()
def _process_doc(
self, env: BuildEnvironment, docname: str, document: nodes.document
) -> None:
for domain in self._domain_instances.values():
domain.process_doc(env, docname, document)
def _clear_doc(self, docname: str) -> None:
for domain in self._domain_instances.values():
domain.clear_doc(docname)
def _merge_domain_data(self, docnames: Set[str], domain_data: dict[str, Any]) -> None:
for domain_name, domain in self._domain_instances.items():
domain.merge_domaindata(docnames, domain_data[domain_name])
def _check_consistency(self) -> None:
for domain in self._domain_instances.values():
domain.check_consistency()
def __contains__(self, key: str) -> bool:
return key in self._domain_instances
def __eq__(self, other: object) -> bool:
if not isinstance(other, _DomainsContainer):
return NotImplemented
return self._domain_instances == other._domain_instances
def __setattr__(self, key: str, value: object) -> None:
if key in self._core_domains:
msg = f'{self.__class__.__name__!r} object does not support assignment to {key!r}'
raise TypeError(msg)
super().__setattr__(key, value)
def __delattr__(self, key: str) -> None:
if key in self._core_domains:
msg = f'{self.__class__.__name__!r} object does not support deletion of {key!r}'
raise TypeError(msg)
super().__delattr__(key)
# Mapping interface: builtin domains
@overload
def __getitem__(self, key: Literal["c"]) -> CDomain:
...
@overload
def __getitem__(self, key: Literal["cpp"]) -> CPPDomain:
...
@overload
def __getitem__(self, key: Literal["changeset"]) -> ChangeSetDomain:
...
@overload
def __getitem__(self, key: Literal["citation"]) -> CitationDomain:
...
@overload
def __getitem__(self, key: Literal["index"]) -> IndexDomain:
...
@overload
def __getitem__(self, key: Literal["js"]) -> JavaScriptDomain:
...
@overload
def __getitem__(self, key: Literal["math"]) -> MathDomain:
...
@overload
def __getitem__(self, key: Literal["py"]) -> PythonDomain:
...
@overload
def __getitem__(self, key: Literal["rst"]) -> ReSTDomain:
...
@overload
def __getitem__(self, key: Literal["std"]) -> StandardDomain:
...
# Mapping interface: first-party domains
@overload
def __getitem__(self, key: Literal["duration"]) -> DurationDomain:
...
@overload
def __getitem__(self, key: Literal["todo"]) -> TodoDomain:
...
# Mapping interface: third-party domains
@overload
def __getitem__(self, key: str) -> Domain:
...
def __getitem__(self, key: str) -> Domain:
if domain := getattr(self, key, None):
return domain
return self._domain_instances[key]
def __setitem__(self, key: str, value: Domain) -> NoReturn:
msg = f'{self.__class__.__name__!r} object does not support item assignment'
raise TypeError(msg)
def __delitem__(self, key: str) -> NoReturn:
msg = f'{self.__class__.__name__!r} object does not support item deletion'
raise TypeError(msg)
def __iter__(self) -> Iterator[str]:
return iter(self._domain_instances.keys())
def __len__(self) -> int:
return len(self._domain_instances)
def get(self, key: str, default: Domain | None = None) -> Domain | None:
return self._domain_instances.get(key, default)
def keys(self) -> Iterable[str]:
return self._domain_instances.keys()
def items(self) -> Iterable[tuple[str, Domain]]:
return self._domain_instances.items()
def values(self) -> Iterable[Domain]:
return self._domain_instances.values()
def sorted(self) -> Iterable[Domain]:
for _domain_name, domain in sorted(self._domain_instances.items()):
yield domain

View File

@@ -33,7 +33,7 @@ from sphinx.util.docutils import SphinxDirective, SphinxRole
from sphinx.util.nodes import make_refnode
if TYPE_CHECKING:
from collections.abc import Iterator
from collections.abc import Iterator, Set
from docutils.nodes import Element, Node, TextElement, system_message
@@ -526,7 +526,7 @@ class AliasTransform(SphinxTransform):
node.replace_self(signode)
continue
rootSymbol: Symbol = self.env.domains['c'].data['root_symbol']
rootSymbol: Symbol = self.env.domains.c_domain.data['root_symbol']
parentSymbol: Symbol | None = rootSymbol.direct_lookup(parentKey)
if not parentSymbol:
logger.debug("Target: %s", sig)
@@ -745,7 +745,7 @@ class CDomain(Domain):
def process_field_xref(self, pnode: pending_xref) -> None:
pnode.attributes.update(self.env.ref_context)
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
if Symbol.debug_show_tree:
logger.debug("merge_domaindata:")
logger.debug("\tself:")

View File

@@ -2,7 +2,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple, cast
from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple
from docutils import nodes
@@ -12,6 +12,8 @@ from sphinx.locale import _
from sphinx.util.docutils import SphinxDirective
if TYPE_CHECKING:
from collections.abc import Set
from docutils.nodes import Node
from sphinx.application import Sphinx
@@ -96,7 +98,7 @@ class VersionChange(SphinxDirective):
translatable=False)
node.append(para)
domain = cast(ChangeSetDomain, self.env.get_domain('changeset'))
domain = self.env.domains.changeset_domain
domain.note_changeset(node)
ret: list[Node] = [node]
@@ -132,7 +134,7 @@ class ChangeSetDomain(Domain):
if changeset.docname == docname:
changes.remove(changeset)
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
# XXX duplicates?
for version, otherchanges in otherdata['changes'].items():
changes = self.changesets.setdefault(version, [])

View File

@@ -14,6 +14,8 @@ from sphinx.util import logging
from sphinx.util.nodes import copy_source_info, make_refnode
if TYPE_CHECKING:
from collections.abc import Set
from docutils.nodes import Element
from sphinx.application import Sphinx
@@ -53,7 +55,7 @@ class CitationDomain(Domain):
elif docname in docnames:
docnames.remove(docname)
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
# XXX duplicates?
for key, data in otherdata['citations'].items():
if data[0] in docnames:
@@ -108,7 +110,7 @@ class CitationDefinitionTransform(SphinxTransform):
default_priority = 619
def apply(self, **kwargs: Any) -> None:
domain = cast(CitationDomain, self.env.get_domain('citation'))
domain = self.env.domains.citation_domain
for node in self.document.findall(nodes.citation):
# register citation node to domain
node['docname'] = self.env.docname
@@ -128,7 +130,7 @@ class CitationReferenceTransform(SphinxTransform):
default_priority = 619
def apply(self, **kwargs: Any) -> None:
domain = cast(CitationDomain, self.env.get_domain('citation'))
domain = self.env.domains.citation_domain
for node in self.document.findall(nodes.citation_reference):
target = node.astext()
ref = pending_xref(target, refdomain='citation', reftype='ref',

View File

@@ -37,7 +37,7 @@ from sphinx.util.docutils import SphinxDirective, SphinxRole
from sphinx.util.nodes import make_refnode
if TYPE_CHECKING:
from collections.abc import Iterator
from collections.abc import Iterator, Set
from docutils.nodes import Element, Node, TextElement, system_message
@@ -661,7 +661,7 @@ class AliasTransform(SphinxTransform):
node.replace_self(signode)
continue
rootSymbol: Symbol = self.env.domains['cpp'].data['root_symbol']
rootSymbol: Symbol = self.env.domains.cpp_domain.data['root_symbol']
parentSymbol: Symbol = rootSymbol.direct_lookup(parentKey)
if not parentSymbol:
logger.debug("Target: %s", sig)
@@ -933,7 +933,7 @@ class CPPDomain(Domain):
def process_field_xref(self, pnode: pending_xref) -> None:
pnode.attributes.update(self.env.ref_context)
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
if Symbol.debug_show_tree:
logger.debug("merge_domaindata:")
logger.debug("\tself:")

View File

@@ -15,7 +15,7 @@ from sphinx.util.index_entries import split_index_msg
from sphinx.util.nodes import process_index_entry
if TYPE_CHECKING:
from collections.abc import Iterable
from collections.abc import Set
from docutils.nodes import Node, system_message
@@ -40,7 +40,7 @@ class IndexDomain(Domain):
def clear_doc(self, docname: str) -> None:
self.entries.pop(docname, None)
def merge_domaindata(self, docnames: Iterable[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
for docname in docnames:
self.entries[docname] = otherdata['entries'][docname]

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
import contextlib
from typing import TYPE_CHECKING, Any, ClassVar, cast
from typing import TYPE_CHECKING, Any, ClassVar
from docutils import nodes
from docutils.parsers.rst import directives
@@ -20,7 +20,7 @@ from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_id, make_refnode
if TYPE_CHECKING:
from collections.abc import Iterator
from collections.abc import Iterator, Set
from docutils.nodes import Element, Node
@@ -150,7 +150,7 @@ class JSObject(ObjectDescription[tuple[str, str]]):
signode['ids'].append(node_id)
self.state.document.note_explicit_target(signode)
domain = cast(JavaScriptDomain, self.env.get_domain('js'))
domain = self.env.domains.javascript_domain
domain.note_object(fullname, self.objtype, node_id, location=signode)
if 'no-index-entry' not in self.options:
@@ -321,7 +321,7 @@ class JSModule(SphinxDirective):
ret: list[Node] = []
if not no_index:
domain = cast(JavaScriptDomain, self.env.get_domain('js'))
domain = self.env.domains.javascript_domain
node_id = make_id(self.env, self.state.document, 'module', mod_name)
domain.note_module(mod_name, node_id)
@@ -423,7 +423,7 @@ class JavaScriptDomain(Domain):
if pkg_docname == docname:
del self.modules[modname]
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
# XXX check duplicates
for fullname, (fn, node_id, objtype) in otherdata['objects'].items():
if fn in docnames:

View File

@@ -14,7 +14,7 @@ from sphinx.util import logging
from sphinx.util.nodes import make_refnode
if TYPE_CHECKING:
from collections.abc import Iterable
from collections.abc import Iterable, Set
from sphinx.addnodes import pending_xref
from sphinx.application import Sphinx
@@ -85,7 +85,7 @@ class MathDomain(Domain):
self.data['has_equations'].pop(docname, None)
def merge_domaindata(self, docnames: Iterable[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
for labelid, (doc, eqno) in otherdata['objects'].items():
if doc in docnames:
self.equations[labelid] = (doc, eqno)

View File

@@ -25,7 +25,7 @@ from sphinx.util.nodes import (
)
if TYPE_CHECKING:
from collections.abc import Iterable, Iterator
from collections.abc import Iterable, Iterator, Set
from docutils.nodes import Element, Node
@@ -455,7 +455,7 @@ class PyModule(SphinxDirective):
if 'no-index' not in self.options and 'noindex' in self.options:
self.options['no-index'] = self.options['noindex']
domain = cast(PythonDomain, self.env.get_domain('py'))
domain = self.env.domains.python_domain
modname = self.arguments[0].strip()
no_index = 'no-index' in self.options
@@ -721,7 +721,7 @@ class PythonDomain(Domain):
if mod.docname == docname:
del self.modules[modname]
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
# XXX check duplicates?
for fullname, obj in otherdata['objects'].items():
if obj.docname in docnames:

View File

@@ -337,7 +337,7 @@ class PyObject(ObjectDescription[tuple[str, str]]):
signode['ids'].append(node_id)
self.state.document.note_explicit_target(signode)
domain = self.env.domains['py']
domain = self.env.domains.python_domain
domain.note_object(fullname, self.objtype, node_id, location=signode)
canonical_name = self.options.get('canonical')

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
import re
from typing import TYPE_CHECKING, Any, ClassVar, cast
from typing import TYPE_CHECKING, Any, ClassVar
from docutils.parsers.rst import directives
@@ -16,7 +16,7 @@ from sphinx.util import logging
from sphinx.util.nodes import make_id, make_refnode
if TYPE_CHECKING:
from collections.abc import Iterator
from collections.abc import Iterator, Set
from docutils.nodes import Element
@@ -51,7 +51,7 @@ class ReSTMarkup(ObjectDescription[str]):
signode['ids'].append(node_id)
self.state.document.note_explicit_target(signode)
domain = cast(ReSTDomain, self.env.get_domain('rst'))
domain = self.env.domains.restructuredtext_domain
domain.note_object(self.objtype, name, node_id, location=signode)
if 'no-index-entry' not in self.options:
@@ -164,7 +164,7 @@ class ReSTDirectiveOption(ReSTMarkup):
return name
def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
domain = cast(ReSTDomain, self.env.get_domain('rst'))
domain = self.env.domains.restructuredtext_domain
directive_name = self.current_directive
if directive_name:
@@ -254,7 +254,7 @@ class ReSTDomain(Domain):
if doc == docname:
del self.objects[typ, name]
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
# XXX check duplicates
for (typ, name), (doc, node_id) in otherdata['objects'].items():
if doc in docnames:

View File

@@ -23,7 +23,7 @@ from sphinx.util.nodes import clean_astext, make_id, make_refnode
from sphinx.util.parsing import nested_parse_to_nodes
if TYPE_CHECKING:
from collections.abc import Callable, Iterable, Iterator
from collections.abc import Callable, Iterable, Iterator, Set
from sphinx.application import Sphinx
from sphinx.builders import Builder
@@ -78,7 +78,7 @@ class GenericObject(ObjectDescription[str]):
indexentry = self.indextemplate % (name,)
self.indexnode['entries'].append((indextype, indexentry, node_id, '', None))
std = cast(StandardDomain, self.env.get_domain('std'))
std = self.env.domains.standard_domain
std.note_object(self.objtype, name, node_id, location=signode)
@@ -140,7 +140,8 @@ class ConfigurationValue(ObjectDescription[str]):
self.state.document.note_explicit_target(signode)
index_entry = self.index_template % name
self.indexnode['entries'].append(('pair', index_entry, node_id, '', None))
self.env.domains['std'].note_object(self.objtype, name, node_id, location=signode)
domain = self.env.domains.standard_domain
domain.note_object(self.objtype, name, node_id, location=signode)
def transform_content(self, content_node: addnodes.desc_content) -> None:
"""Insert *type* and *default* as a field list."""
@@ -211,7 +212,7 @@ class Target(SphinxDirective):
if ':' in self.name:
_, name = self.name.split(':', 1)
std = cast(StandardDomain, self.env.get_domain('std'))
std = self.env.domains.standard_domain
std.note_object(name, fullname, node_id, location=node)
return ret
@@ -294,7 +295,7 @@ class Cmdoption(ObjectDescription[str]):
self.state.document.note_explicit_target(signode)
domain = self.env.domains['std']
domain = self.env.domains.standard_domain
for optname in signode.get('allnames', []):
domain.add_program_option(currprogram, optname,
self.env.docname, signode['ids'][0])
@@ -365,8 +366,7 @@ def make_glossary_term(env: BuildEnvironment, textnodes: Iterable[Node], index_k
term['ids'].append(node_id)
document.note_explicit_target(term)
std = cast(StandardDomain, env.get_domain('std'))
std._note_term(termtext, node_id, location=term)
env.domains.standard_domain._note_term(termtext, node_id, location=term)
# add an index entry too
indexnode = addnodes.index()
@@ -539,7 +539,7 @@ class ProductionList(SphinxDirective):
option_spec: ClassVar[OptionSpec] = {}
def run(self) -> list[Node]:
domain = cast(StandardDomain, self.env.get_domain('std'))
domain = self.env.domains.standard_domain
node: Element = addnodes.productionlist()
self.set_source_info(node)
# The backslash handling is from ObjectDescription.get_signatures
@@ -766,7 +766,7 @@ class StandardDomain(Domain):
if fn == docname:
del self.anonlabels[key]
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
# XXX duplicates?
for key, data in otherdata['progoptions'].items():
if data[0] in docnames:

View File

@@ -8,9 +8,10 @@ import pickle
from collections import defaultdict
from copy import copy
from os import path
from typing import TYPE_CHECKING, Any, NoReturn
from typing import TYPE_CHECKING
from sphinx import addnodes
from sphinx.domains._domains_container import _DomainsContainer
from sphinx.environment.adapters import toctree as toctree_adapters
from sphinx.errors import (
BuildEnvironmentError,
@@ -29,8 +30,9 @@ from sphinx.util.nodes import is_translatable
from sphinx.util.osutil import _last_modified_time, canon_path, os_path
if TYPE_CHECKING:
from collections.abc import Callable, Iterator
from collections.abc import Callable, Iterable, Iterator
from pathlib import Path
from typing import Any, Literal
from docutils import nodes
from docutils.nodes import Node
@@ -66,7 +68,7 @@ default_settings: dict[str, Any] = {
# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
ENV_VERSION = 63
ENV_VERSION = 64
# config status
CONFIG_UNSET = -1
@@ -87,61 +89,6 @@ versioning_conditions: dict[str, Literal[False] | Callable[[Node], bool]] = {
'text': is_translatable,
}
if TYPE_CHECKING:
from collections.abc import MutableMapping
from typing import Literal, overload
from sphinx.domains.c import CDomain
from sphinx.domains.changeset import ChangeSetDomain
from sphinx.domains.citation import CitationDomain
from sphinx.domains.cpp import CPPDomain
from sphinx.domains.index import IndexDomain
from sphinx.domains.javascript import JavaScriptDomain
from sphinx.domains.math import MathDomain
from sphinx.domains.python import PythonDomain
from sphinx.domains.rst import ReSTDomain
from sphinx.domains.std import StandardDomain
from sphinx.ext.duration import DurationDomain
from sphinx.ext.todo import TodoDomain
class _DomainsType(MutableMapping[str, Domain]):
@overload
def __getitem__(self, key: Literal["c"]) -> CDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["cpp"]) -> CPPDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["changeset"]) -> ChangeSetDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["citation"]) -> CitationDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["index"]) -> IndexDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["js"]) -> JavaScriptDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["math"]) -> MathDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["py"]) -> PythonDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["rst"]) -> ReSTDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["std"]) -> StandardDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["duration"]) -> DurationDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: Literal["todo"]) -> TodoDomain: ... # NoQA: E704
@overload
def __getitem__(self, key: str) -> Domain: ... # NoQA: E704
def __getitem__(self, _key: str) -> Domain: raise NotImplementedError # NoQA: E704
def __setitem__( # NoQA: E301,E704
self, key: str, value: Domain,
) -> NoReturn: raise NotImplementedError
def __delitem__(self, key: str) -> NoReturn: raise NotImplementedError # NoQA: E704
def __iter__(self) -> NoReturn: raise NotImplementedError # NoQA: E704
def __len__(self) -> NoReturn: raise NotImplementedError # NoQA: E704
else:
_DomainsType = dict
class BuildEnvironment:
"""
@@ -150,8 +97,6 @@ class BuildEnvironment:
transformations to resolve links to them.
"""
domains: _DomainsType
# --------- ENVIRONMENT INITIALIZATION -------------------------------------
def __init__(self, app: Sphinx) -> None:
@@ -169,9 +114,6 @@ class BuildEnvironment:
self.versioning_condition: Literal[False] | Callable[[Node], bool] | None = None
self.versioning_compare: bool | None = None
# all the registered domains, set by the application
self.domains = _DomainsType()
# the docutils settings for building
self.settings: dict[str, Any] = default_settings.copy()
self.settings['env'] = self
@@ -276,6 +218,9 @@ class BuildEnvironment:
# objtype index -> (domain, type, objname (localized))
self._search_index_objnames: dict[int, tuple[str, str, str]] = {}
# all the registered domains, set by the application
self.domains: _DomainsContainer = _DomainsContainer._from_environment(self)
# set up environment
self.setup(app)
@@ -283,7 +228,7 @@ class BuildEnvironment:
"""Obtains serializable data for pickling."""
__dict__ = self.__dict__.copy()
# clear unpickable attributes
__dict__.update(app=None, domains={}, events=None)
__dict__.update(app=None, domains=None, events=None)
# clear in-memory doctree caches, to reduce memory consumption and
# ensure that, upon restoring the state, the most recent pickled files
# on the disk are used instead of those from a possibly outdated state
@@ -310,14 +255,12 @@ class BuildEnvironment:
self.project = app.project
self.version = app.registry.get_envversion(app)
# initialize domains
self.domains = _DomainsType()
for domain in app.registry.create_domains(self):
self.domains[domain.name] = domain
# initialise domains
if self.domains is None:
# if we are unpickling an environment, we need to recreate the domains
self.domains = _DomainsContainer._from_environment(self)
# setup domains (must do after all initialization)
for domain in self.domains.values():
domain.setup()
self.domains._setup()
# initialize config
self._update_config(app.config)
@@ -392,25 +335,23 @@ class BuildEnvironment:
self.included.pop(docname, None)
self.reread_always.discard(docname)
for domain in self.domains.values():
domain.clear_doc(docname)
self.domains._clear_doc(docname)
def merge_info_from(self, docnames: list[str], other: BuildEnvironment,
def merge_info_from(self, docnames: Iterable[str], other: BuildEnvironment,
app: Sphinx) -> None:
"""Merge global information gathered about *docnames* while reading them
from the *other* environment.
This possibly comes from a parallel build process.
"""
docnames = set(docnames) # type: ignore[assignment]
docnames = frozenset(docnames)
for docname in docnames:
self.all_docs[docname] = other.all_docs[docname]
self.included[docname] = other.included[docname]
if docname in other.reread_always:
self.reread_always.add(docname)
for domainname, domain in self.domains.items():
domain.merge_domaindata(docnames, other.domaindata[domainname])
self.domains._merge_domain_data(docnames, other.domaindata)
self.events.emit('env-merge-info', self, docnames, other)
def path2doc(self, filename: str | os.PathLike[str]) -> str | None:
@@ -563,8 +504,7 @@ class BuildEnvironment:
self.temp_data['docname'] = docname
# defaults to the global default, but can be re-set in a document
self.temp_data['default_role'] = self.config.default_role
self.temp_data['default_domain'] = \
self.domains.get(self.config.primary_domain)
self.temp_data['default_domain'] = self.domains.get(self.config.primary_domain)
# utilities to use while reading a document
@@ -622,7 +562,8 @@ class BuildEnvironment:
try:
return self.domains[domainname]
except KeyError as exc:
raise ExtensionError(__('Domain %r is not registered') % domainname) from exc
msg = __('Domain %r is not registered') % domainname
raise ExtensionError(msg) from exc
# --------- RESOLVING REFERENCES AND TOCTREES ------------------------------
@@ -760,8 +701,7 @@ class BuildEnvironment:
location=docname)
# call check-consistency for all extensions
for domain in self.domains.values():
domain.check_consistency()
self.domains._check_consistency()
self.events.emit('env-check-consistency', self)

View File

@@ -65,7 +65,7 @@ class IndexEntries:
new: _IndexEntryMap = {}
rel_uri: str | Literal[False]
index_domain = self.env.domains['index']
index_domain = self.env.domains.index_domain
for docname, entries in index_domain.entries.items():
try:
rel_uri = builder.get_relative_uri('genindex', docname)

View File

@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, TypeVar, cast
from docutils import nodes
from sphinx import addnodes
from sphinx.domains.std import StandardDomain
from sphinx.environment.adapters.toctree import note_toctree
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.locale import __
@@ -260,7 +261,7 @@ class TocTreeCollector(EnvironmentCollector):
def assign_figure_numbers(self, env: BuildEnvironment) -> list[str]:
"""Assign a figure number to each figure under a numbered toctree."""
generated_docnames = frozenset(env.domains['std']._virtual_doc_names)
generated_docnames = frozenset(env.domains.standard_domain._virtual_doc_names)
rewrite_needed = []
@@ -270,10 +271,9 @@ class TocTreeCollector(EnvironmentCollector):
fignum_counter: dict[str, dict[tuple[int, ...], int]] = {}
def get_figtype(node: Node) -> str | None:
for domain in env.domains.values():
for domain in env.domains.sorted():
figtype = domain.get_enumerable_node_type(node)
if (domain.name == 'std'
and not domain.get_numfig_title(node)): # type: ignore[attr-defined] # NoQA: E501
if isinstance(domain, StandardDomain) and not domain.get_numfig_title(node):
# Skip if uncaptioned node
continue

View File

@@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, cast
from docutils import nodes
import sphinx
from sphinx.domains.std import StandardDomain
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.nodes import clean_astext
@@ -31,7 +30,7 @@ def get_node_depth(node: Node) -> int:
def register_sections_as_label(app: Sphinx, document: Node) -> None:
domain = cast(StandardDomain, app.env.get_domain('std'))
domain = app.env.domains.standard_domain
for node in document.findall(nodes.section):
if (app.config.autosectionlabel_maxdepth and
get_node_depth(node) >= app.config.autosectionlabel_maxdepth):

View File

@@ -754,7 +754,7 @@ class AutoLink(SphinxRole):
"""
def run(self) -> tuple[list[Node], list[system_message]]:
pyobj_role = self.env.get_domain('py').role('obj')
pyobj_role = self.env.domains.python_domain.role('obj')
assert pyobj_role is not None
objects, errors = pyobj_role('obj', self.rawtext, self.text, self.lineno,
self.inliner, self.options, self.content)

View File

@@ -205,7 +205,7 @@ class CoverageBuilder(Builder):
def build_c_coverage(self) -> None:
c_objects = {}
for obj in self.env.domains['c'].get_objects():
for obj in self.env.domains.c_domain.get_objects():
c_objects[obj[2]] = obj[1]
for filename in self.c_sourcefiles:
undoc: set[tuple[str, str]] = set()

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
import time
from itertools import islice
from operator import itemgetter
from typing import TYPE_CHECKING, cast
from typing import TYPE_CHECKING
import sphinx
from sphinx.domains import Domain
@@ -13,6 +13,7 @@ from sphinx.locale import __
from sphinx.util import logging
if TYPE_CHECKING:
from collections.abc import Set
from typing import TypedDict
from docutils import nodes
@@ -43,7 +44,7 @@ class DurationDomain(Domain):
def clear_doc(self, docname: str) -> None:
self.reading_durations.pop(docname, None)
def merge_domaindata(self, docnames: list[str], otherdata: _DurationDomainData) -> None: # type: ignore[override]
def merge_domaindata(self, docnames: Set[str], otherdata: _DurationDomainData) -> None: # type: ignore[override]
other_reading_durations = otherdata.get('reading_durations', {})
docnames_set = frozenset(docnames)
for docname, duration in other_reading_durations.items():
@@ -56,7 +57,7 @@ def on_builder_inited(app: Sphinx) -> None:
This clears the results of the last build.
"""
domain = cast(DurationDomain, app.env.get_domain('duration'))
domain = app.env.domains['duration']
domain.clear()
@@ -69,13 +70,13 @@ def on_doctree_read(app: Sphinx, doctree: nodes.document) -> None:
"""Record a reading duration."""
started_at = app.env.temp_data['started_at']
duration = time.monotonic() - started_at
domain = cast(DurationDomain, app.env.get_domain('duration'))
domain = app.env.domains['duration']
domain.note_reading_duration(duration)
def on_build_finished(app: Sphinx, error: Exception) -> None:
"""Display duration ranking on the current build."""
domain = cast(DurationDomain, app.env.get_domain('duration'))
domain = app.env.domains['duration']
if not domain.reading_durations:
return
durations = sorted(domain.reading_durations.items(), key=itemgetter(1), reverse=True)

View File

@@ -359,7 +359,7 @@ class InheritanceDiagram(SphinxDirective):
node = inheritance_diagram()
node.document = self.state.document
class_names = self.arguments[0].split()
class_role = self.env.get_domain('py').role('class')
class_role = self.env.domains.python_domain.role('class')
# Store the original content for use as a hash
node['parts'] = self.options.get('parts', 0)
node['content'] = ', '.join(class_names)

View File

@@ -162,8 +162,8 @@ def _resolve_reference(env: BuildEnvironment,
typ = node['reftype']
if typ == 'any':
for domain_name, domain in env.domains.items():
if honor_disabled_refs and f'{domain_name}:*' in intersphinx_disabled_reftypes:
for domain in env.domains.sorted():
if honor_disabled_refs and f'{domain.name}:*' in intersphinx_disabled_reftypes:
continue
objtypes: Iterable[str] = domain.object_types.keys()
res = _resolve_reference_in_domain(env, inv_name, inventory,
@@ -302,16 +302,17 @@ class IntersphinxRole(SphinxRole):
if domain_name is not None:
# the user specified a domain, so we only check that
if (domain := self.env.domains.get(domain_name)) is None:
if domain_name not in self.env.domains:
self._emit_warning(
__('domain for external cross-reference not found: %r'), domain_name
)
return [], []
if (role_func := domain.roles.get(role_name)) is None:
domain = self.env.domains[domain_name]
role_func = domain.roles.get(role_name)
if role_func is None:
msg = 'role for external cross-reference not found in domain %r: %r'
if (
object_types := domain.object_types.get(role_name)
) is not None and object_types.roles:
object_types = domain.object_types.get(role_name)
if object_types is not None and object_types.roles:
self._emit_warning(
__(f'{msg} (perhaps you meant one of: %s)'),
domain_name,
@@ -329,7 +330,7 @@ class IntersphinxRole(SphinxRole):
if default_domain := self.env.temp_data.get('default_domain'):
domains.append(default_domain)
if (
std_domain := self.env.domains.get('std')
std_domain := self.env.domains.standard_domain
) is not None and std_domain not in domains:
domains.append(std_domain)

View File

@@ -14,7 +14,6 @@ from docutils import nodes
import sphinx
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.domains.math import MathDomain
from sphinx.errors import ExtensionError
from sphinx.locale import _
from sphinx.util.math import get_node_equation_number
@@ -82,7 +81,7 @@ def install_mathjax(app: Sphinx, pagename: str, templatename: str, context: dict
msg = 'mathjax_path config value must be set for the mathjax extension to work'
raise ExtensionError(msg)
domain = cast(MathDomain, app.env.get_domain('math'))
domain = app.env.domains.math_domain
builder = cast(StandaloneHTMLBuilder, app.builder)
if app.registry.html_assets_policy == 'always' or domain.has_equations(pagename):
# Enable mathjax only if equations exists

View File

@@ -24,6 +24,8 @@ from sphinx.util import logging, texescape
from sphinx.util.docutils import SphinxDirective, new_document
if TYPE_CHECKING:
from collections.abc import Set
from docutils.nodes import Element, Node
from sphinx.application import Sphinx
@@ -87,7 +89,7 @@ class TodoDomain(Domain):
def clear_doc(self, docname: str) -> None:
self.todos.pop(docname, None)
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
for docname in docnames:
self.todos[docname] = otherdata['todos'][docname]
@@ -125,7 +127,7 @@ class TodoListProcessor:
self.builder = app.builder
self.config = app.config
self.env = app.env
self.domain = cast(TodoDomain, app.env.get_domain('todo'))
self.domain = app.env.domains['todo']
self.document = new_document('')
self.process(doctree, docname)

View File

@@ -336,9 +336,9 @@ class IndexBuilder:
rv: dict[str, list[tuple[int, int, int, str, str]]] = {}
otypes = self._objtypes
onames = self._objnames
for domainname, domain in sorted(self.env.domains.items()):
for fullname, dispname, type, docname, anchor, prio in \
sorted(domain.get_objects()):
for domain in self.env.domains.sorted():
sorted_objects = sorted(domain.get_objects())
for fullname, dispname, type, docname, anchor, prio in sorted_objects:
if docname not in fn2index:
continue
if prio < 0:
@@ -348,17 +348,17 @@ class IndexBuilder:
prefix, _, name = dispname.rpartition('.')
plist = rv.setdefault(prefix, [])
try:
typeindex = otypes[domainname, type]
typeindex = otypes[domain.name, type]
except KeyError:
typeindex = len(otypes)
otypes[domainname, type] = typeindex
otypes[domain.name, type] = typeindex
otype = domain.object_types.get(type)
if otype:
# use str() to fire translation proxies
onames[typeindex] = (domainname, type,
onames[typeindex] = (domain.name, type,
str(domain.get_type_name(otype)))
else:
onames[typeindex] = (domainname, type, type)
onames[typeindex] = (domain.name, type, type)
if anchor == fullname:
shortanchor = ''
elif anchor == type + '-' + fullname:

View File

@@ -123,6 +123,8 @@ class SphinxTestApp(sphinx.application.Sphinx):
# unknown keyword arguments
**extras: Any,
) -> None:
self._builder_name = buildername
assert srcdir is not None
if verbosity == -1:
@@ -209,7 +211,7 @@ class SphinxTestApp(sphinx.application.Sphinx):
os.remove(self.docutils_conf_path)
def __repr__(self) -> str:
return f'<{self.__class__.__name__} buildername={self.builder.name!r}>'
return f'<{self.__class__.__name__} buildername={self._builder_name!r}>'
def build(self, force_all: bool = False, filenames: list[str] | None = None) -> None:
self.env._pickled_doctree_cache.clear()

View File

@@ -208,7 +208,7 @@ class AutoNumbering(SphinxTransform):
default_priority = 210
def apply(self, **kwargs: Any) -> None:
domain: StandardDomain = self.env.domains['std']
domain: StandardDomain = self.env.domains.standard_domain
for node in self.document.findall(nodes.Element):
if (domain.is_enumerable_node(node) and

View File

@@ -122,7 +122,7 @@ class ReferencesResolver(SphinxPostTransform):
self, refdoc: str, node: pending_xref, contnode: Element,
) -> Element | None:
"""Resolve reference generated by the "any" role."""
stddomain = self.env.get_domain('std')
stddomain = self.env.domains.standard_domain
target = node['reftarget']
results: list[tuple[str, Element]] = []
# first, try resolving as :doc:
@@ -133,7 +133,7 @@ class ReferencesResolver(SphinxPostTransform):
# next, do the standard domain (makes this a priority)
results.extend(stddomain.resolve_any_xref(self.env, refdoc, self.app.builder,
target, node, contnode))
for domain in self.env.domains.values():
for domain in self.env.domains.sorted():
if domain.name == 'std':
continue # we did this one already
try:

View File

@@ -34,8 +34,7 @@ class SphinxDomains(SphinxTransform):
default_priority = 850
def apply(self, **kwargs: Any) -> None:
for domain in self.env.domains.values():
domain.process_doc(self.env, self.env.docname, self.document)
self.env.domains._process_doc(self.env, self.env.docname, self.document)
def setup(app: Sphinx) -> ExtensionMetadata:

View File

@@ -268,7 +268,7 @@ class sphinx_domains(CustomReSTDispatcher):
return element, []
# always look in the std domain
element = getattr(self.env.get_domain('std'), type)(name)
element = getattr(self.env.domains.standard_domain, type)(name)
if element is not None:
return element, []

View File

@@ -194,18 +194,18 @@ class InventoryFile:
# body
compressor = zlib.compressobj(9)
for domainname, domain in sorted(env.domains.items()):
for name, dispname, typ, docname, anchor, prio in \
sorted(domain.get_objects()):
if anchor.endswith(name):
for domain in env.domains.sorted():
sorted_objects = sorted(domain.get_objects())
for fullname, dispname, type, docname, anchor, prio in sorted_objects:
if anchor.endswith(fullname):
# this can shorten the inventory by as much as 25%
anchor = anchor[:-len(name)] + '$'
anchor = anchor.removesuffix(fullname) + '$'
uri = builder.get_target_uri(docname)
if anchor:
uri += '#' + anchor
if dispname == name:
if dispname == fullname:
dispname = '-'
entry = ('%s %s:%s %s %s %s\n' %
(name, domainname, typ, prio, uri, dispname))
(fullname, domain.name, type, prio, uri, dispname))
f.write(compressor.compress(entry.encode()))
f.write(compressor.flush())

View File

@@ -402,7 +402,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): # type: ignore[misc]
self.body.append(prefix % '.'.join(map(str, numbers)) + ' ')
self.body.append('</span>')
figtype = self.builder.env.domains['std'].get_enumerable_node_type(node)
figtype = self.builder.env.domains.standard_domain.get_enumerable_node_type(node)
if figtype:
if len(node['ids']) == 0:
msg = __('Any IDs not assigned for %s node') % node.tagname

View File

@@ -15,7 +15,6 @@ from typing import TYPE_CHECKING, Any, cast
from docutils import nodes, writers
from sphinx import addnodes, highlighting
from sphinx.domains.std import StandardDomain
from sphinx.errors import SphinxError
from sphinx.locale import _, __, admonitionlabels
from sphinx.util import logging, texescape
@@ -507,8 +506,7 @@ class LaTeXTranslator(SphinxTranslator):
indices_config = frozenset(indices_config)
else:
check_names = False
for domain_name in sorted(self.builder.env.domains):
domain = self.builder.env.domains[domain_name]
for domain in self.builder.env.domains.sorted():
for index_cls in domain.indices:
index_name = f'{domain.name}-{index_cls.name}'
if check_names and index_name not in indices_config:
@@ -1682,7 +1680,7 @@ class LaTeXTranslator(SphinxTranslator):
while isinstance(next_node, nodes.target):
next_node = next_node.next_node(ascend=True)
domain = cast(StandardDomain, self.builder.env.get_domain('std'))
domain = self.builder.env.domains.standard_domain
if isinstance(next_node, HYPERLINK_SUPPORT_NODES):
return
if domain.get_enumerable_node_type(next_node) and domain.get_numfig_title(next_node):

View File

@@ -11,7 +11,6 @@ from typing import TYPE_CHECKING, Any, cast
from docutils import nodes, writers
from sphinx import __display_version__, addnodes
from sphinx.domains.index import IndexDomain
from sphinx.errors import ExtensionError
from sphinx.locale import _, __, admonitionlabels
from sphinx.util import logging
@@ -482,8 +481,7 @@ class TexinfoTranslator(SphinxTranslator):
indices_config = frozenset(indices_config)
else:
check_names = False
for domain_name in sorted(self.builder.env.domains):
domain = self.builder.env.domains[domain_name]
for domain in self.builder.env.domains.sorted():
for index_cls in domain.indices:
index_name = f'{domain.name}-{index_cls.name}'
if check_names and index_name not in indices_config:
@@ -496,7 +494,7 @@ class TexinfoTranslator(SphinxTranslator):
generate(content, collapsed),
))
# only add the main Index if it's not empty
domain = cast(IndexDomain, self.builder.env.get_domain('index'))
domain = self.builder.env.domains.index_domain
for docname in self.builder.docnames:
if domain.entries[docname]:
self.indices.append((_('Index'), '\n@printindex ge\n'))

View File

@@ -1 +1 @@
Search.setIndex({"alltitles": {}, "docnames": ["index"], "envversion": {"sphinx": 63, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {"sphinx (c++ class)": [[0, "_CPPv46Sphinx", false]]}, "objects": {"": [[0, 0, 1, "_CPPv46Sphinx", "Sphinx"]]}, "objnames": {"0": ["cpp", "class", "C++ class"]}, "objtypes": {"0": "cpp:class"}, "terms": {"The": 0, "becaus": 0, "c": 0, "can": 0, "cardin": 0, "challeng": 0, "charact": 0, "class": 0, "descript": 0, "drop": 0, "engin": 0, "fixtur": 0, "frequent": 0, "gener": 0, "i": 0, "index": 0, "inflat": 0, "mathemat": 0, "occur": 0, "often": 0, "project": 0, "punctuat": 0, "queri": 0, "relat": 0, "sampl": 0, "search": 0, "size": 0, "sphinx": 0, "term": 0, "thei": 0, "thi": 0, "token": 0, "us": 0, "web": 0, "would": 0}, "titles": ["&lt;no title&gt;"], "titleterms": {}})
Search.setIndex({"alltitles": {}, "docnames": ["index"], "envversion": {"sphinx": 64, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {"sphinx (c++ class)": [[0, "_CPPv46Sphinx", false]]}, "objects": {"": [[0, 0, 1, "_CPPv46Sphinx", "Sphinx"]]}, "objnames": {"0": ["cpp", "class", "C++ class"]}, "objtypes": {"0": "cpp:class"}, "terms": {"The": 0, "becaus": 0, "c": 0, "can": 0, "cardin": 0, "challeng": 0, "charact": 0, "class": 0, "descript": 0, "drop": 0, "engin": 0, "fixtur": 0, "frequent": 0, "gener": 0, "i": 0, "index": 0, "inflat": 0, "mathemat": 0, "occur": 0, "often": 0, "project": 0, "punctuat": 0, "queri": 0, "relat": 0, "sampl": 0, "search": 0, "size": 0, "sphinx": 0, "term": 0, "thei": 0, "thi": 0, "token": 0, "us": 0, "web": 0, "would": 0}, "titles": ["&lt;no title&gt;"], "titleterms": {}})

View File

@@ -1 +1 @@
Search.setIndex({"alltitles": {"Main Page": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 63, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"At": 0, "adjac": 0, "all": 0, "an": 0, "appear": 0, "applic": 0, "ar": 0, "built": 0, "can": 0, "check": 0, "contain": 0, "do": 0, "document": 0, "doesn": 0, "each": 0, "fixtur": 0, "format": 0, "function": 0, "futur": 0, "html": 0, "i": 0, "includ": 0, "match": 0, "messag": 0, "multipl": 0, "multiterm": 0, "order": 0, "other": 0, "output": 0, "perform": 0, "perhap": 0, "phrase": 0, "project": 0, "queri": 0, "requir": 0, "same": 0, "search": 0, "successfulli": 0, "support": 0, "t": 0, "term": 0, "test": 0, "thi": 0, "time": 0, "us": 0, "when": 0, "write": 0}, "titles": ["Main Page"], "titleterms": {"main": 0, "page": 0}})
Search.setIndex({"alltitles": {"Main Page": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 64, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"At": 0, "adjac": 0, "all": 0, "an": 0, "appear": 0, "applic": 0, "ar": 0, "built": 0, "can": 0, "check": 0, "contain": 0, "do": 0, "document": 0, "doesn": 0, "each": 0, "fixtur": 0, "format": 0, "function": 0, "futur": 0, "html": 0, "i": 0, "includ": 0, "match": 0, "messag": 0, "multipl": 0, "multiterm": 0, "order": 0, "other": 0, "output": 0, "perform": 0, "perhap": 0, "phrase": 0, "project": 0, "queri": 0, "requir": 0, "same": 0, "search": 0, "successfulli": 0, "support": 0, "t": 0, "term": 0, "test": 0, "thi": 0, "time": 0, "us": 0, "when": 0, "write": 0}, "titles": ["Main Page"], "titleterms": {"main": 0, "page": 0}})

View File

@@ -1 +1 @@
Search.setIndex({"alltitles": {"sphinx_utils module": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 63, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"ar": 0, "both": 0, "built": 0, "confirm": 0, "document": 0, "function": 0, "html": 0, "i": 0, "includ": 0, "input": 0, "javascript": 0, "match": 0, "partial": 0, "possibl": 0, "project": 0, "provid": 0, "restructuredtext": 0, "sampl": 0, "search": 0, "should": 0, "term": 0, "thi": 0, "titl": 0, "us": 0, "when": 0}, "titles": ["sphinx_utils module"], "titleterms": {"modul": 0, "sphinx_util": 0}})
Search.setIndex({"alltitles": {"sphinx_utils module": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 64, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"ar": 0, "both": 0, "built": 0, "confirm": 0, "document": 0, "function": 0, "html": 0, "i": 0, "includ": 0, "input": 0, "javascript": 0, "match": 0, "partial": 0, "possibl": 0, "project": 0, "provid": 0, "restructuredtext": 0, "sampl": 0, "search": 0, "should": 0, "term": 0, "thi": 0, "titl": 0, "us": 0, "when": 0}, "titles": ["sphinx_utils module"], "titleterms": {"modul": 0, "sphinx_util": 0}})

View File

@@ -1 +1 @@
Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]], "Result Scoring": [[0, "result-scoring"]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 63, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "index-1", false], [0, "module-relevance", false]], "relevance (relevance.example attribute)": [[0, "relevance.Example.relevance", false]], "scoring": [[0, "index-0", true]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]], "relevance.Example": [[0, 2, 1, "", "relevance"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "attribute", "Python attribute"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:attribute"}, "terms": {"": [0, 1], "A": 1, "By": 0, "For": [0, 1], "In": [0, 1], "against": 0, "align": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "assign": 0, "attempt": 0, "attribut": 0, "both": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "collect": 0, "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": [0, 1], "docstr": 0, "document": [0, 1], "domain": 1, "dure": 0, "engin": 0, "evalu": 0, "exampl": [0, 1], "extract": 0, "feedback": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": [0, 1], "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": [0, 1], "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "mani": 0, "match": 0, "mention": 1, "more": 0, "name": [0, 1], "numer": 0, "object": 0, "often": 0, "one": [0, 1], "onli": [0, 1], "order": 0, "other": 0, "over": 0, "page": 1, "part": 1, "particular": 0, "present": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "retriev": 0, "sai": 0, "same": 1, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": [0, 1], "thei": 0, "them": 0, "thi": 0, "time": 0, "titl": 0, "two": 0, "typic": 0, "us": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "which": 0, "within": 0, "word": 0, "would": [0, 1]}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1], "result": 0, "score": 0}})
Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]], "Result Scoring": [[0, "result-scoring"]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 64, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "index-1", false], [0, "module-relevance", false]], "relevance (relevance.example attribute)": [[0, "relevance.Example.relevance", false]], "scoring": [[0, "index-0", true]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]], "relevance.Example": [[0, 2, 1, "", "relevance"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "attribute", "Python attribute"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:attribute"}, "terms": {"": [0, 1], "A": 1, "By": 0, "For": [0, 1], "In": [0, 1], "against": 0, "align": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "assign": 0, "attempt": 0, "attribut": 0, "both": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "collect": 0, "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": [0, 1], "docstr": 0, "document": [0, 1], "domain": 1, "dure": 0, "engin": 0, "evalu": 0, "exampl": [0, 1], "extract": 0, "feedback": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": [0, 1], "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": [0, 1], "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "mani": 0, "match": 0, "mention": 1, "more": 0, "name": [0, 1], "numer": 0, "object": 0, "often": 0, "one": [0, 1], "onli": [0, 1], "order": 0, "other": 0, "over": 0, "page": 1, "part": 1, "particular": 0, "present": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "retriev": 0, "sai": 0, "same": 1, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": [0, 1], "thei": 0, "them": 0, "thi": 0, "time": 0, "titl": 0, "two": 0, "typic": 0, "us": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "which": 0, "within": 0, "word": 0, "would": [0, 1]}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1], "result": 0, "score": 0}})

View File

@@ -804,7 +804,7 @@ def test_domain_c_build_field_role(app):
def _get_obj(app, queryName):
domain = app.env.get_domain('c')
domain = app.env.domains.c_domain
for name, _dispname, objectType, docname, anchor, _prio in domain.get_objects():
if name == queryName:
return docname, anchor, objectType

View File

@@ -102,8 +102,8 @@ def test_domain_js_xrefs(app):
def test_domain_js_objects(app):
app.build(force_all=True)
modules = app.env.domains['js'].data['modules']
objects = app.env.domains['js'].data['objects']
modules = app.env.domains.javascript_domain.data['modules']
objects = app.env.domains.javascript_domain.data['objects']
assert 'module_a.submodule' in modules
assert 'module_a.submodule' in objects
@@ -131,7 +131,7 @@ def test_domain_js_objects(app):
@pytest.mark.sphinx('dummy', testroot='domain-js')
def test_domain_js_find_obj(app):
def find_obj(mod_name, prefix, obj_name, obj_type, searchmode=0):
return app.env.domains['js'].find_obj(
return app.env.domains.javascript_domain.find_obj(
app.env, mod_name, prefix, obj_name, obj_type, searchmode
)

View File

@@ -206,8 +206,8 @@ def test_domain_py_xrefs_abbreviations(app):
def test_domain_py_objects(app):
app.build(force_all=True)
modules = app.env.domains['py'].data['modules']
objects = app.env.domains['py'].data['objects']
modules = app.env.domains.python_domain.data['modules']
objects = app.env.domains.python_domain.data['objects']
assert 'module_a.submodule' in modules
assert 'module_a.submodule' in objects
@@ -264,7 +264,7 @@ def test_resolve_xref_for_properties(app):
@pytest.mark.sphinx('dummy', testroot='domain-py')
def test_domain_py_find_obj(app):
def find_obj(modname, prefix, obj_name, obj_type, searchmode=0):
return app.env.domains['py'].find_obj(
return app.env.domains.python_domain.find_obj(
app.env, modname, prefix, obj_name, obj_type, searchmode
)
@@ -569,7 +569,7 @@ def test_module_index(app):
'.. py:module:: sphinx_intl\n'
)
restructuredtext.parse(app, text)
index = PythonModuleIndex(app.env.get_domain('py'))
index = PythonModuleIndex(app.env.domains.python_domain)
assert index.generate() == (
[
('d', [IndexEntry('docutils', 0, 'index', 'module-docutils', '', '', '')]),
@@ -612,7 +612,7 @@ def test_module_index(app):
def test_module_index_submodule(app):
text = '.. py:module:: sphinx.config\n'
restructuredtext.parse(app, text)
index = PythonModuleIndex(app.env.get_domain('py'))
index = PythonModuleIndex(app.env.domains.python_domain)
assert index.generate() == (
[
(
@@ -633,7 +633,7 @@ def test_module_index_submodule(app):
def test_module_index_not_collapsed(app):
text = '.. py:module:: docutils\n.. py:module:: sphinx\n'
restructuredtext.parse(app, text)
index = PythonModuleIndex(app.env.get_domain('py'))
index = PythonModuleIndex(app.env.domains.python_domain)
assert index.generate() == (
[
('d', [IndexEntry('docutils', 0, 'index', 'module-docutils', '', '', '')]),
@@ -659,7 +659,7 @@ def test_modindex_common_prefix(app):
'.. py:module:: sphinx_intl\n'
)
restructuredtext.parse(app, text)
index = PythonModuleIndex(app.env.get_domain('py'))
index = PythonModuleIndex(app.env.domains.python_domain)
assert index.generate() == (
[
(

View File

@@ -38,7 +38,7 @@ def test_domain_py_canonical(app):
@pytest.mark.sphinx('html', testroot='root')
def test_canonical(app):
text = '.. py:class:: io.StringIO\n :canonical: _io.StringIO'
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -75,7 +75,7 @@ def test_canonical_definition_overrides(app):
restructuredtext.parse(app, text)
assert app.warning.getvalue() == ''
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
assert domain.objects['_io.StringIO'] == ('index', 'id0', 'class', False)
@@ -90,7 +90,7 @@ def test_canonical_definition_skip(app):
restructuredtext.parse(app, text)
assert app.warning.getvalue() == ''
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', False)

View File

@@ -36,7 +36,7 @@ def test_pyfunction(app):
'.. py:function:: func2\n'
' :async:\n'
)
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,

View File

@@ -199,7 +199,7 @@ def test_pyobject_prefix(app):
@pytest.mark.sphinx('html', testroot='root')
def test_pydata(app):
text = '.. py:module:: example\n.. py:data:: var\n :type: int\n'
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -238,7 +238,7 @@ def test_pydata(app):
@pytest.mark.sphinx('html', testroot='root')
def test_pyclass_options(app):
text = '.. py:class:: Class1\n.. py:class:: Class2\n :final:\n'
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -313,7 +313,7 @@ def test_pymethod_options(app):
' .. py:method:: meth6\n'
' :final:\n'
)
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -486,7 +486,7 @@ def test_pymethod_options(app):
@pytest.mark.sphinx('html', testroot='root')
def test_pyclassmethod(app):
text = '.. py:class:: Class\n\n .. py:classmethod:: meth\n'
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -533,7 +533,7 @@ def test_pyclassmethod(app):
@pytest.mark.sphinx('html', testroot='root')
def test_pystaticmethod(app):
text = '.. py:class:: Class\n\n .. py:staticmethod:: meth\n'
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -586,7 +586,7 @@ def test_pyattribute(app):
' :type: Optional[str]\n'
" :value: ''\n"
)
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -664,7 +664,7 @@ def test_pyproperty(app):
' :classmethod:\n'
' :type: str\n'
)
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -761,7 +761,7 @@ def test_py_type_alias(app):
' .. py:type:: Alias2\n'
' :canonical: int\n'
)
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -891,7 +891,7 @@ def test_domain_py_type_alias(app):
@pytest.mark.sphinx('html', testroot='root')
def test_pydecorator_signature(app):
text = '.. py:decorator:: deco'
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -922,7 +922,7 @@ def test_pydecorator_signature(app):
@pytest.mark.sphinx('html', testroot='root')
def test_pydecoratormethod_signature(app):
text = '.. py:decoratormethod:: deco'
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -968,7 +968,7 @@ def test_pycurrentmodule(app):
' .. py:method:: m3\n'
' .. py:method:: m4\n'
)
domain = app.env.get_domain('py')
domain = app.env.domains.python_domain
doctree = restructuredtext.parse(app, text)
print(doctree)
assert_node(

View File

@@ -110,7 +110,7 @@ def test_cmd_option_with_optional_value(app):
entries=[('pair', 'command line option; -j', 'cmdoption-j', '', None)],
)
objects = list(app.env.get_domain('std').get_objects())
objects = list(app.env.domains.standard_domain.get_objects())
assert ('-j', '-j', 'cmdoption', 'index', 'cmdoption-j', 1) in objects
@@ -134,7 +134,7 @@ def test_cmd_option_starting_with_bracket(app):
],
),
)
objects = list(app.env.get_domain('std').get_objects())
objects = list(app.env.domains.standard_domain.get_objects())
assert (
'[enable',
'[enable',
@@ -209,7 +209,7 @@ def test_glossary(app):
assert_node(doctree[0][0][2][1], [nodes.definition, nodes.paragraph, 'description'])
# index
domain = app.env.get_domain('std')
domain = app.env.domains.standard_domain
objects = list(domain.get_objects())
assert ('term1', 'term1', 'term', 'index', 'term-term1', -1) in objects
assert ('TERM2', 'TERM2', 'term', 'index', 'term-TERM2', -1) in objects
@@ -375,7 +375,7 @@ def test_glossary_sorted(app):
def test_glossary_alphanumeric(app):
text = '.. glossary::\n\n 1\n /\n'
restructuredtext.parse(app, text)
objects = list(app.env.get_domain('std').get_objects())
objects = list(app.env.domains.standard_domain.get_objects())
assert ('1', '1', 'term', 'index', 'term-1', -1) in objects
assert ('/', '/', 'term', 'index', 'term-0', -1) in objects
@@ -384,14 +384,14 @@ def test_glossary_alphanumeric(app):
def test_glossary_conflicted_labels(app):
text = '.. _term-foo:\n.. glossary::\n\n foo\n'
restructuredtext.parse(app, text)
objects = list(app.env.get_domain('std').get_objects())
objects = list(app.env.domains.standard_domain.get_objects())
assert ('foo', 'foo', 'term', 'index', 'term-0', -1) in objects
@pytest.mark.sphinx('html', testroot='root')
def test_cmdoption(app):
text = '.. program:: ls\n\n.. option:: -l\n'
domain = app.env.get_domain('std')
domain = app.env.domains.standard_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -418,7 +418,7 @@ def test_cmdoption(app):
@pytest.mark.sphinx('html', testroot='root')
def test_cmdoption_for_None(app):
text = '.. program:: ls\n.. program:: None\n\n.. option:: -l\n'
domain = app.env.get_domain('std')
domain = app.env.domains.standard_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -445,7 +445,7 @@ def test_cmdoption_for_None(app):
@pytest.mark.sphinx('html', testroot='root')
def test_multiple_cmdoptions(app):
text = '.. program:: cmd\n\n.. option:: -o directory, --output directory\n'
domain = app.env.get_domain('std')
domain = app.env.domains.standard_domain
doctree = restructuredtext.parse(app, text)
assert_node(
doctree,
@@ -577,7 +577,7 @@ def test_labeled_rubric(app):
text = '.. _label:\n.. rubric:: blah *blah* blah\n'
restructuredtext.parse(app, text)
domain = app.env.get_domain('std')
domain = app.env.domains.standard_domain
assert 'label' in domain.labels
assert domain.labels['label'] == ('index', 'label', 'blah blah blah')
@@ -598,7 +598,7 @@ def test_labeled_definition(app):
)
restructuredtext.parse(app, text)
domain = app.env.get_domain('std')
domain = app.env.domains.standard_domain
assert 'label1' in domain.labels
assert domain.labels['label1'] == ('index', 'label1', 'Foo blah blah blah')
assert 'label2' in domain.labels
@@ -621,7 +621,7 @@ def test_labeled_field(app):
)
restructuredtext.parse(app, text)
domain = app.env.get_domain('std')
domain = app.env.domains.standard_domain
assert 'label1' in domain.labels
assert domain.labels['label1'] == ('index', 'label1', 'Foo blah blah blah')
assert 'label2' in domain.labels

View File

@@ -130,8 +130,8 @@ def test_object_inventory(app):
False,
)
assert app.env.domains['py'].data is app.env.domaindata['py']
assert app.env.domains['c'].data is app.env.domaindata['c']
assert app.env.domains.python_domain.data is app.env.domaindata['py']
assert app.env.domains.c_domain.data is app.env.domaindata['c']
@pytest.mark.sphinx('dummy', testroot='basic')

View File

@@ -263,7 +263,7 @@ def test_create_index_with_name(app):
assert index[2] == ('S', [('Sphinx', ([('', '#index-0')], [], None))])
# check the reference labels are created correctly
std = app.env.get_domain('std')
std = app.env.domains.standard_domain
assert std.anonlabels['ref1'] == ('index', 'ref1')
assert std.anonlabels['ref2'] == ('index', 'ref2')

View File

@@ -2,7 +2,6 @@
import sys
from io import StringIO
from pathlib import Path
from unittest.mock import Mock, patch
import pytest
@@ -37,7 +36,6 @@ defaults = {
'extensions': ['sphinx.ext.autosummary'],
'autosummary_generate': True,
'autosummary_generate_overwrite': False,
'source_suffix': '.rst',
}
@@ -219,13 +217,10 @@ def str_content(elem):
def test_escaping(app):
app.build(force_all=True)
outdir = Path(app.builder.outdir)
docpage = outdir / 'underscore_module_.xml'
docpage = app.builder.outdir / 'underscore_module_.xml'
assert docpage.exists()
title = etree_parse(docpage).find('section/title')
assert str_content(title) == 'underscore_module_'

View File

@@ -5,6 +5,7 @@ from __future__ import annotations
import json
import warnings
from io import BytesIO
from typing import TYPE_CHECKING
import pytest
from docutils import frontend, utils
@@ -14,6 +15,9 @@ from sphinx.search import IndexBuilder
from tests.utils import TESTS_ROOT
if TYPE_CHECKING:
from collections.abc import Iterator
JAVASCRIPT_TEST_ROOTS = [
directory
for directory in (TESTS_ROOT / 'js' / 'roots').iterdir()
@@ -21,22 +25,32 @@ JAVASCRIPT_TEST_ROOTS = [
]
class DummyDomainsContainer:
def __init__(self, **domains: DummyDomain) -> None:
self._domain_instances = domains
def sorted(self) -> Iterator[DummyDomain]:
for _domain_name, domain in sorted(self._domain_instances.items()):
yield domain
class DummyEnvironment:
def __init__(self, version, domains):
def __init__(self, version: str, domains: DummyDomainsContainer) -> None:
self.version = version
self.domains = domains
def __getattr__(self, name):
def __getattr__(self, name: str):
if name.startswith('_search_index_'):
setattr(self, name, {})
return getattr(self, name, {})
def __str__(self):
def __str__(self) -> str:
return f'DummyEnvironment({self.version!r}, {self.domains!r})'
class DummyDomain:
def __init__(self, data):
def __init__(self, name: str, data: dict) -> None:
self.name = name
self.data = data
self.object_types = {}
@@ -162,15 +176,21 @@ def test_term_in_raw_directive(app):
def test_IndexBuilder():
domain1 = DummyDomain([
('objname1', 'objdispname1', 'objtype1', 'docname1_1', '#anchor', 1),
('objname2', 'objdispname2', 'objtype2', 'docname1_2', '', -1),
])
domain2 = DummyDomain([
('objname1', 'objdispname1', 'objtype1', 'docname2_1', '#anchor', 1),
('objname2', 'objdispname2', 'objtype2', 'docname2_2', '', -1),
])
env = DummyEnvironment('1.0', {'dummy1': domain1, 'dummy2': domain2})
domain1 = DummyDomain(
'dummy1',
[
('objname1', 'objdispname1', 'objtype1', 'docname1_1', '#anchor', 1),
('objname2', 'objdispname2', 'objtype2', 'docname1_2', '', -1),
],
)
domain2 = DummyDomain(
'dummy2',
[
('objname1', 'objdispname1', 'objtype1', 'docname2_1', '#anchor', 1),
('objname2', 'objdispname2', 'objtype2', 'docname2_2', '', -1),
],
)
env = DummyEnvironment('1.0', DummyDomainsContainer(dummy1=domain1, dummy2=domain2))
doc = utils.new_document(b'test data', settings)
doc['file'] = 'dummy'
parser.parse(FILE_CONTENTS, doc)
@@ -258,7 +278,7 @@ def test_IndexBuilder():
1: ('dummy2', 'objtype1', 'objtype1'),
}
env = DummyEnvironment('1.0', {'dummy1': domain1, 'dummy2': domain2})
env = DummyEnvironment('1.0', DummyDomainsContainer(dummy1=domain1, dummy2=domain2))
# dump / load
stream = BytesIO()