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 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 **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"

Here is an example of the folder structure you might obtain:

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


Writing the extension
=====================

Open "helloworld.py" and paste the following code in it:

   from docutils import nodes
   from docutils.parsers.rst import Directive


   class HelloWorld(Directive):

       def run(self):
           paragraph_node = nodes.paragraph(text='Hello World!')
           return [paragraph_node]


   def setup(app):
       app.add_directive("helloworld", HelloWorld)

       return {
           'version': '0.1',
           'parallel_read_safe': True,
           'parallel_write_safe': True,
       }

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.

   class HelloWorld(Directive):

       def run(self):
           paragraph_node = nodes.paragraph(text='Hello World!')
           return [paragraph_node]

This class extends the docutils' "Directive" class. All extensions
that create directives should extend this class.

See also: 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.

See also: 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.

-[ The "setup" function ]-

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

   def setup(app):
       app.add_directive("helloworld", HelloWorld)

       return {
           'version': '0.1',
           'parallel_read_safe': True,
           'parallel_write_safe': True,
       }

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

For example:

   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:

   Some intro text here...

   .. helloworld::

   Some more text here...

The sample above would generate:

   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 Developing a "TODO" extension.
