Automatically Generate Documentation with Sphinx

Posted on Mon 03 February 2020 in Python • 5 min read

Document code automatically through docstrings with Sphinx

This post goes into how to generate documentation for your python projects automatically with Sphinx!

First off we have to install sphinx into our virtual environment. Pending on your flavour, we can do any of the following

1
2
3
pip install sphinx
conda install sphinx
pipenv install sphinx

Once you have installed sphinx, inside the project (let's use the directory of this blog post), we can create a docs folder in which all our documentation will live.

1
2
mkdir docs
cd docs

Ensuring to have our virtual environment with sphinx installed active, we run sphinx-quickstart, this tool allows us to populate some information for our documentation in a nice Q&A style.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Welcome to the Sphinx 2.3.1 quickstart utility.

Please enter values for the following settings (just press Enter to
accept a default value, if one is given in brackets).

Selected root path: .

You have two options for placing the build directory for Sphinx output.
Either, you use a directory "_build" within the root path, or you separate
"source" and "build" directories within the root path.
> Separate source and build directories (y/n) [n]: y

The project name will occur in several places in the built documentation.
> Project name: SphinxDemo
> Author name(s): Jack McKew
> Project release []:

If the documents are to be written in a language other than English,
you can select a language here by its language code. Sphinx will then
translate text that it generates into that language.

For a list of supported codes, see
https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language.
> Project language [en]:

Creating file .\source\conf.py.
Creating file .\source\index.rst.
Creating file .\Makefile.
Creating file .\make.bat.

Finished: An initial directory structure has been created.

You should now populate your master file .\source\index.rst and create other documentation
source files. Use the Makefile to build the docs, like so:
   make builder
where "builder" is one of the supported builders, e.g. html, latex or linkcheck.

Now let's create an example package that we can write some documentation in.

1
2
mkdir sphinxdemo
cd sphinxdemo

Then we create 3 files inside our example package:

1
__init__.py
download
1
version = "0.1.1"
1
__main__.py
download
1
2
3
4
5
6
from .file_functions import get_files_in_folder

if __name__ == "__main__":
    py_files = get_files_in_folder(".", extension=".py")

    print(py_files)
1
file_functions.py
download
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import os


def get_files_in_folder(path, extension):
    f = []
    for (dirpath, dirnames, filenames) in os.walk(path):
        if extension:
            for filename in filenames:
                if filename.endswith(extension):
                    f.append(filename)
        else:
            f.extend(filenames)
    return f

If you are using VS Code to use packages with debugging, change your launch.json with the following: "configurations": [ { "name": "Python: Module - sphinxdemo", "type": "python", "request": "launch", "module": "sphinxdemo.__main__" }

To add documentation within our source code, we use docstrings. There are many available styles of docstrings out there, my personal preference is Google Docstring Style.

We need to enable the napoleon sphinx extensions in docs/conf.py for this style to work.

The resulting documented code will look like:

1
__init__.py
download
1
2
3
4
5
6
""" Initialisation file for package sphinxdemo-with-docs

Declare any package wide variables here
"""

version = "0.1.1"
1
__main__.py
download
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
""" Main runtime for sphinxdemo-with-docs package

__main__.py file used within package to work with `python -m` functionality.

Prints out list of all *.py files within current directory when run
"""
from .file_functions import get_files_in_folder

if __name__ == "__main__":
    py_files = get_files_in_folder(".", extension=".py")

    print(py_files)
1
file_functions.py
download
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
""" Functions for parsing folders for files.
"""

import os


def get_files_in_folder(path, extension):
    """
    Prints all files in folder, if an extension is given, will only print the files with the given extension

    Args:
        path (string): folder to recursively search through for specific extensions
        extension (string): extension of file type to filter by

    Returns:
        list: list of all filenames within path with matching extension
    """
    f = []
    for (dirpath, dirnames, filenames) in os.walk(path):
        if extension:
            for filename in filenames:
                if filename.endswith(extension):
                    f.append(filename)
        else:
            f.extend(filenames)
    return f

Now at a minimum our source code is documented, now to present these docstrings in a format that we can share with others (html).

First we need to set the sphinx configuration, the file which contains this (we generated with sphinx-quickstart) is located in docs/source/conf.py.

We are going to utilise the following sphinx extensions (they are all in-built into sphinx):

Our conf.py file for sphinx's configuration results in:

Sphinx Configuration File conf.py download
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys

sys.path.insert(0, os.path.abspath("../.."))


# -- Project information -----------------------------------------------------

project = "SphinxDemo"
copyright = "2020, Jack McKew"
author = "Jack McKew"


# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.napoleon",
    "sphinx.ext.viewcode",
    "sphinx.ext.autosummary",
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

autosummary_generate = True

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = "alabaster"

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]

We must also set our index.rst (restructured text) with what we want to see in our documentation.

Documentation Index File index.rst download
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.. SphinxDemo documentation master file, created by
   sphinx-quickstart on Tue Feb  4 20:05:16 2020.
   You can adapt this file completely to your liking, but it should at least
   contain the root `toctree` directive.

Welcome to SphinxDemo's documentation!
======================================

.. toctree::
   :maxdepth: 4
   :caption: Contents:

.. rubric:: Modules

.. autosummary::
   :toctree: generated

   sphinxdemo_with_docs.__init__
   sphinxdemo_with_docs.__main__
   sphinxdemo_with_docs.file_functions
   

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

To generate individual pages for our modules, classes and functions, we define separate templates, these are detailed here: autosummary templates

Next we navigate our docs directory, and finally run:

1
make html

This will generate all the stubs for our documentation and compile them into HTML format.

Generated Docs