12

我正在使用 Sphinx 来记录一个 python 项目。我想在我的文档字符串中使用 Markdown 来格式化它们。即使我使用recommonmark扩展名,它也只涵盖.md手动编写的文件,而不是文档字符串。

我在我的扩展中使用autodoc,napoleon和。recommonmark

如何在我的文档字符串中制作 sphinx 解析降价?

4

4 回答 4

21

autodoc-process-docstringSphinx 的 Autodoc 扩展在每次处理文档字符串时都会发出一个名为的事件。我们可以使用该机制将语法从 Markdown 转换为 reStructuredText。

不幸的是,Recommonmark没有公开 Markdown-to-reST 转换器。它将解析后的 Markdown 直接映射到Docutils对象,即Sphinx本身从 reStructuredText 内部创建的相同表示。

相反,我在我的项目中使用Commonmark进行转换。因为它很快——例如,比Pandoc快得多。速度很重要,因为转换是即时进行的,并单独处理每个文档字符串。除此之外,任何 Markdown-to-reST 转换器都可以。M2R2将是第三个例子。其中任何一个的缺点是它们不支持 Recommonmark 的语法扩展,例如对文档其他部分的交叉引用。只是基本的降价。

要插入 Commonmark 文档字符串转换器,请确保已安装包 ( pip install commonmark) 并将以下内容添加到 Sphinx 的配置文件中conf.py

import commonmark

def docstring(app, what, name, obj, options, lines):
    md  = '\n'.join(lines)
    ast = commonmark.Parser().parse(md)
    rst = commonmark.ReStructuredTextRenderer().render(ast)
    lines.clear()
    lines += rst.splitlines()

def setup(app):
    app.connect('autodoc-process-docstring', docstring)

同时,Recommonmark 在 2021 年 5 月被弃用。Sphinx扩展MyST是一种功能更丰富的 Markdown 解析器,是SphinxRead-the-Docs推荐的替代品。MyST还不支持文档字符串中的 Markdown,但可以使用与上述相同的钩子通过 Commonmark 获得有限的支持。

此处概述的方法的一种可能替代方法是将MkDocsMkDocStrings插件一起使用,这将完全从流程中消除 Sphinx 和 reStructuredText。

于 2019-06-03T13:11:07.673 回答
0

在@john-hennig 答案的基础上,以下内容将保留重组后的文本字段,例如::py:attr::py:class:。这允许您引用其他类等。

import re
import commonmark

py_attr_re = re.compile(r"\:py\:\w+\:(``[^:`]+``)")

def docstring(app, what, name, obj, options, lines):
    md  = '\n'.join(lines)
    ast = commonmark.Parser().parse(md)
    rst = commonmark.ReStructuredTextRenderer().render(ast)
    lines.clear()
    lines += rst.splitlines()

    for i, line in enumerate(lines):
        while True:
            match = py_attr_re.search(line)
            if match is None:
                break 

            start, end = match.span(1)
            line_start = line[:start]
            line_end = line[end:]
            line_modify = line[start:end]
            line = line_start + line_modify[1:-1] + line_end
        lines[i] = line

def setup(app):
    app.connect('autodoc-process-docstring', docstring)
于 2021-10-04T23:09:01.363 回答
0

我不得不扩展 john-hen 接受的答案,以允许将Args:条目的多行描述视为单个参数:

def docstring(app, what, name, obj, options, lines):
  wrapped = []
  literal = False
  for line in lines:
    if line.strip().startswith(r'```'):
      literal = not literal
    if not literal:
      line = ' '.join(x.rstrip() for x in line.split('\n'))
    indent = len(line) - len(line.lstrip())
    if indent and not literal:
      wrapped.append(' ' + line.lstrip())
    else:
      wrapped.append('\n' + line.strip())
  ast = commonmark.Parser().parse(''.join(wrapped))
  rst = commonmark.ReStructuredTextRenderer().render(ast)
  lines.clear()
  lines += rst.splitlines()

def setup(app):
  app.connect('autodoc-process-docstring', docstring)
于 2021-11-30T18:11:04.637 回答
0

当前的@john-hennig 很棒,但似乎Args:在 python 风格的多行中失败了。这是我的修复:


def docstring(app, what, name, obj, options, lines):
    md = "\n".join(lines)
    ast = commonmark.Parser().parse(md)
    rst = commonmark.ReStructuredTextRenderer().render(ast)

    lines.clear()
    lines += _normalize_docstring_lines(rst.splitlines())


def _normalize_docstring_lines(lines: list[str]) -> list[str]:
    """Fix an issue with multi-line args which are incorrectly parsed.

    ```
    Args:
        x: My multi-line description which fit on multiple lines
          and continue in this line.
    ```

    Is parsed as (missing indentation):

    ```
    :param x: My multi-line description which fit on multiple lines
    and continue in this line.
    ```

    Instead of:

    ```
    :param x: My multi-line description which fit on multiple lines
        and continue in this line.
    ```

    """
    is_param_field = False

    new_lines = []
    for l in lines:
        if l.lstrip().startswith(":param"):
            is_param_field = True
        elif is_param_field:
            if not l.strip():  # Blank line reset param
                is_param_field = False
            else:  # Restore indentation
                l = "    " + l.lstrip()
        new_lines.append(l)
    return new_lines


def setup(app):
    app.connect("autodoc-process-docstring", docstring)
于 2022-01-05T18:27:49.310 回答