mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #4305 from tk0miya/refactor_registry
Refactor registry
This commit is contained in:
commit
a95bc095c0
@ -33,7 +33,6 @@ from sphinx.deprecation import RemovedInSphinx20Warning
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.events import EventManager
|
||||
from sphinx.extension import verify_required_extensions
|
||||
from sphinx.io import SphinxStandaloneReader
|
||||
from sphinx.locale import __
|
||||
from sphinx.registry import SphinxComponentRegistry
|
||||
from sphinx.util import pycompat # noqa: F401
|
||||
@ -54,7 +53,9 @@ if False:
|
||||
from sphinx.domains import Domain, Index # NOQA
|
||||
from sphinx.environment.collectors import EnvironmentCollector # NOQA
|
||||
from sphinx.extension import Extension # NOQA
|
||||
from sphinx.roles import XRefRole # NOQA
|
||||
from sphinx.theming import Theme # NOQA
|
||||
from sphinx.util.typing import RoleFunction # NOQA
|
||||
|
||||
builtin_extensions = (
|
||||
'sphinx.builders.applehelp',
|
||||
@ -120,7 +121,6 @@ class Sphinx(object):
|
||||
self.env = None # type: BuildEnvironment
|
||||
self.registry = SphinxComponentRegistry()
|
||||
self.enumerable_nodes = {} # type: Dict[nodes.Node, Tuple[unicode, Callable]] # NOQA
|
||||
self.post_transforms = [] # type: List[Transform]
|
||||
self.html_themes = {} # type: Dict[unicode, unicode]
|
||||
|
||||
self.srcdir = srcdir
|
||||
@ -444,7 +444,6 @@ class Sphinx(object):
|
||||
|
||||
def add_builder(self, builder):
|
||||
# type: (Type[Builder]) -> None
|
||||
logger.debug('[app] adding builder: %r', builder)
|
||||
self.registry.add_builder(builder)
|
||||
|
||||
def add_config_value(self, name, default, rebuild, types=()):
|
||||
@ -464,7 +463,6 @@ class Sphinx(object):
|
||||
|
||||
def set_translator(self, name, translator_class):
|
||||
# type: (unicode, Type[nodes.NodeVisitor]) -> None
|
||||
logger.info(bold(__('Change of translator for the %s builder.') % name))
|
||||
self.registry.add_translator(name, translator_class)
|
||||
|
||||
def add_node(self, node, **kwds):
|
||||
@ -553,39 +551,30 @@ class Sphinx(object):
|
||||
|
||||
def add_domain(self, domain):
|
||||
# type: (Type[Domain]) -> None
|
||||
logger.debug('[app] adding domain: %r', domain)
|
||||
self.registry.add_domain(domain)
|
||||
|
||||
def override_domain(self, domain):
|
||||
# type: (Type[Domain]) -> None
|
||||
logger.debug('[app] overriding domain: %r', domain)
|
||||
self.registry.override_domain(domain)
|
||||
|
||||
def add_directive_to_domain(self, domain, name, obj,
|
||||
has_content=None, argument_spec=None, **option_spec):
|
||||
# type: (unicode, unicode, Any, bool, Any, Any) -> None
|
||||
logger.debug('[app] adding directive to domain: %r',
|
||||
(domain, name, obj, has_content, argument_spec, option_spec))
|
||||
self.registry.add_directive_to_domain(domain, name, obj,
|
||||
has_content, argument_spec, **option_spec)
|
||||
|
||||
def add_role_to_domain(self, domain, name, role):
|
||||
# type: (unicode, unicode, Any) -> None
|
||||
logger.debug('[app] adding role to domain: %r', (domain, name, role))
|
||||
# type: (unicode, unicode, Union[RoleFunction, XRefRole]) -> None
|
||||
self.registry.add_role_to_domain(domain, name, role)
|
||||
|
||||
def add_index_to_domain(self, domain, index):
|
||||
# type: (unicode, Type[Index]) -> None
|
||||
logger.debug('[app] adding index to domain: %r', (domain, index))
|
||||
self.registry.add_index_to_domain(domain, index)
|
||||
|
||||
def add_object_type(self, directivename, rolename, indextemplate='',
|
||||
parse_node=None, ref_nodeclass=None, objname='',
|
||||
doc_field_types=[]):
|
||||
# type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None
|
||||
logger.debug('[app] adding object type: %r',
|
||||
(directivename, rolename, indextemplate, parse_node,
|
||||
ref_nodeclass, objname, doc_field_types))
|
||||
self.registry.add_object_type(directivename, rolename, indextemplate, parse_node,
|
||||
ref_nodeclass, objname, doc_field_types)
|
||||
|
||||
@ -602,21 +591,16 @@ class Sphinx(object):
|
||||
def add_crossref_type(self, directivename, rolename, indextemplate='',
|
||||
ref_nodeclass=None, objname=''):
|
||||
# type: (unicode, unicode, unicode, nodes.Node, unicode) -> None
|
||||
logger.debug('[app] adding crossref type: %r',
|
||||
(directivename, rolename, indextemplate, ref_nodeclass,
|
||||
objname))
|
||||
self.registry.add_crossref_type(directivename, rolename,
|
||||
indextemplate, ref_nodeclass, objname)
|
||||
|
||||
def add_transform(self, transform):
|
||||
# type: (Type[Transform]) -> None
|
||||
logger.debug('[app] adding transform: %r', transform)
|
||||
SphinxStandaloneReader.transforms.append(transform)
|
||||
self.registry.add_transform(transform)
|
||||
|
||||
def add_post_transform(self, transform):
|
||||
# type: (Type[Transform]) -> None
|
||||
logger.debug('[app] adding post transform: %r', transform)
|
||||
self.post_transforms.append(transform)
|
||||
self.registry.add_post_transform(transform)
|
||||
|
||||
def add_javascript(self, filename):
|
||||
# type: (unicode) -> None
|
||||
@ -677,7 +661,6 @@ class Sphinx(object):
|
||||
|
||||
def add_source_parser(self, suffix, parser):
|
||||
# type: (unicode, Parser) -> None
|
||||
logger.debug('[app] adding search source_parser: %r, %r', suffix, parser)
|
||||
self.registry.add_source_parser(suffix, parser)
|
||||
|
||||
def add_env_collector(self, collector):
|
||||
|
@ -141,7 +141,7 @@ class Domain(object):
|
||||
#: domain label: longer, more descriptive (used in messages)
|
||||
label = ''
|
||||
#: type (usually directive) name -> ObjType instance
|
||||
object_types = {} # type: Dict[unicode, Any]
|
||||
object_types = {} # type: Dict[unicode, ObjType]
|
||||
#: directive name -> directive class
|
||||
directives = {} # type: Dict[unicode, Any]
|
||||
#: role name -> role callable
|
||||
@ -161,6 +161,17 @@ class Domain(object):
|
||||
def __init__(self, env):
|
||||
# type: (BuildEnvironment) -> None
|
||||
self.env = env # type: BuildEnvironment
|
||||
self._role_cache = {} # type: Dict[unicode, Callable]
|
||||
self._directive_cache = {} # type: Dict[unicode, Callable]
|
||||
self._role2type = {} # type: Dict[unicode, List[unicode]]
|
||||
self._type2role = {} # type: Dict[unicode, unicode]
|
||||
|
||||
# convert class variables to instance one (to enhance through API)
|
||||
self.object_types = dict(self.object_types)
|
||||
self.directives = dict(self.directives)
|
||||
self.roles = dict(self.roles)
|
||||
self.indices = list(self.indices)
|
||||
|
||||
if self.name not in env.domaindata:
|
||||
assert isinstance(self.initial_data, dict)
|
||||
new_data = copy.deepcopy(self.initial_data)
|
||||
@ -170,10 +181,6 @@ class Domain(object):
|
||||
self.data = env.domaindata[self.name]
|
||||
if self.data['version'] != self.data_version:
|
||||
raise IOError('data of %r domain out of date' % self.label)
|
||||
self._role_cache = {} # type: Dict[unicode, Callable]
|
||||
self._directive_cache = {} # type: Dict[unicode, Callable]
|
||||
self._role2type = {} # type: Dict[unicode, List[unicode]]
|
||||
self._type2role = {} # type: Dict[unicode, unicode]
|
||||
for name, obj in iteritems(self.object_types):
|
||||
for rolename in obj.roles:
|
||||
self._role2type.setdefault(rolename, []).append(name)
|
||||
@ -181,6 +188,18 @@ class Domain(object):
|
||||
self.objtypes_for_role = self._role2type.get # type: Callable[[unicode], List[unicode]] # NOQA
|
||||
self.role_for_objtype = self._type2role.get # type: Callable[[unicode], unicode]
|
||||
|
||||
def add_object_type(self, name, objtype):
|
||||
# type: (unicode, ObjType) -> None
|
||||
"""Add an object type."""
|
||||
self.object_types[name] = objtype
|
||||
if objtype.roles:
|
||||
self._type2role[name] = objtype.roles[0]
|
||||
else:
|
||||
self._type2role[name] = ''
|
||||
|
||||
for role in objtype.roles:
|
||||
self._role2type.setdefault(role, []).append(name)
|
||||
|
||||
def role(self, name):
|
||||
# type: (unicode) -> Callable
|
||||
"""Return a role adapter function that always gives the registered
|
||||
|
@ -878,7 +878,7 @@ class BuildEnvironment(object):
|
||||
|
||||
transformer = SphinxTransformer(doctree)
|
||||
transformer.set_environment(self)
|
||||
transformer.add_transforms(self.app.post_transforms)
|
||||
transformer.add_transforms(self.app.registry.get_post_transforms())
|
||||
transformer.apply_transforms()
|
||||
finally:
|
||||
self.temp_data = backup
|
||||
|
10
sphinx/io.py
10
sphinx/io.py
@ -80,7 +80,13 @@ class SphinxStandaloneReader(SphinxBaseReader):
|
||||
Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets,
|
||||
HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds,
|
||||
RemoveTranslatableInline, PreserveTranslatableMessages, FilterSystemMessages,
|
||||
RefOnlyBulletListTransform, UnreferencedFootnotesDetector]
|
||||
RefOnlyBulletListTransform, UnreferencedFootnotesDetector
|
||||
] # type: List[Transform]
|
||||
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
# type: (Sphinx, Any, Any) -> None
|
||||
self.transforms = self.transforms + app.registry.get_transforms()
|
||||
SphinxBaseReader.__init__(self, *args, **kwargs) # type: ignore
|
||||
|
||||
|
||||
class SphinxI18nReader(SphinxBaseReader):
|
||||
@ -259,7 +265,7 @@ def read_doc(app, env, filename):
|
||||
# type: (Sphinx, BuildEnvironment, unicode) -> nodes.document
|
||||
"""Parse a document and convert to doctree."""
|
||||
input_class = app.registry.get_source_input(filename)
|
||||
reader = SphinxStandaloneReader()
|
||||
reader = SphinxStandaloneReader(app)
|
||||
source = input_class(app, env, source=None, source_path=filename,
|
||||
encoding=env.config.source_encoding)
|
||||
parser = app.registry.create_source_parser(app, filename)
|
||||
|
@ -24,18 +24,21 @@ from sphinx.parsers import Parser as SphinxParser
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.util import logging
|
||||
from sphinx.util import import_object
|
||||
from sphinx.util.console import bold # type: ignore
|
||||
from sphinx.util.docutils import directive_helper
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Callable, Dict, Iterator, List, Type # NOQA
|
||||
from typing import Any, Callable, Dict, Iterator, List, Type, Union # NOQA
|
||||
from docutils import nodes # NOQA
|
||||
from docutils.io import Input # NOQA
|
||||
from docutils.parsers import Parser # NOQA
|
||||
from docutils.transform import Transform # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
from sphinx.builders import Builder # NOQA
|
||||
from sphinx.domains import Domain, Index # NOQA
|
||||
from sphinx.environment import BuildEnvironment # NOQA
|
||||
from sphinx.util.typing import RoleFunction # NOQA
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -48,14 +51,21 @@ EXTENSION_BLACKLIST = {
|
||||
|
||||
class SphinxComponentRegistry(object):
|
||||
def __init__(self):
|
||||
self.builders = {} # type: Dict[unicode, Type[Builder]]
|
||||
self.domains = {} # type: Dict[unicode, Type[Domain]]
|
||||
self.source_parsers = {} # type: Dict[unicode, Parser]
|
||||
self.source_inputs = {} # type: Dict[unicode, Input]
|
||||
self.translators = {} # type: Dict[unicode, nodes.NodeVisitor]
|
||||
self.builders = {} # type: Dict[unicode, Type[Builder]]
|
||||
self.domains = {} # type: Dict[unicode, Type[Domain]]
|
||||
self.domain_directives = {} # type: Dict[unicode, Dict[unicode, Any]]
|
||||
self.domain_indices = {} # type: Dict[unicode, List[Type[Index]]]
|
||||
self.domain_object_types = {} # type: Dict[unicode, Dict[unicode, ObjType]]
|
||||
self.domain_roles = {} # type: Dict[unicode, Dict[unicode, Union[RoleFunction, XRefRole]]] # NOQA
|
||||
self.post_transforms = [] # type: List[Type[Transform]]
|
||||
self.source_parsers = {} # type: Dict[unicode, Parser]
|
||||
self.source_inputs = {} # type: Dict[unicode, Input]
|
||||
self.translators = {} # type: Dict[unicode, nodes.NodeVisitor]
|
||||
self.transforms = [] # type: List[Type[Transform]]
|
||||
|
||||
def add_builder(self, builder):
|
||||
# type: (Type[Builder]) -> None
|
||||
logger.debug('[app] adding builder: %r', builder)
|
||||
if not hasattr(builder, 'name'):
|
||||
raise ExtensionError(__('Builder class %s has no "name" attribute') % builder)
|
||||
if builder.name in self.builders:
|
||||
@ -87,6 +97,7 @@ class SphinxComponentRegistry(object):
|
||||
|
||||
def add_domain(self, domain):
|
||||
# type: (Type[Domain]) -> None
|
||||
logger.debug('[app] adding domain: %r', domain)
|
||||
if domain.name in self.domains:
|
||||
raise ExtensionError(__('domain %s already registered') % domain.name)
|
||||
self.domains[domain.name] = domain
|
||||
@ -98,10 +109,20 @@ class SphinxComponentRegistry(object):
|
||||
def create_domains(self, env):
|
||||
# type: (BuildEnvironment) -> Iterator[Domain]
|
||||
for DomainClass in itervalues(self.domains):
|
||||
yield DomainClass(env)
|
||||
domain = DomainClass(env)
|
||||
|
||||
# transplant components added by extensions
|
||||
domain.directives.update(self.domain_directives.get(domain.name, {}))
|
||||
domain.roles.update(self.domain_roles.get(domain.name, {}))
|
||||
domain.indices.extend(self.domain_indices.get(domain.name, []))
|
||||
for name, objtype in iteritems(self.domain_object_types.get(domain.name, {})):
|
||||
domain.add_object_type(name, objtype)
|
||||
|
||||
yield domain
|
||||
|
||||
def override_domain(self, domain):
|
||||
# type: (Type[Domain]) -> None
|
||||
logger.debug('[app] overriding domain: %r', domain)
|
||||
if domain.name not in self.domains:
|
||||
raise ExtensionError(__('domain %s not yet registered') % domain.name)
|
||||
if not issubclass(domain, self.domains[domain.name]):
|
||||
@ -112,27 +133,37 @@ class SphinxComponentRegistry(object):
|
||||
def add_directive_to_domain(self, domain, name, obj,
|
||||
has_content=None, argument_spec=None, **option_spec):
|
||||
# type: (unicode, unicode, Any, bool, Any, Any) -> None
|
||||
logger.debug('[app] adding directive to domain: %r',
|
||||
(domain, name, obj, has_content, argument_spec, option_spec))
|
||||
if domain not in self.domains:
|
||||
raise ExtensionError(__('domain %s not yet registered') % domain)
|
||||
directive = directive_helper(obj, has_content, argument_spec, **option_spec)
|
||||
self.domains[domain].directives[name] = directive
|
||||
directives = self.domain_directives.setdefault(domain, {})
|
||||
directives[name] = directive_helper(obj, has_content, argument_spec, **option_spec)
|
||||
|
||||
def add_role_to_domain(self, domain, name, role):
|
||||
# type: (unicode, unicode, Any) -> None
|
||||
# type: (unicode, unicode, Union[RoleFunction, XRefRole]) -> None
|
||||
logger.debug('[app] adding role to domain: %r', (domain, name, role))
|
||||
if domain not in self.domains:
|
||||
raise ExtensionError(__('domain %s not yet registered') % domain)
|
||||
self.domains[domain].roles[name] = role
|
||||
roles = self.domain_roles.setdefault(domain, {})
|
||||
roles[name] = role
|
||||
|
||||
def add_index_to_domain(self, domain, index):
|
||||
# type: (unicode, Type[Index]) -> None
|
||||
logger.debug('[app] adding index to domain: %r', (domain, index))
|
||||
if domain not in self.domains:
|
||||
raise ExtensionError(__('domain %s not yet registered') % domain)
|
||||
self.domains[domain].indices.append(index)
|
||||
indices = self.domain_indices.setdefault(domain, [])
|
||||
indices.append(index)
|
||||
|
||||
def add_object_type(self, directivename, rolename, indextemplate='',
|
||||
parse_node=None, ref_nodeclass=None, objname='',
|
||||
doc_field_types=[]):
|
||||
# type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None
|
||||
logger.debug('[app] adding object type: %r',
|
||||
(directivename, rolename, indextemplate, parse_node,
|
||||
ref_nodeclass, objname, doc_field_types))
|
||||
|
||||
# create a subclass of GenericObject as the new directive
|
||||
directive = type(directivename, # type: ignore
|
||||
(GenericObject, object),
|
||||
@ -140,26 +171,32 @@ class SphinxComponentRegistry(object):
|
||||
'parse_node': staticmethod(parse_node),
|
||||
'doc_field_types': doc_field_types})
|
||||
|
||||
stddomain = self.domains['std']
|
||||
stddomain.directives[directivename] = directive
|
||||
stddomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
|
||||
stddomain.object_types[directivename] = ObjType(objname or directivename, rolename)
|
||||
self.add_directive_to_domain('std', directivename, directive)
|
||||
self.add_role_to_domain('std', rolename, XRefRole(innernodeclass=ref_nodeclass))
|
||||
|
||||
object_types = self.domain_object_types.setdefault('std', {})
|
||||
object_types[directivename] = ObjType(objname or directivename, rolename)
|
||||
|
||||
def add_crossref_type(self, directivename, rolename, indextemplate='',
|
||||
ref_nodeclass=None, objname=''):
|
||||
# type: (unicode, unicode, unicode, nodes.Node, unicode) -> None
|
||||
logger.debug('[app] adding crossref type: %r',
|
||||
(directivename, rolename, indextemplate, ref_nodeclass, objname))
|
||||
|
||||
# create a subclass of Target as the new directive
|
||||
directive = type(directivename, # type: ignore
|
||||
(Target, object),
|
||||
{'indextemplate': indextemplate})
|
||||
|
||||
stddomain = self.domains['std']
|
||||
stddomain.directives[directivename] = directive
|
||||
stddomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
|
||||
stddomain.object_types[directivename] = ObjType(objname or directivename, rolename)
|
||||
self.add_directive_to_domain('std', directivename, directive)
|
||||
self.add_role_to_domain('std', rolename, XRefRole(innernodeclass=ref_nodeclass))
|
||||
|
||||
object_types = self.domain_object_types.setdefault('std', {})
|
||||
object_types[directivename] = ObjType(objname or directivename, rolename)
|
||||
|
||||
def add_source_parser(self, suffix, parser):
|
||||
# type: (unicode, Type[Parser]) -> None
|
||||
logger.debug('[app] adding search source_parser: %r, %r', suffix, parser)
|
||||
if suffix in self.source_parsers:
|
||||
raise ExtensionError(__('source_parser for %r is already registered') % suffix)
|
||||
self.source_parsers[suffix] = parser
|
||||
@ -216,6 +253,7 @@ class SphinxComponentRegistry(object):
|
||||
|
||||
def add_translator(self, name, translator):
|
||||
# type: (unicode, Type[nodes.NodeVisitor]) -> None
|
||||
logger.info(bold(__('Change of translator for the %s builder.') % name))
|
||||
self.translators[name] = translator
|
||||
|
||||
def get_translator_class(self, builder):
|
||||
@ -228,6 +266,24 @@ class SphinxComponentRegistry(object):
|
||||
translator_class = self.get_translator_class(builder)
|
||||
return translator_class(builder, document)
|
||||
|
||||
def add_transform(self, transform):
|
||||
# type: (Type[Transform]) -> None
|
||||
logger.debug('[app] adding transform: %r', transform)
|
||||
self.transforms.append(transform)
|
||||
|
||||
def get_transforms(self):
|
||||
# type: () -> List[Type[Transform]]
|
||||
return self.transforms
|
||||
|
||||
def add_post_transform(self, transform):
|
||||
# type: (Type[Transform]) -> None
|
||||
logger.debug('[app] adding post transform: %r', transform)
|
||||
self.post_transforms.append(transform)
|
||||
|
||||
def get_post_transforms(self):
|
||||
# type: () -> List[Type[Transform]]
|
||||
return self.post_transforms
|
||||
|
||||
def load_extension(self, app, extname):
|
||||
# type: (Sphinx, unicode) -> None
|
||||
"""Load a Sphinx extension."""
|
||||
|
Loading…
Reference in New Issue
Block a user