Skip to content

Instantly share code, notes, and snippets.

@mayo
Created January 17, 2019 00:24
Show Gist options
  • Save mayo/dc1d56e551d83ef77055ebdcbc70062d to your computer and use it in GitHub Desktop.
Save mayo/dc1d56e551d83ef77055ebdcbc70062d to your computer and use it in GitHub Desktop.
Process rST (restructured text) strings using Sphinx
# 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