Erweitern der Syntax mit Rollen und Direktiven¶
Übersicht¶
Die Syntax von reStructuredText und MyST kann durch die Erstellung neuer Direktiven – für Block-Level-Elemente – und Rollen – für Inline-Elemente – erweitert werden.
In diesem Tutorial erweitern wir Sphinx um
eine
hello-Rolle, die einfach den TextHallo {text}!ausgibt.eine
hello-Direktive, die einfach den TextHallo {text}!als Absatz ausgibt.
Für diese Erweiterung benötigen Sie grundlegende Python-Kenntnisse, und wir werden auch Aspekte der docutils-API einführen.
Einrichten des Projekts¶
Sie können entweder ein bestehendes Sphinx-Projekt verwenden oder mit sphinx-quickstart ein neues erstellen.
Damit fügen wir die Erweiterung zum Projekt hinzu, innerhalb des source-Ordners.
Erstellen Sie einen
_ext-Ordner insource.Erstellen Sie eine neue Python-Datei im
_ext-Ordner mit dem Namenhelloworld.py.
Hier ist ein Beispiel für die Ordnerstruktur, die Sie erhalten könnten
└── source
├── _ext
│ └── helloworld.py
├── conf.py
├── index.rst
Schreiben der Erweiterung¶
Öffnen Sie helloworld.py und fügen Sie den folgenden Code ein.
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 }
In diesem Beispiel geschehen einige wesentliche Dinge.
Die Rollenklasse¶
Unsere neue Rolle wird in der HelloRole-Klasse deklariert.
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], []
Diese Klasse erweitert die Klasse SphinxRole. Die Klasse enthält eine run-Methode, die für jede Rolle erforderlich ist. Sie enthält die Hauptlogik der Rolle und gibt ein Tupel zurück, das Folgendes enthält:
eine Liste von Inline-Docutils-Knoten, die von Sphinx verarbeitet werden sollen.
eine (optionale) Liste von Systemmeldungs-Knoten.
Die Direktivenklasse¶
Unsere neue Direktive wird in der HelloDirective-Klasse deklariert.
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]
Diese Klasse erweitert die Klasse SphinxDirective. Die Klasse enthält eine run-Methode, die für jede Direktive erforderlich ist. Sie enthält die Hauptlogik der Direktive und gibt eine Liste von Block-Level-Docutils-Knoten zurück, die von Sphinx verarbeitet werden sollen. Sie enthält außerdem ein Attribut required_arguments, das Sphinx mitteilt, wie viele Argumente für die Direktive erforderlich sind.
Was sind docutils-Knoten?¶
Wenn Sphinx ein Dokument analysiert, erstellt es einen "Abstract Syntax Tree" (AST) von Knoten, die den Inhalt des Dokuments in einer strukturierten Weise darstellen, die im Allgemeinen unabhängig von einem einzelnen Eingabeformat (rST, MyST usw.) oder Ausgabeformat (HTML, LaTeX usw.) ist. Es ist ein Baum, da jeder Knoten Kindknoten haben kann und so weiter.
<document>
<paragraph>
<text>
Hello world!
Das Paket docutils bietet viele eingebaute Knoten, um verschiedene Arten von Inhalten wie Text, Absätze, Referenzen, Tabellen usw. darzustellen.
Jeder Knotentyp akzeptiert im Allgemeinen nur eine bestimmte Menge an direkten Kindknoten. Zum Beispiel sollte der document-Knoten nur "Block-Level"-Knoten enthalten, wie paragraph, section, table usw., während der paragraph-Knoten nur "Inline-Level"-Knoten enthalten sollte, wie text, emphasis, strong usw.
Siehe auch
Die docutils-Dokumentation zum Erstellen von Direktiven und Erstellen von Rollen.
Die Funktion setup¶
Diese Funktion ist erforderlich. Wir verwenden sie, um unsere neue Direktive in Sphinx einzubinden.
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,
}
Das Einfachste, was Sie tun können, ist, die Methoden Sphinx.add_role() und Sphinx.add_directive() aufzurufen, was wir hier getan haben. Für diesen spezifischen Aufruf ist das erste Argument der Name der Rolle/Direktive selbst, wie er in einer reStructuredText-Datei verwendet wird. In diesem Fall würden wir hello verwenden. Zum Beispiel:
Some intro text here...
.. hello:: world
Some text with a :hello:`world` role.
Wir geben auch die Erweiterungsmetadaten zurück, die die Version unserer Erweiterung angeben und dass die Erweiterung sowohl für paralleles Lesen als auch Schreiben sicher verwendet werden kann.
Verwenden der Erweiterung¶
Die Erweiterung muss in Ihrer conf.py-Datei deklariert werden, damit Sphinx sie erkennt. Hier sind zwei Schritte notwendig:
Fügen Sie das Verzeichnis
_extmitsys.path.appendzum Python-Pfad hinzu. Dies sollte am Anfang der Datei platziert werden.Aktualisieren oder erstellen Sie die Liste
extensionsund fügen Sie den Namen der Erweiterungsdatei zur Liste hinzu.
Zum Beispiel
import sys
from pathlib import Path
sys.path.append(str(Path('_ext').resolve()))
extensions = ['helloworld']
Tipp
Da wir unsere Erweiterung nicht als Python-Paket installiert haben, müssen wir den Python-Pfad ändern, damit Sphinx unsere Erweiterung finden kann. Deshalb benötigen wir den Aufruf von sys.path.append.
Sie können die Erweiterung jetzt in einer Datei verwenden. Zum Beispiel:
Some intro text here...
.. hello:: world
Some text with a :hello:`world` role.
Das obige Beispiel würde Folgendes generieren:
Some intro text here...
Hello world!
Some text with a hello world! role.
Weitere Lektüre¶
Dies ist das sehr grundlegende Prinzip einer Erweiterung, die eine neue Rolle und Direktive erstellt.
Für ein fortgeschritteneres Beispiel siehe Erweiterung des Build-Prozesses.
Wenn Sie Ihre Erweiterung über mehrere Projekte oder mit anderen teilen möchten, lesen Sie den Abschnitt Drittanbieter-Erweiterungen.