Using Sphinx for generating documentation for Python software

Documentation is an essential aspect of any software project. It serves as a source of information for end users and as a resource for developers who need to understand how the software works. Additionally, well-written documentation helps avoid maintenance issues in the future (i.e., avoiding technical debt) and improves the overall quality of the software.

In this article, I provide a practical example of how to create documentation (section Example). I use Sphinx as the main tool for generating documentation and the Read the Docs repository for publishing it.

I demonstrate how to use the autodoc extension to enable automatic creation of the API, which extracts information from the code’s docstring automatically (section Automatic API Creation). To change the default theme to one that offers a more visually pleasing appearance, I use the sphinx_rtd_theme extension (section Defining the Documentation Theme). I also show how to publish the created documentation on Read the Docs (section Publishing on Read the Docs), allowing users to easily access the documentation through the web.

Additionally, I mention several Sphinx extensions (section Other Sphinx Features), both those included by default in Sphinx and others created by third parties and available on PyPI. These extensions provide extra functionality for the documentation, making it more versatile and useful for different types of projects.

Sphinx and Read the Docs

Sphinx

One of the most popular tools for creating documentation in Python projects is the Sphinx library. Sphinx is an open-source documentation tool that is easy to use and highly configurable. It uses the ReStructuredText (or RST) markup format, which is a lightweight and easy-to-learn markup language, to create the documentation.

One of the great advantages of Sphinx is its ability to automatically generate API documentation from the code’s own documentation. This means that by writing code documentation, the API reference can be automatically generated from that documentation, saving time and effort in creating the documentation.

Another major advantage of Sphinx is its ability to generate documentation in various formats, such as HTML, PDF, and EPUB.

Read the Docs

Sphinx can also be easily integrated with Read the Docs, an online documentation hosting service. The integration of Sphinx with Read the Docs allows the documentation to be easily published and accessible to all project users. Additionally, Read the Docs for Business allows private projects to use the platform, keeping the documentation private and secure.

Structure of Documentation

Basic Sections

Documentation can include various sections, such as getting started, user guide, API, and development. I describe the information contained in each section below.

  • Getting Started: An overview of the library, demonstrating how to quickly and efficiently use it.

  • User Guide: More detailed usage examples, with step-by-step explanations for utilizing the library.

  • API: Detailed descriptions of the different functionalities and methods of the library.

  • Development: Information on contributing to the development of the library, including details on coding standards, code structure, and testing.

Business Information

In the case of internal documentation for the team, it may be interesting to add sections that address business aspects of the team, such as project objectives, contributors, financial information, clients, planning and long-term goals, meeting history, and requirements documentation. The sections vary between projects, and it is up to the team to define the necessary information for the documentation, taking into account the unique aspects of each project.

Other Documentation Channels

It is worth noting that while many project progress-related information needs to be documented, much of it is documented through management features included in code repositories, such as GitHub and GitLab. This information includes issues, release history, information related to each sprint and deliverable, and more.

Example

In this example, I will use the log_decor library (v1) - an open-source logging decorator library that I recently developed - to demonstrate how to use Sphinx, in conjunction with the online hosting service Read the Docs, to generate and publish the documentation for a Python application.

In this text, I would like to share a practical example of how to use the Sphinx library together with the Read the Docs online documentation hosting service to create and publish the documentation for a Python library. For this purpose, we will use the log_decor library, an open-source logging decorator library that I recently developed.

Before following the example on this page, I suggest you explore the log_decor documentation on Read the Docs. This will make it easier to understand the different points that will be covered.

Project Structure

The directory structure of the log_decor library consists of several files and folders that are important for the development and documentation of the library. The structure and a brief description of each file and folder are presented below:

log_decor/
├── docs/
├── src/
│   └── log_decor/
|       ├── __init__.py
|       └── ...
├── pyproject.toml
└── requirements.txt

The docs/ folder contains all the necessary files for creating the library’s documentation.

The src/log_decor/ folder contains all the source code files of the library. These files include the Python modules, as well as other files such as the __init__.py file, which indicates that the folder is a Python package.

The pyproject.toml file is used for installing the library. This file contains important information such as the name of the library, its version, and its dependencies.

Finally, the requirements.txt file in the project root lists the requirements necessary for the library to function.

Creating the Base Documentation Files

To create the base documentation files, simply execute the command

sphinx-quickstart

in the example_project/docs/ directory. During the execution, the user is guided through a series of questions, such as the project name, the language used, the authors, and the version, among other options. These options are important to configure the documentation according to the project’s needs.

When running the sphinx-quickstart command to create the library’s documentation, some default files and folders are generated. These files are:

  • conf.py: the main configuration file for Sphinx, where general documentation settings are defined, such as the title, description, list of extensions, and other customization options.

  • index.rst: the main documentation file where the main topics are defined, such as the introduction, installation, and usage of the library. From this file, other documentation files are created.

  • make.bat (for Windows) and Makefile (for Unix-based systems): script files used to compile the .rst files into HTML, PDF, and other formats.

After executing the command, the directory structure becomes as follows:

log_decor/
├── docs/
|   ├── conf.py  # new
|   ├── index.rst  # new
|   ├── make.bat  # new
|   └── Makefile  # new
├── src/
│   └── log_decor/
|       ├── __init__.py
|       └── ...
├── pyproject.toml
└── requirements.txt

Adding Content Files

It is possible to write the documentation using multiple .rst files. In fact, it is a good practice to divide the documentation into different files, each with a specific focus, making it easier to navigate and read the content.

In the case of the example we are working on, we added several files in the docs/modules/ folder that correspond to different sections of the documentation, which are displayed on different pages. Each file contains specific information about the subject covered in that section, allowing the reader to easily find what they are looking for.

The directory structure with the new files is as follows:

log_decor/
├── docs/
|   ├── modules/  # new
|   |   ├── api.rst
|   |   ├── contributing.rst
|   |   ├── examples.rst
|   |   ├── license.rst
|   |   └── usage.rst
|   ├── conf.py
|   ├── index.rst
|   ├── make.bat
|   └── Makefile
├── src/
│   └── log_decor/
|       ├── __init__.py
|       └── ...
├── pyproject.toml
└── requirements.txt

Writing the documentation’s home page

The index.rst file is the main file of the documentation, where we define the basic structure and organization of the topics to be covered. It is responsible for importing the other .rst files that will compose the documentation, organizing them in a way that facilitates user navigation.

The index.rst serves as the home page of the documentation, as it is the first file the user accesses when opening the documentation. It is common to include a brief introduction of the project and a list of the topics that will be covered, so that the user can get a general idea of what they will find when browsing through the documentation.

Additionally, the index.rst can be used to include important information about the project, such as its license, credits to authors and contributors, links to the official project page, among other relevant information. All of this contributes to making the documentation more comprehensive and informative for users.

The index.rst file of the log_decor library is as follows:

Welcome to log-decor's documentation!
=====================================

Introduction
############

This package has class and method decorators that provide logging functionality.

Getting Started
###############

To install, execute the following command:

.. code-block::

   pip install git+https://github.com/bernardopaulsen/log_decor.git


.. toctree::
   :caption: Contents

   modules/usage
   modules/examples
   modules/api
   modules/contributing
   modules/license


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

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

The different elements of the above file are:

  • ‘Welcome to log-decor’s documentation!’: Title of the homepage.

  • ‘Introduction’: Section where a brief introduction about the library is presented, highlighting its main functionality.

  • ‘Getting Started’: Section that provides the necessary information for users to start using the library. It includes an installation command using pip and a link to the GitHub repository.

  • .. toctree::: The command is used to create a table of contents for the generated documentation. It allows the user to list other .rst files that will be displayed in the table of contents.

  • :caption: Contents: Title that will be used for the table of contents.

  • modules/usage, modules/examples, modules/contributing, modules/license: Links to the .rst files that will be included in the table of contents.

  • Indices and tables: Section that presents indexes and tables related to the documentation.

  • :ref:genindex: Link to the general index, which contains information about all documented modules, classes, and functions.

  • :ref:modindex: Link to the module index, which contains information about all documented modules.

  • :ref:search: Link to the search table, which allows users to search for any keyword in the documentation.

You can check the final result of this configuration below:

Welcome to log-decor’s documentation!

Introduction

This package has class and method decorators that provide logging functionality.

Getting Started

To install, execute the following command:

pip install git+https://github.com/bernardopaulsen/log_decor.git

Indices and tables

Note that the contents of the table of contents depend on the contents of each of the included .rst files in the table.

Automatic API Creation

The Sphinx autodoc extension allows documentation to be automatically generated from the library’s source code. In our example, we are using this extension in conjunction with the sphinx_autodoc_typehints library (which adds support for automatically documenting argument types and return values of functions and methods) to automatically document classes and functions from our source code.

To configure the use of autodoc and sphinx-autodoc-typehints, they need to be included in the list of extensions in the conf.py file. Additionally, various options can be configured for the extensions, such as including private members, documenting attributes, and more.

Documentation written in the source code

There are several ways to document the source code, all described in the autodoc documentation. One of these ways is through docstrings, which are triple-quoted strings placed immediately after the definition of a function, method, class, or module. The docstrings can be read by the Sphinx autodoc extension and used to generate the documentation.

The following example is the add_logger.py module from the log_decor library:

# add_logger.py

import functools
import logging
from typing import Optional, Type, TypeVar


Class = TypeVar('Class')


class AddLogger:
    """Class decorator that adds a logger attribute to the decorated class.

    :param name: Name of Logger. If None, the qualified name of the class
        passed to :meth:`add_logger.AddLogger.__call__` will be the name of
        the logger.
    """
    def __init__(self,
                name: Optional[str] = None
                ) -> None:
        self._name = name

    def __call__(self,
                cls: Class
                ) -> Type[Class]:
        """Add logger attribute to class.

        The name of the attribute will be '_logger'.

        :param cls: Class to which add logger. If no name was passed to
            :meth:`add_logger.AddLogger.__init__`, the qualified name of the
            class passed to this parameter will be the name of the logger.
        :return: Class with logger attribute.
        """
        @functools.wraps(cls,
                        updated=())
        class LoggedClass(cls):
            def __init__(self_,
                        *args,
                        **kwargs
                        ) -> None:
                self_.logger = logging.getLogger(
                    name=self._name if self._name is not None else cls.__name__
                )
                super().__init__(*args,
                                **kwargs)
        return LoggedClass

The docstring begins with a brief description of the object’s function it is written for. Then, it provides descriptions of the object’s parameters, and finally a description of the object’s return value. It’s worth noting that the descriptions of the parameters for the __init__ method are written in the class’s docstring, not in the method’s docstring.

Docstrings can contain more information than just the simple description of the object and its parameters and return value. It is good practice to include usage examples of the object, examples of the relationship between arguments and return values, and other relevant information that can help the reader better understand how to use the documented object.

It is worth noting that the parameter types and return value types are not documented in the example’s docstrings, but that is because the types are already documented in the code itself, and they will be read by the sphinx_autodoc_typehints extension and automatically added to the final documentation.

Setting up the source code directory

First, it is necessary to indicate the base directory of the library’s source code by configuring the sys.path variable in the conf.py file. For example:

# -- Path setup
import os
import sys

sys.path.insert(0, os.path.abspath('../src/log_decor'))

Defining the extensions

To add the autodoc and sphinx_autodoc_typehints extensions, they need to be added to the extensions list in the conf.py file. For example:

# -- General configuration
extensions = [
    'sphinx.ext.autodoc',
    'sphinx_autodoc_typehints',
]

Adding automatic documentation of a class to a .rst file

To add the API documentation of a class to a .rst file, we can use the .. autoclass:: directive. For example, to add the documentation of the AddLogger class from the add_logger module, we can use the following code:

.. autoclass:: add_logger.AddLogger
   :members:
   :special-members: __call__

In the RST code above, the autoclass directive indicates that we are automatically documenting a class using the autodoc extension. The add_logger.AddLogger argument indicates the class we are documenting. The :members: option indicates that we want to document all members of the class, and the :special-members: __call__ option indicates that we also want to document the special method __call__, which is not documented by default by autodoc because it is a special method.

Below you can check the same result:

class add_logger.AddLogger(name=None)

Class decorator that adds a logger attribute to the decorated class.

Parameters

name (Optional[str]) – Name of Logger. If None, the qualified name of the class passed to add_logger.AddLogger.__call__() will be the name of the logger.

__call__(cls)

Add logger attribute to class.

The name of the attribute will be ‘_logger’.

Parameters

cls (TypeVar(Class)) – Class to which add logger. If no name was passed to add_logger.AddLogger.__init__(), the qualified name of the class passed to this parameter will be the name of the logger.

Return type

Type[TypeVar(Class)]

Returns

Class with logger attribute.

Defining the documentation theme

Sphinx allows you to choose themes to customize the appearance of the documentation. The default Sphinx theme is classic, but there are several other themes available, such as labaster, sphinx_rtd_theme, and sphinx_bootstrap_theme, among others.

In the case of the log_decor library used as an example, the sphinx_rtd_theme theme was chosen, which is a popular and modern theme that offers a clean and professional look for the documentation.

The code required to configure the sphinx_rtd_theme theme in the conf.py file is as follows:

# -- Options for HTML output
import sphinx_rtd_theme

html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

Installation of Required Libraries in the Development Environment

To create documentation with Sphinx, both Sphinx and the auxiliary libraries need to be installed in the development environment. The auxiliary libraries required for generating the documentation are usually specified in a requirements.txt file, which should be located in the docs/ directory. The libraries imported by the source code should also be installed since we are using the autodoc extension, but they are specified in the requirements.txt file in the project’s root directory.

The directory structure will be as follows:

log_decor/
├── docs/
|   ├── modules/
|   |   └── ...
|   ├── conf.py
|   ├── index.rst
|   ├── make.bat
|   ├── Makefile
|   └── requirements.txt  # new
├── src/
│   └── log_decor/
|       ├── __init__.py
|       └── ...
├── pyproject.toml
└── requirements.txt

The contents of the requirements.txt file will be:

sphinx
sphinx_rtd_theme
sphinx_autodoc_typehints

These packages can be installed with the command:

pip install --requirement docs/requirements.txt

Generating HTML Documentation

Inside the docs/ folder, the command

make html

is used to generate HTML documentation from the source files. The HTML files will be generated in the docs/_build/html directory by default. The documentation’s main page is in the index.html file, which should be opened to access the documentation.

log_decor/
├── docs/
|   ├── _build/  # new
|   |   ├── doctrees/
|   |   └── html/
|   |        ├── ...
|   |        ├── index.html
|   |        └── ...
|   ├── modules/
|   |   └── ...
|   ├── conf.py
|   ├── index.rst
|   ├── make.bat
|   ├── Makefile
|   └── requirements.txt
├── src/
│   └── log_decor/
|       ├── __init__.py
|       └── ...
├── pyproject.toml
└── requirements.txt

As mentioned earlier, the complete documentation can be accessed through the log_decor page on Read the Docs.

Sphinx also allows generating other types of documentation files, such as PDF and ePub. To generate a PDF file, for example, it is necessary to install LaTeX software and then use the command make latexpdf. To generate an ePub file, the Calibre software needs to be installed, and the command make epub should be used.

Publishing on Read the Docs

The integration between Sphinx, GitHub, and Read the Docs is extremely useful for creating and maintaining up-to-date documentation for software projects. By using Sphinx to generate documentation from the source code and GitHub to store the code, it is possible to automate the process of updating the documentation with each new project version. And with integration with Read the Docs, the documentation can be easily hosted and shared with the community.

As mentioned earlier, the documentation for the log_decor project is available on Read the Docs at its page on the platform

Configuration

To integrate Sphinx with Read the Docs, it is necessary to configure a file called readthedocs.yaml in the project’s root directory. This file allows specifying the Python version used in the documentation, as well as other configurations. For example, it is possible to specify the commands to be executed before building the documentation and the extensions to be used.

Below is the directory structure of the sample project after including the readthedocs.yaml file:

log_decor/
├── docs/
|   └── ...
├── src/
│   └── log_decor/
|       ├── __init__.py
|       └── ...
├── pyproject.toml
├── readthedocs.yaml  # new
└── requirements.txt

The readthedocs.yaml file is as follows:

# readthedocs.yaml
version: 2

sphinx:
builder: html
configuration: docs/conf.py
fail_on_warning: true

python:
version: 3.8
install:
- requirements: docs/requirements.txt
    extra_requirements:
    - requirements.txt

The file starts with the definition of the configuration file version, in this case, version 2.

The sphinx section is used to specify the Sphinx configurations. In this case, the defined builder is html, the configuration file used is docs/conf.py, and fail_on_warning is set to true, which causes the documentation build process to fail if there are any warnings.

The python section is used to define the Python version that will be used to build the documentation and the list of packages that will be installed before the build. In this case, it is defined that version 3.8 will be used and that the files docs/requirements.txt and requirements.txt will be used to install the necessary packages, with the former defining the packages used by Sphinx and the latter defining the packages used by the source code of the library.

Integration with Read the Docs

Integrating Read the Docs with GitHub is straightforward and easy to set up. To create documentation on Read the Docs from a GitHub repository, simply create an account on Read the Docs, set up a new project, and link the project to the GitHub repository by selecting the repository and authorizing access. Once configured, Read the Docs will automatically monitor the GitHub repository for updates and generate a new version of the documentation whenever there is a new commit or pull request.

Accessing the Documentation

Once the integration is set up and the documentation has been built, it can be accessed through the link provided by Read the Docs.

Other Sphinx Features

Sphinx offers many features beyond documentation generation. Some of these features can make the documentation even more useful and accessible.

ReStructuredText

The ReStructuredText markup format itself has several features not demonstrated in the above example.

One of them is the creation of links to sections and chapters of the documentation, including links to source code objects. These links can be included in the code’s own docstrings, making the documentation even more interconnected and user-friendly.

Another feature worth mentioning is the support for code blocks in the documentation (including in docstrings), which allows usage examples of the library to be directly exposed in the documentation. This can be particularly useful in helping users understand how to use the library in their own projects.

Extensions

In addition to the standard features, Sphinx also has several very useful extensions, both those included in the library and third-party extensions, which can further facilitate the documentation process and make the documentation richer and more comprehensive. Some of the included extensions in Sphinx are:

  • doctest: inclusion of unit tests within the documentation, making it more interactive and facilitating understanding of the library’s usage.

  • intersphinx: inclusion of links to documentation of other libraries, facilitating access to the documentation of other tools.

  • viewcode: inclusion of links to the source code of documented classes, functions, and methods, making the documentation more comprehensive and allowing for a deeper understanding of how the library works.

Some third-party extensions that can be easily installed and used with Sphinx and are available in the PyPI repository are:

  • sphinxcontrib-jupyter: inclusion of Jupyter notebooks in the documentation, making it more interactive and allowing for the visualization of usage examples of the library in a more dynamic way. It is worth noting that this extension is developed by the QuantEcon organization, which is an organization dedicated to economic modeling, providing libraries and educational materials on the subject, among other projects. I strongly recommend exploring the organization’s projects if you are interested in economic modeling and quantitative finance.

  • sphinxcontrib-bibtex: automatic inclusion of bibliographic citations in the documentation, facilitating reference to articles and scientific works related to the library.

Conclusion

In conclusion, documentation is a fundamental part of any software project, but unfortunately, it is often neglected by developers. Clear and well-written documentation not only helps to facilitate understanding of the project, but also eases the maintenance process and collaboration with other developers.

Sphinx is a powerful tool that makes documentation creation and maintenance much easier and efficient. Furthermore, integration with GitHub and Read the Docs enables simple and accessible sharing of the documentation with anyone interested in the project.

I hope you have enjoyed the article and that it has been useful in improving the documentation of your software projects. Always remember that good documentation is essential for the success of any project!