2016-02-14 07:00:03 -06:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
|
|
sphinx.io
|
|
|
|
~~~~~~~~~
|
|
|
|
|
|
|
|
Input/Output files
|
|
|
|
|
2017-03-22 06:21:12 -05:00
|
|
|
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
|
2016-02-14 07:00:03 -06:00
|
|
|
:license: BSD, see LICENSE for details.
|
|
|
|
"""
|
2017-10-31 09:04:26 -05:00
|
|
|
import codecs
|
|
|
|
|
2017-10-31 09:04:25 -05:00
|
|
|
from docutils.io import FileInput, NullOutput
|
|
|
|
from docutils.core import Publisher
|
2016-02-14 07:00:03 -06:00
|
|
|
from docutils.readers import standalone
|
|
|
|
from docutils.writers import UnfilteredWriter
|
2017-03-09 05:26:46 -06:00
|
|
|
from six import string_types, text_type, iteritems
|
2016-11-07 23:05:58 -06:00
|
|
|
from typing import Any, Union # NOQA
|
2016-02-14 07:00:03 -06:00
|
|
|
|
2016-08-21 08:15:50 -05:00
|
|
|
from sphinx.transforms import (
|
2016-09-10 04:22:51 -05:00
|
|
|
ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
|
|
|
|
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds,
|
|
|
|
AutoNumbering, AutoIndexUpgrader, FilterSystemMessages,
|
2017-03-18 02:19:21 -05:00
|
|
|
UnreferencedFootnotesDetector
|
2016-09-10 04:22:51 -05:00
|
|
|
)
|
2016-09-10 09:31:43 -05:00
|
|
|
from sphinx.transforms.compact_bullet_list import RefOnlyBulletListTransform
|
2016-09-10 04:22:51 -05:00
|
|
|
from sphinx.transforms.i18n import (
|
|
|
|
PreserveTranslatableMessages, Locale, RemoveTranslatableInline,
|
2016-08-21 08:15:50 -05:00
|
|
|
)
|
2017-10-31 09:04:26 -05:00
|
|
|
from sphinx.util import logging
|
2016-02-14 07:00:03 -06:00
|
|
|
from sphinx.util import import_object, split_docinfo
|
2016-09-05 04:27:32 -05:00
|
|
|
from sphinx.util.docutils import LoggingReporter
|
2016-02-14 07:00:03 -06:00
|
|
|
|
2016-11-07 23:05:58 -06:00
|
|
|
if False:
|
|
|
|
# For type annotation
|
2017-03-02 21:19:09 -06:00
|
|
|
from typing import Any, Dict, List, Tuple, Union # NOQA
|
2016-11-07 23:05:58 -06:00
|
|
|
from docutils import nodes # NOQA
|
|
|
|
from docutils.io import Input # NOQA
|
|
|
|
from docutils.parsers import Parser # NOQA
|
|
|
|
from docutils.transforms import Transform # NOQA
|
|
|
|
from sphinx.application import Sphinx # NOQA
|
|
|
|
from sphinx.builders import Builder # NOQA
|
|
|
|
from sphinx.environment import BuildEnvironment # NOQA
|
|
|
|
|
2016-02-14 07:00:03 -06:00
|
|
|
|
2017-10-31 09:04:26 -05:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2016-02-14 07:00:03 -06:00
|
|
|
class SphinxBaseReader(standalone.Reader):
|
|
|
|
"""
|
|
|
|
Add our source parsers
|
|
|
|
"""
|
|
|
|
def get_transforms(self):
|
2016-11-07 23:05:58 -06:00
|
|
|
# type: () -> List[Transform]
|
2016-02-14 07:00:03 -06:00
|
|
|
return standalone.Reader.get_transforms(self) + self.transforms
|
|
|
|
|
2016-09-05 04:27:32 -05:00
|
|
|
def new_document(self):
|
2017-02-07 23:31:59 -06:00
|
|
|
# type: () -> nodes.document
|
2016-09-05 04:27:32 -05:00
|
|
|
document = standalone.Reader.new_document(self)
|
|
|
|
reporter = document.reporter
|
|
|
|
document.reporter = LoggingReporter(reporter.source, reporter.report_level,
|
|
|
|
reporter.halt_level, reporter.debug_flag,
|
|
|
|
reporter.error_handler)
|
|
|
|
return document
|
|
|
|
|
2016-02-14 07:00:03 -06:00
|
|
|
|
|
|
|
class SphinxStandaloneReader(SphinxBaseReader):
|
|
|
|
"""
|
|
|
|
Add our own transforms.
|
|
|
|
"""
|
2016-08-21 08:15:50 -05:00
|
|
|
transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, PreserveTranslatableMessages,
|
|
|
|
Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets,
|
|
|
|
HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds,
|
2016-09-10 09:31:43 -05:00
|
|
|
RemoveTranslatableInline, PreserveTranslatableMessages, FilterSystemMessages,
|
2017-03-18 02:19:21 -05:00
|
|
|
RefOnlyBulletListTransform, UnreferencedFootnotesDetector]
|
2016-02-14 07:00:03 -06:00
|
|
|
|
|
|
|
|
|
|
|
class SphinxI18nReader(SphinxBaseReader):
|
|
|
|
"""
|
|
|
|
Replacer for document.reporter.get_source_and_line method.
|
|
|
|
|
|
|
|
reST text lines for translation do not have the original source line number.
|
|
|
|
This class provides the correct line numbers when reporting.
|
|
|
|
"""
|
|
|
|
|
2017-12-07 08:49:32 -06:00
|
|
|
lineno = None # type: int
|
2016-02-14 07:00:03 -06:00
|
|
|
transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
|
|
|
|
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
|
2016-09-10 03:38:10 -05:00
|
|
|
AutoNumbering, SortIds, RemoveTranslatableInline,
|
2017-03-18 02:19:21 -05:00
|
|
|
FilterSystemMessages, RefOnlyBulletListTransform,
|
|
|
|
UnreferencedFootnotesDetector]
|
2016-02-14 07:00:03 -06:00
|
|
|
|
|
|
|
def set_lineno_for_reporter(self, lineno):
|
2016-11-07 23:05:58 -06:00
|
|
|
# type: (int) -> None
|
2016-02-14 07:00:03 -06:00
|
|
|
self.lineno = lineno
|
|
|
|
|
|
|
|
def new_document(self):
|
2016-11-07 23:05:58 -06:00
|
|
|
# type: () -> nodes.document
|
2016-02-14 07:00:03 -06:00
|
|
|
document = SphinxBaseReader.new_document(self)
|
|
|
|
reporter = document.reporter
|
|
|
|
|
|
|
|
def get_source_and_line(lineno=None):
|
2017-02-07 23:31:59 -06:00
|
|
|
# type: (int) -> Tuple[unicode, int]
|
2016-02-14 07:00:03 -06:00
|
|
|
return reporter.source, self.lineno
|
|
|
|
|
|
|
|
reporter.get_source_and_line = get_source_and_line
|
|
|
|
return document
|
|
|
|
|
|
|
|
|
|
|
|
class SphinxDummyWriter(UnfilteredWriter):
|
|
|
|
supported = ('html',) # needed to keep "meta" nodes
|
|
|
|
|
|
|
|
def translate(self):
|
2016-11-07 23:05:58 -06:00
|
|
|
# type: () -> None
|
2016-02-14 07:00:03 -06:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2017-10-31 09:04:24 -05:00
|
|
|
def SphinxDummySourceClass(source, *args, **kwargs):
|
|
|
|
"""Bypass source object as is to cheat Publisher."""
|
|
|
|
return source
|
|
|
|
|
|
|
|
|
2017-11-14 23:48:30 -06:00
|
|
|
class SphinxBaseFileInput(FileInput):
|
|
|
|
"""A base class of SphinxFileInput."""
|
|
|
|
|
2016-02-14 07:00:03 -06:00
|
|
|
def __init__(self, app, env, *args, **kwds):
|
2016-11-07 23:05:58 -06:00
|
|
|
# type: (Sphinx, BuildEnvironment, Any, Any) -> None
|
2016-02-14 07:00:03 -06:00
|
|
|
self.app = app
|
|
|
|
self.env = env
|
2017-10-31 09:04:26 -05:00
|
|
|
|
|
|
|
# set up error handler
|
|
|
|
codecs.register_error('sphinx', self.warn_and_replace) # type: ignore
|
|
|
|
|
2016-02-14 07:00:03 -06:00
|
|
|
kwds['error_handler'] = 'sphinx' # py3: handle error on open.
|
|
|
|
FileInput.__init__(self, *args, **kwds)
|
|
|
|
|
|
|
|
def decode(self, data):
|
2016-11-07 23:05:58 -06:00
|
|
|
# type: (Union[unicode, bytes]) -> unicode
|
2016-02-14 07:00:03 -06:00
|
|
|
if isinstance(data, text_type): # py3: `data` already decoded.
|
|
|
|
return data
|
|
|
|
return data.decode(self.encoding, 'sphinx') # py2: decoding
|
|
|
|
|
|
|
|
def read(self):
|
2016-11-07 23:05:58 -06:00
|
|
|
# type: () -> unicode
|
2016-02-14 07:00:03 -06:00
|
|
|
data = FileInput.read(self)
|
2017-11-14 20:34:52 -06:00
|
|
|
|
|
|
|
# emit source-read event
|
|
|
|
arg = [data]
|
|
|
|
self.app.emit('source-read', self.env.docname, arg)
|
2017-11-14 23:48:30 -06:00
|
|
|
return arg[0]
|
2017-10-31 09:04:25 -05:00
|
|
|
|
2017-10-31 09:04:26 -05:00
|
|
|
def warn_and_replace(self, error):
|
|
|
|
# type: (Any) -> Tuple
|
|
|
|
"""Custom decoding error handler that warns and replaces."""
|
|
|
|
linestart = error.object.rfind(b'\n', 0, error.start)
|
|
|
|
lineend = error.object.find(b'\n', error.start)
|
|
|
|
if lineend == -1:
|
|
|
|
lineend = len(error.object)
|
|
|
|
lineno = error.object.count(b'\n', 0, error.start) + 1
|
|
|
|
logger.warning('undecodable source characters, replacing with "?": %r',
|
|
|
|
(error.object[linestart + 1:error.start] + b'>>>' +
|
|
|
|
error.object[error.start:error.end] + b'<<<' +
|
|
|
|
error.object[error.end:lineend]),
|
|
|
|
location=(self.env.docname, lineno))
|
|
|
|
return (u'?', error.end)
|
|
|
|
|
2017-10-31 09:04:25 -05:00
|
|
|
|
2017-11-14 23:48:30 -06:00
|
|
|
class SphinxFileInput(SphinxBaseFileInput):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class SphinxRSTFileInput(SphinxBaseFileInput):
|
|
|
|
def read(self):
|
|
|
|
# type: () -> unicode
|
|
|
|
data = SphinxBaseFileInput.read(self)
|
|
|
|
docinfo, data = split_docinfo(data)
|
|
|
|
if self.env.config.rst_epilog:
|
|
|
|
data = data + '\n' + self.env.config.rst_epilog + '\n'
|
|
|
|
if self.env.config.rst_prolog:
|
|
|
|
data = self.env.config.rst_prolog + '\n' + data
|
|
|
|
return docinfo + data
|
|
|
|
|
|
|
|
|
2017-10-31 09:04:25 -05:00
|
|
|
def read_doc(app, env, filename):
|
|
|
|
# type: (Sphinx, BuildEnvironment, unicode) -> nodes.document
|
2017-11-11 03:47:35 -06:00
|
|
|
"""Parse a document and convert to doctree."""
|
2017-11-14 23:48:30 -06:00
|
|
|
input_class = app.registry.get_source_input(filename)
|
2017-12-07 08:49:32 -06:00
|
|
|
reader = SphinxStandaloneReader()
|
2017-11-14 23:48:30 -06:00
|
|
|
source = input_class(app, env, source=None, source_path=filename,
|
|
|
|
encoding=env.config.source_encoding)
|
2017-12-07 08:49:32 -06:00
|
|
|
parser = app.registry.create_source_parser(app, filename)
|
2017-10-31 09:04:25 -05:00
|
|
|
|
|
|
|
pub = Publisher(reader=reader,
|
2017-12-07 08:49:32 -06:00
|
|
|
parser=parser,
|
2017-10-31 09:04:25 -05:00
|
|
|
writer=SphinxDummyWriter(),
|
|
|
|
source_class=SphinxDummySourceClass,
|
|
|
|
destination=NullOutput())
|
|
|
|
pub.set_components(None, 'restructuredtext', None)
|
|
|
|
pub.process_programmatic_settings(None, env.settings, None)
|
|
|
|
pub.set_source(source, filename)
|
|
|
|
pub.publish()
|
|
|
|
return pub.document
|
2017-11-14 23:48:30 -06:00
|
|
|
|
|
|
|
|
|
|
|
def setup(app):
|
|
|
|
app.registry.add_source_input('*', SphinxFileInput)
|
|
|
|
app.registry.add_source_input('restructuredtext', SphinxRSTFileInput)
|