如何使用Python程序生成Sphinx文档的一部分?

30 投票
7 回答
15583 浏览
提问于 2025-04-17 00:47

我正在使用Sphinx来为我的一个项目生成文档。

在这个项目中,我在一个yaml文件里列出了可用的命令,这个文件加载后会变成一个字典,格式是{命令名称 : 命令描述},比如:

commands = {"copy"  : "Copy the highlighted text in the clipboard",
            "paste" : "Paste the clipboard text to cursor location",
            ...}

我想知道的是,Sphinx有没有办法在执行make html的时候加载这个yaml文件,把这个Python字典转换成某种reStructuredText格式(例如,一个定义列表),然后包含在我的HTML输出中。

我希望我的.rst文件看起来像这样:

Available commands
==================
The commands available in bla-bla-bla...

.. magic-directive-that-execute-python-code::
   :maybe python code or name of python file here:

然后在内部转换成:

Available commands
==================
The commands available in bla-bla-bla...

copy
  Copy the highlighted text in the clipboard

paste
  Paste the clipboard text to cursor location

最后再转换成HTML。

7 个回答

9

我也需要这个功能,所以我简单做了一个新的指令,似乎可以用(我对自定义 Sphinx 指令一无所知,但到目前为止它都能正常工作):

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)

用法如下:

.. exec::
   print "Python code!"
   print "This text will show up in the document"
18

这是对Michael的代码和内置的包含指令进行的一些改进:

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)

这个方法会提前导入输出,这样它就可以直接通过解析器处理了。而且它在Python 3中也能正常工作。

27

最后我找到了一种方法来实现我想要的效果。下面是具体步骤:

  1. 创建一个Python脚本(我们叫它 generate-includes.py),这个脚本会生成 reStructuredText 格式的内容,并把它保存到 myrst.inc 文件中。(在我的例子中,这个脚本会加载和解析YAML文件,但这并不重要)。确保这个文件是可以执行的!!!
  2. 在你的主 .rst 文档中使用 include 指令,在你希望插入动态生成文档的地方:

    .. include:: myrst.inc
    
  3. 修改 Sphinx 的 Makefile,以便在构建时生成所需的 .inc 文件:

    myrst.inc:
        ./generate-includes.py
    
    html: myrst.inc
        ...(other stuff here)
    
  4. 正常构建你的文档,使用 make html 命令。

撰写回答