使用 Sphinx 构建自定义文档工作流程

创建最适合您的文档方式。
341 位读者喜欢这篇文章。
hands programming

WOCinTech Chat。由 Opensource.com 修改。CC BY-SA 4.0

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 可以找到角色和指令,因为这些是域的一部分。

该域还充当本示例中对象的中心目录。使用初始数据,它定义了两个变量 objectsobj2ingredient。这些变量包含已定义对象(所有食谱)的列表,以及将规范配料名称映射到对象列表的哈希。

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() 添加目标(链接到)和此节点的索引条目

创建索引

IngredientIndexRecipeIndex 都派生自 Sphinx 的 Index 类。它们实现了自定义逻辑以生成定义索引的值元组。请注意,RecipeIndex 是一个退化索引,只有一个条目。将其扩展以涵盖更多对象类型,并将 RecipeDomain 迁移到 CookbookDomain 尚未成为代码的一部分。

这两个索引都使用方法 generate() 来完成它们的工作。此方法结合了我们域中的信息,对其进行排序,并将其返回到 Sphinx 将接受的列表结构中。有关更多信息,请参阅 Sphinx Domain API 页面。

首次访问 Domain API 页面时,您可能会对结构感到有些不知所措。但我们的配料索引只是一个元组列表,例如 ('tomato', 'TomatoSoup', 'test', 'rec-TomatoSoup',...)

引用食谱

添加交叉引用并不困难(但也不是理所当然的)。将 XRefRole 添加到域并实现方法 resolve_xref()。拥有用于引用类型的自定义角色使我们能够明确地引用任何对象,即使两个对象具有相同的名称。如果您查看 Domainresolve_xref() 的参数,您会看到 typtarget。这些定义了交叉引用类型及其目标名称。我们将使用 target 从域的 objects 中解析我们的目标,因为我们目前只有一种类型的节点。

我们可以通过以下方式将交叉引用角色添加到 RecipeDomain

roles = {
    'reref': XRefRole()
}

我们没有什么需要实现的。定义一个工作的 resolve_xref() 并将 XRefRole 附加到域就是您需要做的全部事情。

进一步阅读

如果您想了解更多信息,请查看以下资源

标签
User profile image.
马克是德国汉堡 MaibornWolff 公司的 IT 架构师,在 DevOps 和云原生部门工作。在工作中,他尽可能地回馈开源社区。当不工作时,他也会抽出一些时间进行开源工作。除此之外,他对文档和文档工作流程也很感兴趣。马克是 BJCP 认证的啤酒评委,拥有一台相机。

1 条评论

我逐渐将我的食谱演变成 ePub 格式。这是一种我可以轻松创建的格式,然后可以保存在我的台式机、平板电脑和手机上——在购物时在手机上很有用。(我是我们家的厨师)
我有很多食谱书,但我使用的食谱分散在其中,并且在许多情况下,我使用它们来获得灵感,并进行了一些重要的个人修改。我也在互联网上浏览过其他食谱,有些是我自己的个人创作。
我制作目录,但我不觉得需要让它们像数据库一样工作。

知识共享许可协议本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.