Add app.set_translator() API to register or override a Docutils translator class like :confval:html_translator_class.

This commit is contained in:
Takayuki Shimizukawa 2014-08-05 00:18:54 +09:00
parent 2e1c16486c
commit f7399a261a
17 changed files with 324 additions and 18 deletions

View File

@ -43,6 +43,8 @@ Features added
* PR#255: When generating latex references, also insert latex target/anchor
for the ids defined on the node. Thanks to Olivier Heurtier.
* PR#229: Allow registration of other translators. Thanks to Russell Sim.
* Add app.set_translator() API to register or override a Docutils translator
class like :confval:`html_translator_class`.
Bugs fixed
----------

View File

@ -706,6 +706,8 @@ that use Sphinx's HTMLWriter class.
used to translate document trees to HTML. Default is ``None`` (use the
builtin translator).
.. seealso:: :meth:`~sphinx.application.Sphinx.set_translator`
.. confval:: html_show_copyright
If true, "(C) Copyright ..." is shown in the HTML footer. Default is

View File

@ -82,13 +82,17 @@ package.
Register an event called *name*. This is needed to be able to emit it.
.. method:: Sphinx.add_translator(name, translator_class)
.. method:: Sphinx.set_translator(name, translator_class)
Register a Docutils translator class. This is used to register a
custom output translator. This allows extensions to define custom
nodes for the translator (see :meth:`add_node`). If the name
clashes with an existing translator an
:exc:`sphinx.errors.ExtensionError` will be raised.
Register or override a Docutils translator class. This is used to register
a custom output translator or to replace a builtin translator.
This allows extensions to use custom translator and define custom
nodes for the translator (see :meth:`add_node`).
This is a API version of :confval:`html_translator_class` for all other
builders. Note that if :confval:`html_translator_class` is specified and
this API is called for html related builders, API overriding takes
precedence.
.. versionadded:: 1.3

View File

@ -99,8 +99,7 @@ class Sphinx(object):
self.warningiserror = warningiserror
self._events = events.copy()
self._translators = dict.fromkeys([
'html', 'latex', 'text', 'man', 'texinfo'])
self._translators = {}
# say hello to the world
self.info(bold('Running Sphinx v%s' % sphinx.__version__))
@ -451,10 +450,8 @@ class Sphinx(object):
raise ExtensionError('Event %r already present' % name)
self._events[name] = ''
def add_translator(self, name, translator_class):
if name in self._translators:
raise ExtensionError('A Translator by the name '
'%s is already registered.' % name)
def set_translator(self, name, translator_class):
self.info(bold('A Translator for the %s builder is changed.' % name))
self._translators[name] = translator_class
def add_node(self, node, **kwds):

View File

@ -65,6 +65,9 @@ class Builder(object):
# images that need to be copied over (source -> dest)
self.images = {}
# load default translator class
self.translator_class = app._translators.get(self.name)
self.init()
# helper methods

View File

@ -152,7 +152,9 @@ class StandaloneHTMLBuilder(Builder):
self.config.trim_doctest_flags)
def init_translator_class(self):
if self.config.html_translator_class:
if self.translator_class is not None:
pass
elif self.config.html_translator_class:
self.translator_class = self.app.import_object(
self.config.html_translator_class,
'html_translator_class setting')

View File

@ -46,7 +46,8 @@ class WebSupportBuilder(PickleHTMLBuilder):
self.storage = storage
def init_translator_class(self):
self.translator_class = WebSupportTranslator
if self.translator_class is None:
self.translator_class = WebSupportTranslator
def prepare_writing(self, docnames):
PickleHTMLBuilder.prepare_writing(self, docnames)

View File

@ -89,9 +89,11 @@ class LaTeXWriter(writers.Writer):
def __init__(self, builder):
writers.Writer.__init__(self)
self.builder = builder
self.translator_class = (
self.builder.translator_class or LaTeXTranslator)
def translate(self):
visitor = LaTeXTranslator(self.document, self.builder)
visitor = self.translator_class(self.document, self.builder)
self.document.walkabout(visitor)
self.output = visitor.astext()

View File

@ -26,9 +26,11 @@ class ManualPageWriter(Writer):
def __init__(self, builder):
Writer.__init__(self)
self.builder = builder
self.translator_class = (
self.builder.translator_class or ManualPageTranslator)
def translate(self):
visitor = ManualPageTranslator(self.builder, self.document)
visitor = self.translator_class(self.builder, self.document)
self.visitor = visitor
self.document.walkabout(visitor)
self.output = visitor.astext()

View File

@ -120,9 +120,12 @@ class TexinfoWriter(writers.Writer):
def __init__(self, builder):
writers.Writer.__init__(self)
self.builder = builder
self.translator_class = (
self.builder.translator_class or TexinfoTranslator)
def translate(self):
self.visitor = visitor = TexinfoTranslator(self.document, self.builder)
self.visitor = visitor = self.translator_class(
self.document, self.builder)
self.document.walkabout(visitor)
visitor.finish()
for attr in self.visitor_attributes:

View File

@ -140,9 +140,10 @@ class TextWriter(writers.Writer):
def __init__(self, builder):
writers.Writer.__init__(self)
self.builder = builder
self.translator_class = self.builder.translator_class or TextTranslator
def translate(self):
visitor = TextTranslator(self.document, self.builder)
visitor = self.translator_class(self.document, self.builder)
self.document.walkabout(visitor)
self.output = visitor.body

