使用角色和指令扩展语法¶
概述¶
reStructuredText 和 MyST 的语法都可以通过创建新的指令(用于块级元素)和角色(用于行内元素)来扩展。
在本教程中,我们将扩展 Sphinx 以添加
一个
hello角色,它将简单地输出文本Hello {text}!。一个
hello指令,它将简单地输出文本Hello {text}!,作为一个段落。
对于此扩展,您需要对 Python 有基本的了解,我们还将介绍 docutils API 的一些方面。
项目设置¶
您可以使用现有的 Sphinx 项目,或者使用 sphinx-quickstart 创建一个新项目。
我们将把扩展添加到项目中的 source 文件夹中
在
source中创建一个_ext文件夹在
_ext文件夹中创建一个名为helloworld.py的新 Python 文件
这是一个您可能获得的文件夹结构示例
└── source
├── _ext
│ └── helloworld.py
├── conf.py
├── index.rst
编写扩展¶
打开 helloworld.py 并粘贴以下代码
1from __future__ import annotations
2
3from docutils import nodes
4
5from sphinx.application import Sphinx
6from sphinx.util.docutils import SphinxDirective, SphinxRole
7from sphinx.util.typing import ExtensionMetadata
8
9
10class HelloRole(SphinxRole):
11 """A role to say hello!"""
12
13 def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]:
14 node = nodes.inline(text=f'Hello {self.text}!')
15 return [node], []
16
17
18class HelloDirective(SphinxDirective):
19 """A directive to say hello!"""
20
21 required_arguments = 1
22
23 def run(self) -> list[nodes.Node]:
24 paragraph_node = nodes.paragraph(text=f'hello {self.arguments[0]}!')
25 return [paragraph_node]
26
27
28def setup(app: Sphinx) -> ExtensionMetadata:
29 app.add_role('hello', HelloRole())
30 app.add_directive('hello', HelloDirective)
31
32 return {
33 'version': '0.1',
34 'parallel_read_safe': True,
35 'parallel_write_safe': True,
36 }
此示例中发生了一些重要的事件
角色类¶
我们的新角色在 HelloRole 类中声明。
1class HelloRole(SphinxRole):
2 """A role to say hello!"""
3
4 def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]:
5 node = nodes.inline(text=f'Hello {self.text}!')
6 return [node], []
这个类扩展了 SphinxRole 类。该类包含一个 run 方法,这是每个角色的要求。它包含角色的主要逻辑,并返回一个包含以下内容的元组:
一个由 Sphinx 处理的行内级 docutils 节点列表。
一个(可选)系统消息节点列表
指令类¶
我们的新指令在 HelloDirective 类中声明。
1class HelloDirective(SphinxDirective):
2 """A directive to say hello!"""
3
4 required_arguments = 1
5
6 def run(self) -> list[nodes.Node]:
7 paragraph_node = nodes.paragraph(text=f'hello {self.arguments[0]}!')
8 return [paragraph_node]
这个类扩展了 SphinxDirective 类。该类包含一个 run 方法,这是每个指令的要求。它包含指令的主要逻辑,并返回一个由 Sphinx 处理的块级 docutils 节点列表。它还包含一个 required_arguments 属性,它告诉 Sphinx 指令需要多少个参数。
什么是 docutils 节点?¶
当 Sphinx 解析文档时,它会创建一个节点的“抽象语法树”(AST),以结构化的方式表示文档内容,通常独立于任何一种输入(rST、MyST 等)或输出(HTML、LaTeX 等)格式。它之所以是树,是因为每个节点都可以有子节点,依此类推。
<document>
<paragraph>
<text>
Hello world!
docutils 包提供了许多 内置节点,用于表示不同类型的内容,如文本、段落、引用、表格等。
每种节点类型通常只接受特定的直接子节点集,例如 document 节点应只包含“块级”节点,如 paragraph、section、table 等,而 paragraph 节点应只包含“行内级”节点,如 text、emphasis、strong 等。
setup 函数¶
此函数是必需的。我们用它将新指令插入 Sphinx。
def setup(app: Sphinx) -> ExtensionMetadata:
app.add_role('hello', HelloRole())
app.add_directive('hello', HelloDirective)
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}
最简单的做法是调用 Sphinx.add_role() 和 Sphinx.add_directive() 方法,我们这里就是这样做的。对于这个特定的调用,第一个参数是角色/指令本身在 reStructuredText 文件中使用的名称。在这种情况下,我们将使用 hello。例如
Some intro text here...
.. hello:: world
Some text with a :hello:`world` role.
我们还返回了 扩展元数据,它指示了我们扩展的版本,以及该扩展在并行读取和写入时都是安全的。
使用扩展¶
扩展必须在您的 conf.py 文件中声明,以便 Sphinx 识别它。这里需要两个步骤
使用
sys.path.append将_ext目录添加到 Python 路径。这应该放在文件的顶部。更新或创建
extensions列表,并将扩展文件名添加到列表中
例如
import sys
from pathlib import Path
sys.path.append(str(Path('_ext').resolve()))
extensions = ['helloworld']
您现在可以在文件中使用此扩展。例如
Some intro text here...
.. hello:: world
Some text with a :hello:`world` role.
上述示例将生成
Some intro text here...
Hello world!
Some text with a hello world! role.
延伸阅读¶
这是一个创建新角色和指令的扩展的最基本原理。
有关更高级的示例,请参阅 扩展构建过程。
如果您希望在多个项目或与他人共享您的扩展,请查看第三方扩展部分。