Sphinx 是一款流行的文档创建应用程序,类似于 JavaDoc 或 Jekyll。但是,Sphinx 的 reStructuredText 输入允许比其他工具更高程度的自定义。
本教程将解释如何自定义 Sphinx 以适应您的工作流程。您可以按照 GitHub 上的示例代码进行操作。
一些定义
Sphinx 的功能远不止让您使用预定义的标签来设置文本样式。它允许您通过定义新的角色和指令来塑造和自动化您的文档。角色 是一个通常在文档中以内联方式呈现的单字元素,而指令 可以包含更复杂的内容。这些可以包含在域中。
Sphinx 域 是指令和角色的集合,以及其他一些东西,例如索引定义。您的下一个 Sphinx 域可能是一种特定的编程语言(Sphinx 的开发目的是创建 Python 的文档)。或者您可能有一个命令行工具,它一遍又一遍地实现相同的命令模式(例如,tool <command> --args)。您可以使用自定义域来记录它,并在过程中添加指令和索引。
以下是我们 recipe 域中的一个示例
The recipe contains `tomato` and `cilantro`.
.. rcp:recipe:: TomatoSoup
:contains: tomato cilantro salt pepper
This recipe is a tasty tomato soup, combine all ingredients
and cook.
现在我们已经定义了食谱 TomatoSoup,我们可以在文档中的任何位置使用自定义角色 refef 来引用它。例如
You can use the :rcp:reref:`TomatoSoup` recipe to feed your family.
这使我们的食谱能够出现在两个索引中:第一个索引列出所有食谱,第二个索引按配料列出所有食谱。
域中有什么?
Sphinx 域是一个专门的容器,它将角色、指令和索引以及其他内容联系在一起。该域有一个名称 (rcp),用于在文档源中寻址其组件。它在软件包的 setup() 方法中向 Sphinx 声明其存在。从那里,Sphinx 可以找到角色和指令,因为这些是域的一部分。
该域还充当本示例中对象的中心目录。使用初始数据,它定义了两个变量 objects 和 obj2ingredient。这些变量包含已定义对象(所有食谱)的列表,以及将规范配料名称映射到对象列表的哈希。
initial_data = {
'objects': [], # object list
'obj2ingredient': {}, # ingredient -> [objects]
}
我们命名对象的方式在我们的扩展中是通用的。对于创建的每个对象,规范名称是 rcp.<typename>.<objectname>,其中 <typename> 是对象的 Python 类型,<objectname> 是文档编写者给对象的名称。这使扩展能够使用共享相同名称的不同对象类型。
拥有规范名称和对象的中心位置是一个巨大的优势。我们的索引和交叉引用代码都使用了此功能。
自定义角色和指令
在我们的示例中,.. rcp:recipe:: 表示自定义指令。您可能会认为为这些项目创建自定义语法过于具体,但这说明了您可以在 Sphinx 中获得的自定义程度。这提供了丰富的标记,可以构建文档并生成更好的文档。专业化使我们能够从文档中提取信息。
我们对此指令的定义将提供最少的格式,但它将是功能性的。
class RecipeNode(ObjectDescription):
"""A custom node that describes a recipe."""
required_arguments = 1
option_spec = {
'contains': rst.directives.unchanged_required
}
对于此指令,required_arguments 告诉 Sphinx 期望一个参数,即食谱名称。option_spec 列出了可选参数,包括它们的名称。最后,has_content 指定将有更多 reStructuredText 作为此节点的子节点。
我们还实现了多种方法
- handle_signature() 实现了解析指令的签名并将对象的名称和类型传递给其超类
- add_taget_and_index() 添加目标(链接到)和此节点的索引条目
创建索引
IngredientIndex 和 RecipeIndex 都派生自 Sphinx 的 Index 类。它们实现了自定义逻辑以生成定义索引的值元组。请注意,RecipeIndex 是一个退化索引,只有一个条目。将其扩展以涵盖更多对象类型,并将 RecipeDomain 迁移到 CookbookDomain 尚未成为代码的一部分。
这两个索引都使用方法 generate() 来完成它们的工作。此方法结合了我们域中的信息,对其进行排序,并将其返回到 Sphinx 将接受的列表结构中。有关更多信息,请参阅 Sphinx Domain API 页面。
首次访问 Domain API 页面时,您可能会对结构感到有些不知所措。但我们的配料索引只是一个元组列表,例如 ('tomato', 'TomatoSoup', 'test', 'rec-TomatoSoup',...)。
引用食谱
添加交叉引用并不困难(但也不是理所当然的)。将 XRefRole 添加到域并实现方法 resolve_xref()。拥有用于引用类型的自定义角色使我们能够明确地引用任何对象,即使两个对象具有相同的名称。如果您查看 Domain 中 resolve_xref() 的参数,您会看到 typ 和 target。这些定义了交叉引用类型及其目标名称。我们将使用 target 从域的 objects 中解析我们的目标,因为我们目前只有一种类型的节点。
我们可以通过以下方式将交叉引用角色添加到 RecipeDomain
roles = {
'reref': XRefRole()
}
我们没有什么需要实现的。定义一个工作的 resolve_xref() 并将 XRefRole 附加到域就是您需要做的全部事情。
进一步阅读
如果您想了解更多信息,请查看以下资源
1 条评论