diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index 4064b7eaf75..f564367d867 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -13,7 +13,7 @@ from sphinx.locale import _ if TYPE_CHECKING: - from collections.abc import Callable, Iterable, Sequence, Set + from collections.abc import Iterable, Sequence, Set from typing import Any from docutils import nodes @@ -108,8 +108,8 @@ class Domain: def __init__(self, env: BuildEnvironment) -> None: self.env: BuildEnvironment = env - self._role_cache: dict[str, Callable] = {} - self._directive_cache: dict[str, Callable] = {} + self._role_cache: dict[str, RoleFunction] = {} + self._directive_cache: dict[str, type[Directive]] = {} self._role2type: dict[str, list[str]] = {} self._type2role: dict[str, str] = {} @@ -181,7 +181,7 @@ def role_adapter( self._role_cache[name] = role_adapter return role_adapter - def directive(self, name: str) -> Callable | None: + def directive(self, name: str) -> type[Directive] | None: """Return a directive adapter class that always gives the registered directive its full name ('domain:name') as ``self.name``. """ diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index faa0695c9fe..5a26bc88e10 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -19,7 +19,7 @@ from docutils.utils import Reporter, unescape from sphinx.errors import SphinxError -from sphinx.locale import _, __ +from sphinx.locale import __ from sphinx.util import logging from sphinx.util.parsing import nested_parse_to_nodes @@ -263,51 +263,44 @@ class sphinx_domains(CustomReSTDispatcher): """ def __init__(self, env: BuildEnvironment) -> None: - self.env = env + self.domains = env.domains + self.current_document = env.current_document super().__init__() - def lookup_domain_element(self, type: str, name: str) -> Any: - """Lookup a markup element (directive or role), given its name which can - be a full name (with domain). - """ - name = name.lower() + def directive( + self, + directive_name: str, + language_module: ModuleType, + document: nodes.document, + ) -> tuple[type[Directive] | None, list[system_message]]: + """Lookup a directive, given its name which can include a domain.""" + directive_name = directive_name.lower() # explicit domain given? - if ':' in name: - domain_name, name = name.split(':', 1) - if domain_name in self.env.domains: - domain = self.env.get_domain(domain_name) - element = getattr(domain, type)(name) + if ':' in directive_name: + domain_name, _, name = directive_name.partition(':') + try: + domain = self.domains[domain_name] + except KeyError: + logger.warning(__('unknown directive name: %s'), directive_name) + else: + element = domain.directive(name) if element is not None: return element, [] - else: - logger.warning( - _('unknown directive or role name: %s:%s'), domain_name, name - ) # else look in the default domain else: - def_domain = self.env.current_document.default_domain - if def_domain is not None: - element = getattr(def_domain, type)(name) + name = directive_name + default_domain = self.current_document.default_domain + if default_domain is not None: + element = default_domain.directive(name) if element is not None: return element, [] # always look in the std domain - element = getattr(self.env.domains.standard_domain, type)(name) + element = self.domains.standard_domain.directive(name) if element is not None: return element, [] - raise ElementLookupError - - def directive( - self, - directive_name: str, - language_module: ModuleType, - document: nodes.document, - ) -> tuple[type[Directive] | None, list[system_message]]: - try: - return self.lookup_domain_element('directive', directive_name) - except ElementLookupError: - return super().directive(directive_name, language_module, document) + return super().directive(directive_name, language_module, document) def role( self, @@ -316,10 +309,34 @@ def role( lineno: int, reporter: Reporter, ) -> tuple[RoleFunction, list[system_message]]: - try: - return self.lookup_domain_element('role', role_name) - except ElementLookupError: - return super().role(role_name, language_module, lineno, reporter) + """Lookup a role, given its name which can include a domain.""" + role_name = role_name.lower() + # explicit domain given? + if ':' in role_name: + domain_name, _, name = role_name.partition(':') + try: + domain = self.domains[domain_name] + except KeyError: + logger.warning(__('unknown role name: %s'), role_name) + else: + element = domain.role(name) + if element is not None: + return element, [] + # else look in the default domain + else: + name = role_name + default_domain = self.current_document.default_domain + if default_domain is not None: + element = default_domain.role(name) + if element is not None: + return element, [] + + # always look in the std domain + element = self.domains.standard_domain.role(name) + if element is not None: + return element, [] + + return super().role(role_name, language_module, lineno, reporter) class WarningStream: