7.6. Sphinx中功能的扩展开发#
Sphinx远远不仅仅使您能够使用预定义标签设置文本样式。 它允许您通过定义新的角色和指令来调整和自动化文档。 角色是一个单词元素,通常在文档中以内联方式呈现,而指令可以包含更复杂的内容。 这些可以包含在一个域中 。
Sphinx 域是指令和角色以及其他一些东西(例如索引定义)的集合。
您的下一个Sphinx域可能是一种特定的编程语言(Sphinx是为创建Python文档而开发的)。
或者,您可能有一个命令行工具可以反复执行相同的命令模式(例如, 工具 <command> --args
)。
您可以使用自定义域对其进行记录,并在此过程中添加指令和索引。
这是我们食谱域中的一个示例:
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.
这使我们的食谱可以显示在两个索引中:第一个列出所有食谱,第二个列出按成分列出的所有食谱。
7.6.1. 域中有什么?#
Sphinx域是一个专门的容器,将角色,指令和索引等联系在一起。 该域具有一个名称( rcp ),用于在文档源中寻址其组件。 它在软件包的setup()方法中向Sphinx宣布其存在。 Sphinx可以从那里找到角色和指令,因为它们是域的一部分。
此域还充当此样本中对象的中央目录。 使用初始数据,它定义了两个变量, 对象和obj2ingredient 。 这些包含所有已定义对象的列表( 所有配方 )和将规范成分名称映射到对象列表的哈希。
initial_data = {
'objects': [], # object list
'obj2ingredient': {}, # ingredient -> [objects]
}
在整个扩展中,我们使用对象命名的方式很常见。
对于创建的每个对象,规范名称为 rcp.<typename>.<objectname>
,
其中 <typename>
是对象的Python类型,而 <objectname>
是文档编写者提供的对象名称。
这使扩展能够使用共享相同名称的不同对象类型。
为我们的对象指定规范的名称和中心位置是一个巨大的优势。 我们的索引和交叉引用代码都使用此功能。
7.6.2. 自定义角色和指令#
在我们的示例中, .. 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指定将有更多的重组文本作为此节点的子级。
我们还实现了多种方法:
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',...)
。
7.6.3. 参考配方#
添加交叉引用并不困难(但这也不是给定的)。
将XRefRole添加到域中并实现方法 resolve_xref()
。
通过使用自定义角色来引用类型,即使两个对象具有相同的名称,我们也可以明确地引用任何对象。
如果您在Domain中查看 resolve_xref()
的参数,则会看到 typ
和 target
。
这些定义了交叉引用类型及其目标名称。
我们将使用target来从域的对象中解析目标,因为我们目前只有一种类型的节点。
我们可以通过以下方式将交叉引用角色添加到 RecipeDomain
:
roles = {
'reref': XRefRole()
}
我们没有什么可以实现的。 您只需要做的就是定义一个有效的 resolve_xref()
并将 XRefRole
附加到域。