Ability to override docutils HTML writer/translator.

The RstReader class can now use user-specified writer/translator classes
instead of the hardcoded ones from docutils. This allows for far easier
overriding of the default HTML output -- in the past one would need to
override the internal _parse_metadata() and _get_publisher() functions.
With hypothetical Html5Writer and Html5FieldBodyTranslator classes,
based for example on docutils.writers.html5_polyglot.Writer and
docutils.writers.html5_polyglot.HTMLTranslator, a plugin that overrides
the default behavior would now look just like this:

    # (definition of Writer / Translator classes omitted)

    class Html5RstReader(RstReader):
        writer_class = Html5Writer
        field_body_translator_class = Html5FieldBodyTranslator

    def add_reader(readers):
        readers.reader_classes['rst'] = Html5RstReader

    def register():
        pelican.signals.readers_init.connect(add_reader)
This commit is contained in:
Vladimír Vondruš 2017-06-06 20:34:56 +02:00
commit 7336de45cb

View file

@ -9,7 +9,7 @@ from collections import OrderedDict
import docutils import docutils
import docutils.core import docutils.core
import docutils.io import docutils.io
from docutils.writers.html4css1 import HTMLTranslator from docutils.writers.html4css1 import HTMLTranslator, Writer
import six import six
from six.moves.html_parser import HTMLParser from six.moves.html_parser import HTMLParser
@ -135,12 +135,19 @@ class _FieldBodyTranslator(HTMLTranslator):
pass pass
def render_node_to_html(document, node): def render_node_to_html(document, node, field_body_translator_class):
visitor = _FieldBodyTranslator(document) visitor = field_body_translator_class(document)
node.walkabout(visitor) node.walkabout(visitor)
return visitor.astext() return visitor.astext()
class PelicanHTMLWriter(Writer):
def __init__(self):
Writer.__init__(self)
self.translator_class = PelicanHTMLTranslator
class PelicanHTMLTranslator(HTMLTranslator): class PelicanHTMLTranslator(HTMLTranslator):
def visit_abbreviation(self, node): def visit_abbreviation(self, node):
@ -160,11 +167,26 @@ class PelicanHTMLTranslator(HTMLTranslator):
class RstReader(BaseReader): class RstReader(BaseReader):
"""Reader for reStructuredText files""" """Reader for reStructuredText files
By default the output HTML is written using
docutils.writers.html4css1.Writer and translated using a subclass of
docutils.writers.html4css1.HTMLTranslator. If you want to override it with
your own writer/translator (e.g. a HTML5-based one), pass your classes to
these two attributes. Look in the source code for details.
writer_class Used for writing contents
field_body_translator_class Used for translating metadata such
as article summary
"""
enabled = bool(docutils) enabled = bool(docutils)
file_extensions = ['rst'] file_extensions = ['rst']
writer_class = PelicanHTMLWriter
field_body_translator_class = _FieldBodyTranslator
class FileInput(docutils.io.FileInput): class FileInput(docutils.io.FileInput):
"""Patch docutils.io.FileInput to remove "U" mode in py3. """Patch docutils.io.FileInput to remove "U" mode in py3.
@ -192,7 +214,9 @@ class RstReader(BaseReader):
name_elem, body_elem = element.children name_elem, body_elem = element.children
name = name_elem.astext() name = name_elem.astext()
if name in formatted_fields: if name in formatted_fields:
value = render_node_to_html(document, body_elem) value = render_node_to_html(
document, body_elem,
self.field_body_translator_class)
else: else:
value = body_elem.astext() value = body_elem.astext()
elif element.tagname == 'authors': # author list elif element.tagname == 'authors': # author list
@ -217,10 +241,10 @@ class RstReader(BaseReader):
extra_params.update(user_params) extra_params.update(user_params)
pub = docutils.core.Publisher( pub = docutils.core.Publisher(
writer=self.writer_class(),
source_class=self.FileInput, source_class=self.FileInput,
destination_class=docutils.io.StringOutput) destination_class=docutils.io.StringOutput)
pub.set_components('standalone', 'restructuredtext', 'html') pub.set_components('standalone', 'restructuredtext', 'html')
pub.writer.translator_class = PelicanHTMLTranslator
pub.process_programmatic_settings(None, extra_params, None) pub.process_programmatic_settings(None, extra_params, None)
pub.set_source(source_path=source_path) pub.set_source(source_path=source_path)
pub.publish(enable_exit_status=True) pub.publish(enable_exit_status=True)