refactor: transplant node handlers for writer on instantiation of translator

For now, node handlers are registered as class methods of translator
classes.  After this change, they are registered as instance method.
This commit is contained in:
Takeshi KOMIYA 2018-02-10 17:24:49 +09:00
parent 18efa1a63a
commit 446eab49b8
3 changed files with 38 additions and 41 deletions

View File

@ -23,7 +23,7 @@ from typing import TYPE_CHECKING
from docutils import nodes
from docutils.parsers.rst import Directive, directives, roles
from six import iteritems, itervalues
from six import itervalues
from six.moves import cStringIO
import sphinx
@ -41,7 +41,7 @@ from sphinx.util import import_object
from sphinx.util import logging
from sphinx.util import pycompat # noqa: F401
from sphinx.util.console import bold # type: ignore
from sphinx.util.docutils import is_html5_writer_available, directive_helper
from sphinx.util.docutils import directive_helper
from sphinx.util.i18n import find_catalog_source_files
from sphinx.util.osutil import abspath, ensuredir
from sphinx.util.tags import Tags
@ -618,39 +618,7 @@ class Sphinx(object):
self._setting_up_extension, node.__name__,
type='app', subtype='add_node')
nodes._add_node_class_names([node.__name__])
for key, val in iteritems(kwds):
try:
visit, depart = val
except ValueError:
raise ExtensionError(__('Value for key %r must be a '
'(visit, depart) function tuple') % key)
translator = self.registry.translators.get(key)
translators = []
if translator is not None:
translators.append(translator)
elif key == 'html':
from sphinx.writers.html import HTMLTranslator
translators.append(HTMLTranslator)
if is_html5_writer_available():
from sphinx.writers.html5 import HTML5Translator
translators.append(HTML5Translator)
elif key == 'latex':
from sphinx.writers.latex import LaTeXTranslator
translators.append(LaTeXTranslator)
elif key == 'text':
from sphinx.writers.text import TextTranslator
translators.append(TextTranslator)
elif key == 'man':
from sphinx.writers.manpage import ManualPageTranslator
translators.append(ManualPageTranslator)
elif key == 'texinfo':
from sphinx.writers.texinfo import TexinfoTranslator
translators.append(TexinfoTranslator)
for translator in translators:
setattr(translator, 'visit_' + node.__name__, visit)
if depart:
setattr(translator, 'depart_' + node.__name__, depart)
self.registry.add_translation_handlers(node, **kwds)
def add_enumerable_node(self, node, figtype, title_getter=None, **kwds):
# type: (nodes.Node, unicode, TitleGetter, Any) -> None

View File

@ -127,9 +127,7 @@ class Builder(object):
This method returns an instance of ``default_translator_class`` by default.
Users can replace the translator class with ``app.set_translator()`` API.
"""
translator_class = self.app.registry.get_translator_class(self)
assert translator_class, "translator not found for %s" % self.__class__.__name__
return translator_class(*args)
return self.app.registry.create_translator(self, *args)
@property
def translator_class(self):

View File

@ -12,6 +12,7 @@ from __future__ import print_function
import traceback
import warnings
from types import MethodType
from typing import TYPE_CHECKING
from pkg_resources import iter_entry_points
@ -104,6 +105,10 @@ class SphinxComponentRegistry(object):
#: custom translators; builder name -> translator class
self.translators = {} # type: Dict[unicode, nodes.NodeVisitor]
#: custom handlers for translators
#: a dict of builder name -> dict of node name -> visitor and departure functions
self.translation_handlers = {} # type: Dict[unicode, Dict[unicode, Tuple[Callable, Callable]]] # NOQA
#: additional transforms; list of transforms
self.transforms = [] # type: List[Type[Transform]]
@ -312,15 +317,41 @@ class SphinxComponentRegistry(object):
logger.info(bold(__('Change of translator for the %s builder.') % name))
self.translators[name] = translator
def add_translation_handlers(self, node, **kwargs):
# type: (nodes.Node, Any) -> None
logger.debug('[app] adding translation_handlers: %r, %r', node, kwargs)
for builder_name, handlers in iteritems(kwargs):
translation_handlers = self.translation_handlers.setdefault(builder_name, {})
try:
visit, depart = handlers # unpack once for assertion
translation_handlers[node.__name__] = (visit, depart)
except ValueError:
raise ExtensionError(__('kwargs for add_node() must be a (visit, depart) '
'function tuple: %r=%r') % builder_name, handlers)
def get_translator_class(self, builder):
# type: (Builder) -> Type[nodes.NodeVisitor]
return self.translators.get(builder.name,
builder.default_translator_class)
def create_translator(self, builder, document):
# type: (Builder, nodes.Node) -> nodes.NodeVisitor
def create_translator(self, builder, *args):
# type: (Builder, Any) -> nodes.NodeVisitor
translator_class = self.get_translator_class(builder)
return translator_class(builder, document)
assert translator_class, "translator not found for %s" % builder.name
translator = translator_class(*args)
# transplant handlers for custom nodes to translator instance
handlers = self.translation_handlers.get(builder.name, None)
if handlers is None:
# retry with builder.format
handlers = self.translation_handlers.get(builder.format, {})
for name, (visit, depart) in iteritems(handlers):
setattr(translator, 'visit_' + name, MethodType(visit, translator))
if depart:
setattr(translator, 'depart_' + name, MethodType(depart, translator))
return translator
def add_transform(self, transform):
# type: (Type[Transform]) -> None