Directives#

As defined by the reStructuredText documentation, the directives are an extension mechanism. This means that developers can add functionality as they see fit. And, if you want to learn and do something not currently possible, you can do so.

However, for most of us not planning to extend current capabilities, directives are complex instructions that abide to the following syntax:

.. directive-name:: arguments
   :option1:
   :option2:

   content

There is a lot to unpack in the code shown above. First, let’s talk about the directive marker, which includes three things:

  1. The starting two periods and a space (starting almost like a comment).

  2. The directive-name, which will identify what the directive will be.

  3. The two colons, which wraps the directive marker.

The directive marker is required (all three parts already mentioned). However, there are other three parts of a directive we haven’t mentioned: arguments, options, and content.

The distinction between arguments, options and content are up to the directive, so an explanation without some context or knowledge of a few directives would be a headache.

Let’s jump straight into our first directive: image.

Image#

The image directive is used with a required argument: the directory, path, or URL of the image. For instance, here I include my logo, hosted on my personal domain:

http://ramoscarlos.com/logo.png

All I had to type was:

.. image:: http://ramoscarlos.com/logo.png

However, depending on your browser or e-reader, the width of the image might be too much. As reStructuredText did no processing to the image (like scaling or setting maximum width), the limits are imposed by the device.

As this may not be what is required for the image, we have options to modify how the image is shown. Some of the options for the image directive are:

height:

is the height of the image, use to reserve the vertical space. When used along with scale, both parameters are applied (i.e. if height is 100px, and scale is 40%, then final height will be 40px).

width:

is the width of the image, which can be given as an absolute value or relative percentage. As height, this can also be combined with scale.

scale:

is the uniform scaling factor of the image. IT’s value is expressed in percentage, and its default value is 100% (which means no scaling).

align:

defines where the image will be placed relative to its container. Values can be top, middle, bottom for vertical alignment, and left, center, o right for horizontal alignment.

alt:

is an alternative text that is displayed in case the image cannot be loaded.

target:

transforms the image into a link, that points to the URL specified in this field.

Let’s first test the :alt: option with a non-existant image:

This text should be displayed instead

And that magic was done with the code:

.. image:: http://this-image.shall/not-exits.gif
    :alt: This text should be displayed instead

To solve the size issue, we can use the option width, with a percentage value of 60%. As align is not used, image will be left-aligned:

.. image:: http://ramoscarlos.com/logo.png
    :width: 60%
    :alt: ramoscarlos.com logo
ramoscarlos.com logo

Local images#

The image directive is not limited to online resources. However, that is something that cannot be tested on the online editor. In case you want to try this out in your machine, the best way to use reStructuredText locally is to install Python3 and Sphinx.

When working locally, you can use the relative path of the image in the directive:

.. image:: img/image-name.png

Sphinx is out of scope on this book, but feel free to try it.

Figures#

A figure is an image with a caption, and an optional legend. Its syntax is similar to the image directive, even the options are the same. The difference? figure uses the content of the directive as the caption and legent to the image. The syntax is:

.. figure:: img/image-name.png

    Caption (for a title)

    Legend (for a longer description, typically underneath the image and caption)

You can take one of the examples above and add the caption on the directive content:

.. figure:: http://ramoscarlos.com/logo.png
    :width: 60%
    :alt: ramoscarlos.com logo

    ramoscarlos.com logo (with legend)

That is all that we need to have an image with a caption below:

ramoscarlos.com logo

ramoscarlos.com logo (with legend)#

Code (Embedded)#

As far as code goes, we are not limited to preformatting. We can include source code with syntax highlighting with the code directive, that receives the language as an argument. The syntax highlighting is provided by the Pygments library, which means that all the languages supported by the library are available for your documentation purposes.

As an example, here is a fragment of C code:

int main() {
    return 0;
}

This code shows the right coloring thanks to the language argument:

.. code:: c

    int main() {
        return 0;
    }

You can highlight reStructuredText:

This has *emphasis*, while this has **bold face**.

And the code for the directive example is:

.. code:: rst

    This has *emphasis*, while this has **bold face**.

Note

The online editor does not load the syntax highlighting styles, as that means more processing. Yes, it’s frustrating. However, doesn’t that make you want to try Shpinx even more?

number-lines option#

The code directive has an option called number-lines, which allows us to add line numbers at the beginning of each line. For instance, we can add them to the C sample:

.. code:: c
    :number-lines:

    int main() {
        return 0;
    }

Which will now display the code as:

1int main() {
2    return 0;
3}

If we want to imply this is a fragment, we can add the option argument after the option colon:

.. code:: c
    :number-lines: 50

    int main() {
        return 0;
    }

Thus, the first printed line of code will be numbered as line 50:

50int main() {
51    return 0;
52}

By the way: if you miss the space between the option and its argument (i.e. :number-lines:50), you will get a warning and the code will not show up:

WARNING: Error in "code" directive:
maximum 1 argument(s) allowed, 2 supplied.

.. code:: c
        :number-lines:50

        int main() {
                return 0;
        }

Code (external file)#

Rather than copying the source code into the reStructuredText entry, you can rather include its full contents by reference, with the include directive. As with the local image example, this is something that you cannot test in the online editor.

As the image directive, include uses its first argument to reference the source code file the directive will include in the documentation. For the purposes of this example, the sourcecode files will be kept at the subdirectory src.

The demo here points to the Python’s official documentation, stored locally as demo.py. To include the file we type:

.. include:: src/demo.py

Which outputs the code into the book:

def scope_test():
def do_local():

spam = “local spam”

def do_nonlocal():

nonlocal spam spam = “nonlocal spam”

def do_global():

global spam spam = “global spam”

spam = “test spam” do_local() print(“After local assignment:”, spam) do_nonlocal() print(“After nonlocal assignment:”, spam) do_global() print(“After global assignment:”, spam)

scope_test() print(“In global scope:”, spam)

Well, what lies above is a poor attempt at including the code, at best. There is no syntax highlighting, and some end of line characters were striped, too. This was because the interpreter believed that the included source file was reStructuredText (it does that by default).

That is, the include directive is to make a full document with reStructuredText, like including a chapter rst file within a book rst file. However, the common usage of this directive is to add source code. To make it act as such, we need to use the code option.

code option#

The errors in trying to parse code as reStructuredText can go away using the code option, which receives as its argument the language the code is written in. In essence, its the same as the code directive argument (that it, it can take any Pygment-supported language). Let’s try:

.. include:: src/demo.py
    :code: python

Now the code displays as Python code, and lines are crammed together:

def scope_test():
        def do_local():
                spam = "local spam"

        def do_nonlocal():
                nonlocal spam
                spam = "nonlocal spam"

        def do_global():
                global spam
                spam = "global spam"

        spam = "test spam"
        do_local()
        print("After local assignment:", spam)
        do_nonlocal()
        print("After nonlocal assignment:", spam)
        do_global()
        print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

number-lines option#

The number-lines works just as the option on the code directive… in theory:

 1 def scope_test():
 2         def do_local():
 3                 spam = "local spam"
 4 
 5         def do_nonlocal():
 6                 nonlocal spam
 7                 spam = "nonlocal spam"
 8 
 9         def do_global():
10                 global spam
11                 spam = "global spam"
12 
13         spam = "test spam"
14         do_local()
15         print("After local assignment:", spam)
16         do_nonlocal()
17         print("After nonlocal assignment:", spam)
18         do_global()
19         print("After global assignment:", spam)
20 
21 scope_test()
22 print("In global scope:", spam)

As you can see, the syntax colors are gone. Lesson: use code if coloring needs to be accomplished along with line numbers.

start-line/end-line options#

A fragment of code can also be included specifying a starting line numbers, up to an ending line number. For instance, to include only the do_global function (lines 9 to 11), the option start-line can be used with a value of 8 (as the count starts at 0). Therefore, end-line option will have an 11, as the last number does not get printed:

.. include:: src/demo.py
    :code: python
    :start-line: 8
    :end-line: 11

Which shows the three target lines of code:

        def do_global():
                global spam
                spam = "global spam"

start-after/end-after options#

What if we want to print the do_global function not depending on the line numbers? For that, we would need to know a string we can use as a starting point. Such option is called start-after, which receives a string to search for (which will not be printed).

As the function starts after the "nonlocal spam" line, that can be used as value for the option:

.. include:: src/demo.py
    :code: python
    :start-after: "nonlocal spam"

This shows:

        def do_global():
                global spam
                spam = "global spam"

        spam = "test spam"
        do_local()
        print("After local assignment:", spam)
        do_nonlocal()
        print("After nonlocal assignment:", spam)
        do_global()
        print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

However, notice how the usage of this option yields code without syntax highlighting.

In case we want to use the end condition, we can try with spam = "test spam" as value for end-before:

.. include:: src/demo.py
    :code: python
    :start-after: "nonlocal spam"
    :end-before: spam = "test spam"

Which will yield the same code as having used the line numbers options:

        def do_global():
                global spam
                spam = "global spam"

tab-width option#

The tab-width options allows you to control how many spaces are show per tabulation character. By default, the value is set to eight. To change that to two, we can use:

.. include:: src/demo.py
    :code: python
    :tab-width: 2

With the same file, notice the difference in spaces:

def scope_test():
  def do_local():
    spam = "local spam"

  def do_nonlocal():
    nonlocal spam
    spam = "nonlocal spam"

  def do_global():
    global spam
    spam = "global spam"

  spam = "test spam"
  do_local()
  print("After local assignment:", spam)
  do_nonlocal()
  print("After nonlocal assignment:", spam)
  do_global()
  print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

In case this option yields no different output, check the file. It may use spaces instead of tabs (as any decent human being should).

Inserting HTML or LaTeX with raw#

If you feel the urge to use HTML in your reST document, is not that simple. For instance, let’s add an horizontal ruler:

<hr>

Yeah… that did not work. To insert HTML we need to use the raw directive, with the language to be used as its argument. In this example, the language is html: este caso es html:

.. raw:: html

    <hr>

That displays the ruler:


However, there is no such rulers for the PDF readers. That is because the PDF is compiled with LaTeX, not with HTML (a reason against using raw HTML). If we want to add an horizontal rule on the PDF we can use the \rule command on a LaTeX environment:

.. raw:: latex

    \noindent{\rule{\linewidth}{0.4pt}}

This will give us the horizontal ruler on the PDF, but not on the HTML page:

We can include as much HTML or LaTeX we desire, but we need to provide two instructions if we intend that our documentation looks good on a webpage, a printed book, or an ereader. To guarantee the presence of all required elements, both raw directives can be included one after the other:

.. raw:: html

    <hr>

.. raw:: latex

    \noindent{\rule{\linewidth}{0.4pt}}

This way, the webpage and the PDF version have a ruler:


Admonitions#

Admonitions are special messages. You can picture them as colored boxes: red for errors, yellow for warnings, blue for information.

The admonition is a set of directives. Yes, you have the generic admonition, but you have several others at your disposal: attention, caution, danger, error, hint, important, note, tip, and warning.

Depending on the HTML theme, each of the admonitions has a different style. The syntax is:

.. admonition-type::

    Admonition content.

For example, to display a warning admonition, type:

.. warning::

    Admonition content.

Now, each admonitions type, to compare style:

Attention

Content for type attention.

Caution

Content for type caution.

Danger

Content for type danger.

Error

Content for type error.

Hint

Content for type hint.

Important

Content for type important.

Note

Content for type note.

Tip

Content for type tip.

Warning

Content for type warning.

Each admonition type comes with a title, except the generic one. As it doesn’t come with a default title, it is required as an argument:

.. admonition:: Admonition title

    Content for type ``admonition``.

Which yields:

Admonition title

Content for type admonition.

Math#

Yes! reStructuredText is also used to write complex math equations… well, sort of. For this, it uses LaTeX. If you know LaTeX, all you need to know is that you can use it with the math directive, as:

.. math::

    \frac{1}{2} + \frac{1}{4} + \ldots = \sum_{n=1}^{\infty} \left(\frac{1}{2}\right)^n = 1

Which gets displayed as:

\[\frac{1}{2} + \frac{1}{4} + \ldots = \sum_{n=1}^{\infty} \left(\frac{1}{2}\right)^n = 1\]

If you do not understand LaTeX, I wrote a book about it (on Spanish, though).

Extensibility#

On the first chapters I talked about reST extensibility, but not much was said on the how. At the beginning of this chapter we saw that a directive is a name between the .. and the ::. In theory, we can make a directive to display an attendance list:

.. attendance-list::
    :class: Algebra

    John Smith
    Lara Croft

With some programming magic, that can print as a table with dates and everything needed. If you want to know more, you can read the Creating reStructuredText Directives documentation.

While Markdown added functionality via flavors and third-party modifications, reST was born with this extensibility in mind. There is even a way for your contributions to be added to the official repository, so people can install your extension easily.

At this point, we are done with the reStructuredText guide, and you are ready to learn about Sphinx.

Summary#

We learned about reStruturedText directives on this chapter, which are more powerful instructions, born out of the language extensibility mechanism.

We limited ourselves to look into the reST-defined directives, but you can go and see what else is out there that may help your documentation needs.