1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
from os.path import join
try:
from itertools import zip_longest
except ImportError:
# Python 2.7 support.
from itertools import izip_longest as zip_longest
import io
import docutils
from docutils.frontend import OptionParser
from docutils.nodes import Text, Element, system_message
from docutils.parsers.rst import Parser
from docutils.utils import new_document
from docutils.core import publish_from_doctree
from sphinx.application import Sphinx
# sphinx.util.docutils requires Sphinx 1.5 and up.
try:
from sphinx.util.docutils import docutils_namespace
except ImportError:
# Attempt to support Sphinx 1.4 and thus the old Debian Stretch (oldstable)
from copy import copy
from contextlib import contextmanager
from docutils.parsers.rst import directives, roles
@contextmanager
def docutils_namespace():
"""Create namespace for reST parsers."""
try:
_directives = copy(directives._directives)
_roles = copy(roles._roles)
yield
finally:
directives._directives = _directives
roles._roles = _roles
def build_sphinx(src_dir, output_dir, files=None, config={}):
doctrees_dir = join(output_dir, '.doctrees')
filenames = []
force_all = True
default_config = {
'extensions': ['sphinxcontrib.restbuilder'],
'master_doc': 'index',
}
default_config.update(config)
config = default_config
if files:
force_all = False
filenames = [join(src_dir, file + '.rst') for file in files]
config['master_doc'] = files[0]
with docutils_namespace():
app = Sphinx(
src_dir,
None,
output_dir,
doctrees_dir,
'rst',
confoverrides=config,
verbosity=0,
)
app.build(force_all=force_all, filenames=filenames)
def assert_node_equal(output, expected):
assert type(output) == type(expected)
if isinstance(output, Text):
output_text = output.replace('\r\n', ' ')
output_text = output_text.replace('\n', ' ')
expected_text = expected.replace('\r\n', ' ')
expected_text = expected_text.replace('\n', ' ')
assert output_text == expected_text
elif isinstance(output, system_message):
assert len(output.children) == len(expected.children)
# Don't check specifics of system_messages (warnings)
# E.g. the line number may be off
elif isinstance(output, Element):
assert len(output.children) == len(expected.children)
assert output.attributes == expected.attributes
else:
raise AssertionError
def assert_doc_equal(output_doc, expected_doc):
"""
Can be used to compare two documents, ignoring any whitespace changes
"""
for output, expected in zip_longest(
output_doc.traverse(include_self=False), expected_doc.traverse(include_self=False)
):
assert_node_equal(output, expected)
def parse_doc(dir, file):
parser = Parser()
with io.open(join(dir, file + '.rst'), encoding='utf-8') as fh:
doc = new_document(
file,
OptionParser(
components=(docutils.parsers.rst.Parser,)
).get_default_values(),
)
parser.parse(
fh.read(),
doc,
)
return doc
def run_parse_test(src_dir, expected_dir, output_dir, subdir, files):
src_dir = join(src_dir, subdir)
expected_dir = join(expected_dir, subdir)
output_dir = join(output_dir, subdir)
build_sphinx(src_dir, output_dir, files)
for file in files:
output_doc = parse_doc(output_dir, file)
expected_doc = parse_doc(expected_dir, file)
try:
assert_doc_equal(output_doc, expected_doc)
except AssertionError:
# output XML version of doctree for easier debugging
with open(join(output_dir, file + '.output.xml'), 'wb') as fw:
fw.write(publish_from_doctree(output_doc, writer_name='xml'))
with open(join(output_dir, file + '.expected.xml'), 'wb') as fw:
fw.write(publish_from_doctree(expected_doc, writer_name='xml'))
raise
if __name__ == '__main__':
pass
|