View File

@ -18,6 +18,8 @@ class XMLWriter(BaseXMLWriter):
def __init__(self, builder):
BaseXMLWriter.__init__(self)
self.builder = builder
if self.builder.translator_class:
self.translator_class = self.builder.translator_class
def translate(self, *args, **kwargs):
self.document.settings.newlines = \

View File

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
import os
import sys
from sphinx.writers.html import HTMLTranslator
from sphinx.writers.latex import LaTeXTranslator
from sphinx.writers.manpage import ManualPageTranslator
from sphinx.writers.texinfo import TexinfoTranslator
from sphinx.writers.text import TextTranslator
from sphinx.writers.websupport import WebSupportTranslator
from docutils.writers.docutils_xml import XMLTranslator
sys.path.insert(0, os.path.abspath('.'))
project = 'test'
master_doc = 'index'
class ConfHTMLTranslator(HTMLTranslator):
pass
class ConfDirHTMLTranslator(HTMLTranslator):
pass
class ConfSingleHTMLTranslator(HTMLTranslator):
pass
class ConfPickleTranslator(HTMLTranslator):
pass
class ConfJsonTranslator(HTMLTranslator):
pass
class ConfLaTeXTranslator(LaTeXTranslator):
pass
class ConfManualPageTranslator(ManualPageTranslator):
pass
class ConfTexinfoTranslator(TexinfoTranslator):
pass
class ConfTextTranslator(TextTranslator):
pass
class ConfWebSupportTranslator(WebSupportTranslator):
pass
class ConfXMLTranslator(XMLTranslator):
pass
class ConfPseudoXMLTranslator(XMLTranslator):
pass
def setup(app):
app.set_translator('html', ConfHTMLTranslator)
app.set_translator('dirhtml', ConfDirHTMLTranslator)
app.set_translator('singlehtml', ConfSingleHTMLTranslator)
app.set_translator('pickle', ConfPickleTranslator)
app.set_translator('json', ConfJsonTranslator)
app.set_translator('latex', ConfLaTeXTranslator)
app.set_translator('man', ConfManualPageTranslator)
app.set_translator('texinfo', ConfTexinfoTranslator)
app.set_translator('text', ConfTextTranslator)
app.set_translator('websupport', ConfWebSupportTranslator)
app.set_translator('xml', ConfXMLTranslator)
app.set_translator('pseudoxml', ConfPseudoXMLTranslator)

View File

@ -0,0 +1,3 @@
=======================
Test API set_translator
=======================

View File

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath('.')))
project = 'test'
master_doc = 'index'

View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from sphinx.writers.html import HTMLTranslator
class ExtHTMLTranslator(HTMLTranslator):
pass

View File

@ -0,0 +1,185 @@
# -*- coding: utf-8 -*-
"""
test_api_translator
~~~~~~~~~~~~~~~~~~~
Test the Sphinx API for translator.
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from util import with_app, test_roots
@with_app(
buildername='html',
srcdir=(test_roots / 'test-api-set-translator'),
confdir=(test_roots / 'test-api-set-translator' / 'nonext'),
)
def test_html_translator(app):
# no set_translator(), no html_translator_class
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'SmartyPantsHTMLTranslator'
@with_app(
buildername='html',
srcdir=(test_roots / 'test-api-set-translator'),
confdir=(test_roots / 'test-api-set-translator' / 'nonext'),
confoverrides={
'html_translator_class': 'translator.ExtHTMLTranslator'},
)
def test_html_with_html_translator_class(app):
# no set_translator(), but html_translator_class
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ExtHTMLTranslator'
@with_app(
buildername='html',
srcdir=(test_roots / 'test-api-set-translator'),
confdir=(test_roots / 'test-api-set-translator' / 'nonext'),
confoverrides={'html_use_smartypants': False},
)
def test_html_with_smartypants(app):
# no set_translator(), html_use_smartypants=False
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'HTMLTranslator'
@with_app(
buildername='html',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_html_(app):
# use set_translator(), no html_translator_class
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfHTMLTranslator'
@with_app(
buildername='html',
srcdir=(test_roots / 'test-api-set-translator'),
confoverrides={'html_translator_class': 'ext.ExtHTMLTranslator'},
)
def test_html_with_set_translator_for_html_and_html_translator_class(app):
# use set_translator() and html_translator_class.
# set_translator() is given priority over html_translator_clas.
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfHTMLTranslator'
@with_app(
buildername='dirhtml',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_dirhtml_set_translator_for_dirhtml(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfDirHTMLTranslator'
@with_app(
buildername='singlehtml',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_singlehtml_set_translator_for_singlehtml(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfSingleHTMLTranslator'
@with_app(
buildername='pickle',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_pickle_set_translator_for_pickle(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfPickleTranslator'
@with_app(
buildername='json',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_json_set_translator_for_json(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfJsonTranslator'
@with_app(
buildername='latex',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_latex(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfLaTeXTranslator'
@with_app(
buildername='man',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_man(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfManualPageTranslator'
@with_app(
buildername='texinfo',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_texinfo(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfTexinfoTranslator'
@with_app(
buildername='text',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_text(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfTextTranslator'
@with_app(
buildername='websupport',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_websupport(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfWebSupportTranslator'
@with_app(
buildername='xml',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_xml(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfXMLTranslator'
@with_app(
buildername='pseudoxml',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_pseudoxml(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfPseudoXMLTranslator'