Développement d’une extension « Hello world »

L’objectif de ce tutoriel est de créer une extension très simple qui ajoute une nouvelle directive affichant un paragraphe contenant « Hello world ».

Ce tutoriel ne comporte que des informations de base. Pour plus d’informations se référer à other tutorials qui entre plus dans les détails.

Avertissement

Pour cette extension, vous devez avoir quelques notions de docutils et Python.

Résumé

Nous voulons que l’extension ajoute les éléments suivants à Sphinx :

  • Une directive « Helloworld » qui affichera simplement le texte « Hello world ».

Pré-requis

We will not be distributing this plugin via PyPI and will instead include it as part of an existing project. This means you will need to use an existing project or create a new one using sphinx-quickstart.

We assume you are using separate source (source) and build (build) folders. Your extension file could be in any folder of your project. In our case, let’s do the following:

  1. Create an _ext folder in source

  2. Create a new Python file in the _ext folder called helloworld.py

Voici un exemple d’arborescence que vous pourriez obtenir :

└── source
    ├── _ext
    │   └── helloworld.py
    ├── _static
    ├── conf.py
    ├── somefolder
    ├── index.rst
    ├── somefile.rst
    └── someotherfile.rst

Écriture de l’extension

Ouvrir helloworld.py et copier le code suivant à l’intérieur

 1from docutils import nodes
 2from docutils.parsers.rst import Directive
 3
 4from sphinx.application import Sphinx
 5from sphinx.util.typing import ExtensionMetadata
 6
 7
 8class HelloWorld(Directive):
 9    def run(self):
10        paragraph_node = nodes.paragraph(text='Hello World!')
11        return [paragraph_node]
12
13
14def setup(app: Sphinx) -> ExtensionMetadata:
15    app.add_directive('helloworld', HelloWorld)
16
17    return {
18        'version': '0.1',
19        'parallel_read_safe': True,
20        'parallel_write_safe': True,
21    }

Some essential things are happening in this example, and you will see them for all directives.

The directive class

Our new directive is declared in the HelloWorld class.

1from sphinx.util.typing import ExtensionMetadata
2
3
4class HelloWorld(Directive):
5    def run(self):

This class extends the docutilsDirective class. All extensions that create directives should extend this class.

This class contains a run method. This method is a requirement and it is part of every directive. It contains the main logic of the directive and it returns a list of docutils nodes to be processed by Sphinx. These nodes are docutils” way of representing the content of a document. There are many types of nodes available: text, paragraph, reference, table, etc.

La classe nodes.paragraph crée un nouveau noeud de paragraphe. Un nœud de paragraphe contient généralement du texte que nous pouvons définir pendant l’instanciation en utilisant le paramètre text.

The setup function

This function is a requirement. We use it to plug our new directive into Sphinx.

 1
 2
 3def setup(app: Sphinx) -> ExtensionMetadata:
 4    app.add_directive('helloworld', HelloWorld)
 5
 6    return {
 7        'version': '0.1',
 8        'parallel_read_safe': True,
 9        'parallel_write_safe': True,
10    }

The simplest thing you can do is to call the add_directive() method, which is what we’ve done here. For this particular call, the first argument is the name of the directive itself as used in a reST file. In this case, we would use helloworld. For example:

Some intro text here...

.. helloworld::

Some more text here...

We also return the extension metadata that indicates the version of our extension, along with the fact that it is safe to use the extension for both parallel reading and writing.

Using the extension

The extension has to be declared in your conf.py file to make Sphinx aware of it. There are two steps necessary here:

  1. Add the _ext directory to the Python path using sys.path.append. This should be placed at the top of the file.

  2. Update or create the extensions list and add the extension file name to the list

Par exemple :

import os
import sys

sys.path.append(os.path.abspath("./_ext"))

extensions = ['helloworld']

Astuce

We’re not distributing this extension as a Python package, we need to modify the Python path so Sphinx can find our extension. This is why we need the call to sys.path.append.

You can now use the extension in a file. For example:

Some intro text here...

.. helloworld::

Some more text here...

L’exemple ci-dessus générera :

Some intro text here...

Hello World!

Some more text here...

Lectures complémentaires

C’est un principe très basique d’une extension que de créer une nouvelle directive.

Pour des exemple plus complet, se référer à Developing a « TODO » extension.