Source code for bag.check_rst

#!/usr/bin/env python3

"""reStructuredText document validator / verifier / checker.

For Python code you have 2 available functions::

    warnings = check_rst_document(a_string)
    warnings = check_rst_file(path)

These functions will return an empty list if the document is OK.

In shell, use it like this::

    check_rst < some_document.rst

Or like this if the package *bag* isn't installed:

    python check_rst.py < some_document.rst

The command prints either "OK" or the warnings.
And it returns 0 if the document is OK.
"""

import codecs
from docutils.parsers.rst import Parser  # type: ignore
from docutils import utils  # type: ignore

# from docutils.readers.standalone import Reader
from docutils.transforms import (
    frontmatter,
    misc,
    references,
    universal,
    writer_aux,
)

# TODO: I don't know if the order of these transforms makes any sense.
check_transforms = [
    # from parsers/rst/__init__.py:
    universal.SmartQuotes,
    # from readers/__init__.py:
    universal.Decorations,
    universal.ExposeInternals,
    universal.StripComments,
    # from readers/standalone.py:
    references.Substitutions,
    references.PropagateTargets,
    frontmatter.DocTitle,
    frontmatter.SectionSubTitle,
    frontmatter.DocInfo,
    references.AnonymousHyperlinks,
    references.IndirectHyperlinks,
    references.Footnotes,
    references.ExternalTargets,
    references.InternalTargets,
    references.DanglingReferences,
    misc.Transitions,
    # from writers/__init__.py:
    universal.Messages,
    universal.FilterMessages,
    universal.StripClassesAndElements,
    # from writers/html4css1/__init__.py:
    writer_aux.Admonitions,
]


[docs]def check_rst_document(source, source_path="<string>", settings=None): """Return a list of objects containing problems in a rst document. Provide the reStructuredText document through the argument ``source``. ``settings`` is the settings object for the docutils document instance. If None, the default settings are used. """ alist = [] def accumulate(x): return alist.append(x) document = utils.new_document(source_path, settings=settings) document.reporter.attach_observer(accumulate) if settings is None: # Fill in some values to prevent AttributeError document.settings.tab_width = 8 document.settings.pep_references = None document.settings.rfc_references = None document.settings.smart_quotes = True document.settings.smartquotes_locales = ["en"] document.settings.file_insertion_enabled = True parser = Parser() parser.parse(source, document) # Now apply transforms to get more warnings document.transformer.add_transforms(check_transforms) document.transformer.apply_transforms() return alist
[docs]def check_rst_file(path, encoding="utf-8", settings=None): # noqa with codecs.open(path, encoding=encoding) as stream: source = stream.read() return check_rst_document(source, path, settings=settings)
""" # ANOTHER WAY would be to detect docinfo printing out a warning when # publish_parts() executes. But that would only work as a command, not as # a Python function, I am right? def check_rst_file2(path, encoding='utf-8'): from docutils.core import publish_parts with codecs.open(path, encoding=encoding) as stream: source = stream.read() adict = publish_parts(source, writer_name='html') # <string>:72: (ERROR/3) Unknown target name: "read the source code". """ """ The following attempt isn't finished, the docutils API is too convoluted: class RestDocumentChecker(Reader): '''Has parse warnings accumulated into its ``checker_result`` instance variable. ''' def new_document(self): # We override this method in order to be able to observe the document document = super(RestDocumentChecker, self).new_document() self.checker_result = [] accumulate = lambda x: self.checker_result.append(x) document.reporter.attach_observer(accumulate) return document def check_rst_file2(path, encoding='utf-8', settings=None): '''Returns a list of objects containing (in their ``message`` attribute) problems in the provided reStructuredText document ``source``. ``settings`` is the settings object for the docutils document instance. If None, the default settings are used. ''' r = RestDocumentChecker(parser=Parser()) with codecs.open(path, encoding=encoding) as stream: r.read(stream, None, None) return r.checker_result """
[docs]def command(): """Entry point; becomes a console script when the package is installed.""" from sys import exit, stdin source = stdin.read() warnings = check_rst_document(source) if warnings: print("\nHere are the warnings for that rst document:") for w in warnings: print(" " + str(w)) exit(1) else: print("OK") exit(0)
if __name__ == "__main__": command()