我正在使用 Sphinx 来记录一个 python 项目。我想在我的文档字符串中使用 Markdown 来格式化它们。即使我使用recommonmark
扩展名,它也只涵盖.md
手动编写的文件,而不是文档字符串。
我在我的扩展中使用autodoc
,napoleon
和。recommonmark
如何在我的文档字符串中制作 sphinx 解析降价?
我正在使用 Sphinx 来记录一个 python 项目。我想在我的文档字符串中使用 Markdown 来格式化它们。即使我使用recommonmark
扩展名,它也只涵盖.md
手动编写的文件,而不是文档字符串。
我在我的扩展中使用autodoc
,napoleon
和。recommonmark
如何在我的文档字符串中制作 sphinx 解析降价?
autodoc-process-docstring
Sphinx 的 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 解析器,是Sphinx和Read-the-Docs推荐的替代品。MyST还不支持文档字符串中的 Markdown,但可以使用与上述相同的钩子通过 Commonmark 获得有限的支持。
此处概述的方法的一种可能替代方法是将MkDocs与MkDocStrings插件一起使用,这将完全从流程中消除 Sphinx 和 reStructuredText。
在@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)
我不得不扩展 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)
当前的@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)