How to use Python to programmatically generate part of Sphinx documentation?
Sphinx doesn't have anything built-in to do what you like. You can either create a custom directive to process your files or generate the reStructuredText in a separate step and include the resulting reStructuredText file using the include directive.
An improvement based on Michael's code and the built-in include directive:
import sys
from os.path import basename
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from docutils.parsers.rst import Directive
from docutils import nodes, statemachine
class ExecDirective(Directive):
"""Execute the specified python code and insert the output into the document"""
has_content = True
def run(self):
oldStdout, sys.stdout = sys.stdout, StringIO()
tab_width = self.options.get('tab-width', self.state.document.settings.tab_width)
source = self.state_machine.input_lines.source(self.lineno - self.state_machine.input_offset - 1)
try:
exec('\n'.join(self.content))
text = sys.stdout.getvalue()
lines = statemachine.string2lines(text, tab_width, convert_whitespace=True)
self.state_machine.insert_input(lines, source)
return []
except Exception:
return [nodes.error(None, nodes.paragraph(text = "Unable to execute python code at %s:%d:" % (basename(source), self.lineno)), nodes.paragraph(text = str(sys.exc_info()[1])))]
finally:
sys.stdout = oldStdout
def setup(app):
app.add_directive('exec', ExecDirective)
This one imports the output earlier so that it goes straight through the parser. It also works in Python 3.
At the end I find a way to achieve what I wanted. Here's the how-to:
- Create a python script (let's call it
generate-includes.py
) that will generate the reStructuredText and save it in themyrst.inc
file. (In my example, this would be the script loading and parsing the YAML, but this is irrelevant). Make sure this file is executable!!! Use the
include
directive in your main .rst document of your documentation, in the point where you want your dynamically-generated documentation to be inserted:.. include:: myrst.inc
Modify the sphinx Makefile in order to generate the required .inc files at build time:
myrst.inc: ./generate-includes.py html: myrst.inc ...(other stuff here)
Build your documentation normally with
make html
.
I needed the same thing, so I threw together a new directive that seems to work (I know nothing about custom Sphinx directives, but it's worked so far):
import sys
from os.path import basename
from StringIO import StringIO
from sphinx.util.compat import Directive
from docutils import nodes
class ExecDirective(Directive):
"""Execute the specified python code and insert the output into the document"""
has_content = True
def run(self):
oldStdout, sys.stdout = sys.stdout, StringIO()
try:
exec '\n'.join(self.content)
return [nodes.paragraph(text = sys.stdout.getvalue())]
except Exception, e:
return [nodes.error(None, nodes.paragraph(text = "Unable to execute python code at %s:%d:" % (basename(self.src), self.srcline)), nodes.paragraph(text = str(e)))]
finally:
sys.stdout = oldStdout
def setup(app):
app.add_directive('exec', ExecDirective)
It's used as follows:
.. exec::
print "Python code!"
print "This text will show up in the document"