Created
January 17, 2019 00:24
-
-
Save mayo/dc1d56e551d83ef77055ebdcbc70062d to your computer and use it in GitHub Desktop.
Process rST (restructured text) strings using Sphinx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is particularly useful when your doc strings are written in rST as part of | |
# a bigger project, but are also needed to be displayed elsewhere, for when when | |
# generating code. | |
""" | |
This is intended to have similar interface to doctools.core (specifically | |
publish_string level of methods), but still could use more cleanup. Plaintext | |
output is assumed. Changing this would require tinkering with the Sphinx writers. | |
""" | |
import docutils.io | |
import sphinx.application | |
import sphinx.util.docutils | |
class FakeFileStringInput(docutils.io.StringInput): | |
"""This is to cheat Sphinx' cheat. Makes a string look like file to Sphinx""" | |
def close(self): | |
pass | |
class SphinxStringProcessor(object): | |
def __init__(self, src_dir, conf_dir=None): | |
self.src_dir = src_dir | |
self.conf_dir = conf_dir or self.src_dir | |
# This should point at real Sphinx conf_dir and src_dir, as the references | |
# and directives in the processed strings depend on the configuration and | |
# source files (terms, etc). | |
# | |
# Sphinx requires outdir and doctreedir to be valid paths. Because this | |
# only processes strings, nothing will be actually written to said paths. | |
# | |
# buildername is set to text, as we'll be outputting plain text. This | |
# affects how files are read. Text is the fastest, but this should stay | |
# in sync with the writer in publish_string. | |
#TODO: use tempdir instead of hardcoded path | |
self.sphinx = sphinx.application.Sphinx( | |
confdir=self.conf_dir, | |
srcdir=self.src_dir, | |
outdir='/tmp/sphinx', | |
doctreedir='/tmp/sphinx', | |
buildername='text' | |
) | |
def publish_programatically(self, rst_string, name="<string>"): | |
"""Process an rST string through Sphinx. | |
Name is an optional parameter to identify this string in the output, in case there are rST parse errors. | |
""" | |
# This preps Sphinx internal setup for clean build - sets temp_data variable to expected values: | |
# - docname (it doesn't matter what it is) | |
# - default_domain | |
self.sphinx.env.prepare_settings(None) | |
with sphinx.util.docutils.sphinx_domains(self.sphinx.env): | |
# This makes Sphinx magic happen when reading files | |
reader = sphinx.io.SphinxStandaloneReader(self.sphinx) | |
# Get the parser for rST | |
parser = self.sphinx.registry.create_source_parser(self.sphinx, 'restructuredtext') | |
# Plain text writer | |
writer = sphinx.writers.text.TextWriter(self.sphinx.builder) | |
#TODO: leaving out source_path breaks Sphinx' transforms. The transforms | |
# check to see if the file is within the docs directory, so we point it | |
# at the doc dir. Normally this would point at a file, but since we're | |
# processing a given string, rather than the file contents, it shouldn't | |
# matter what this points at. | |
# source_path shows up in output when reporting errors in the parsed | |
# string. Appending name makes it eaier to identify where the error | |
# is coming from. The name has to start with the docs source path, to | |
# be valid in Sphinx. | |
source = FakeFileStringInput( | |
encoding='utf-8', | |
source=rst_string, | |
source_path="{:s}<{:s}>".format(self.src_dir, name) | |
) | |
# We'll be writing out to a string | |
destination = docutils.io.StringOutput(encoding='utf-8') | |
pub = docutils.core.Publisher( | |
reader=reader, | |
parser=parser, | |
writer=writer, | |
# NOTE: Sphing tries to cheat docutils and causes mess. To make | |
# things simple, use their dummy source class, and fake the file input with FakeFileStringInput | |
source_class=sphinx.io.SphinxDummySourceClass, | |
source=source, | |
destination_class=docutils.io.StringOutput, | |
destination=destination, | |
) | |
pub.set_components(None, 'restructuredtext', None) | |
pub.process_programmatic_settings(None, self.sphinx.env.settings, None) | |
out = pub.publish() | |
return out, pub | |
def publish_string(self, rst_string, name='<string>'): | |
out, _ = self.publish_programatically(rst_string, name) | |
return out | |
if __name__ == "__main__": | |
doc_dir = '/path/to/sphinx/project/' | |
rst_text = """ | |
test | |
.. note:: | |
test note | |
:meth:`~module.class.method()` | |
""" | |
s = SphinxStringProcessor(doc_dir) | |
print '------------' | |
print s.publish_string(rst_text) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment