Developing a "Hello world" extension ==================================== The objective of this tutorial is to create a very basic extension that adds a new directive. This directive will output a paragraph containing "hello world". Only basic information is provided in this tutorial. For more information, refer to the :doc:`other tutorials ` that go into more details. .. warning:: For this extension, you will need some basic understanding of docutils_ and Python. Overview -------- We want the extension to add the following to Sphinx: * A ``helloworld`` directive, that will simply output the text "hello world". Prerequisites ------------- 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 :program:`sphinx-quickstart`. We assume you are using separate source (:file:`source`) and build (:file:`build`) folders. Your extension file could be in any folder of your project. In our case, let's do the following: #. Create an :file:`_ext` folder in :file:`source` #. Create a new Python file in the :file:`_ext` folder called :file:`helloworld.py` Here is an example of the folder structure you might obtain: .. code-block:: text └── source    ├── _ext │   └── helloworld.py    ├── _static    ├── conf.py    ├── somefolder    ├── index.rst    ├── somefile.rst    └── someotherfile.rst Writing the extension --------------------- Open :file:`helloworld.py` and paste the following code in it: .. literalinclude:: examples/helloworld.py :language: python :linenos: Some essential things are happening in this example, and you will see them for all directives. .. rubric:: The directive class Our new directive is declared in the ``HelloWorld`` class. .. literalinclude:: examples/helloworld.py :language: python :linenos: :lines: 5-9 This class extends the docutils_' ``Directive`` class. All extensions that create directives should extend this class. .. seealso:: `The docutils documentation on creating directives `_ 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. .. seealso:: `The docutils documentation on nodes `_ The ``nodes.paragraph`` class creates a new paragraph node. A paragraph node typically contains some text that we can set during instantiation using the ``text`` parameter. .. rubric:: The ``setup`` function .. currentmodule:: sphinx.application This function is a requirement. We use it to plug our new directive into Sphinx. .. literalinclude:: examples/helloworld.py :language: python :linenos: :lines: 12- The simplest thing you can do is to call the :meth:`~Sphinx.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: .. code-block:: rst Some intro text here... .. helloworld:: Some more text here... We also return the :ref:`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 :file:`conf.py` file to make Sphinx aware of it. There are two steps necessary here: #. Add the :file:`_ext` directory to the `Python path`_ using ``sys.path.append``. This should be placed at the top of the file. #. Update or create the :confval:`extensions` list and add the extension file name to the list For example: .. code-block:: python import os import sys sys.path.append(os.path.abspath("./_ext")) extensions = ['helloworld'] .. tip:: 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: .. code-block:: rst Some intro text here... .. helloworld:: Some more text here... The sample above would generate: .. code-block:: text Some intro text here... Hello World! Some more text here... Further reading --------------- This is the very basic principle of an extension that creates a new directive. For a more advanced example, refer to :doc:`todo`. .. _docutils: https://docutils.sourceforge.io/ .. _docutils directives: https://docutils.sourceforge.io/docs/howto/rst-directives.html .. _docutils nodes: https://docutils.sourceforge.io/docs/ref/doctree.html .. _PyPI: https://pypi.org/ .. _Python package: https://packaging.python.org/ .. _Python path: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH