使用角色和指令擴展語法¶
概觀¶
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']
提示
因為我們還沒有將我們的擴展作為 Python 套件 安裝,所以我們需要修改 Python 路徑,以便 Sphinx 可以找到我們的擴展。這就是為什麼我們需要調用 sys.path.append
。
您現在可以在檔案中使用擴展。例如
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.
延伸閱讀¶
這是一個建立新角色和指令的擴展的基本原理。
有關更進階的範例,請參閱 擴展建置流程。
如果您希望在多個專案或與他人分享您的擴展,請查看 第三方擴展 章節。