Skip to content

Code Formatting and Documentation

Giulio Rossetti edited this page Nov 21, 2022 · 8 revisions

Make it Black!

Writing code is a form of art: ensure that others can read your code as easily as possible, starting from adopting standard, readable, formatting.

Fortunately, Black is designed to simplify your life and automate code reformatting.

Installation

Black can be installed by running pip install black. It requires Python 3.7+ to run. If you want to format Jupyter Notebooks, install with `pip install 'black[jupyter]".

Usage

Write your code, test it and reformat it before pushing it to your GitHub repository. To get started right away with sensible defaults, open a command line and write:

black {source_file_or_directory}

Moreover, you can run Black as a package if running it as a script doesn't work:

python -m black {source_file_or_directory}

That's it. A 5-seconds tasks to improve the readability of your code (and if you're not a command line fan, Black is also fully integrated into most Python IDE).

Sphinx documentation from docstrings

Writing code is not enough (unless you're the only expected user of your code and you plan not to update it at all). Documentation is a real deal: the only problem is that it usually takes time to set up and organize.

Solution: enrich your code with structured comments describing its functionalities; magic will do the rest.

In this wiki, we discuss the basis of sphinx, one of the several documentation engines for python projects.

To get ready, install the needed packages:

pip install sphinx sphinx_rtd_theme mock

Configure Sphinx

After satisfying all the needed dependencies, it's time to set up and configure sphinx for our project. You have two options:

  • (lazy one): copy the docs/ folder of this repository in your project root and adjust the needed configuration;
  • (standard one) generate the docs/ folder from the command line.

The second option is easy to accomplish and the one that you should follow (but who am I to blame you for being lazy?):

cd /path/to/project
mkdir docs
cd docs
sphinx-quickstart

That's it.

When you have the docs/ folder ready, it is time to adjust all the needed configurations. To do so, open the docs/conf.pyand start looking at its contents.

Hint: the is well-commented and easy to modify. However, for your first run, use the one provided in this project as an example. For more advanced stuff, refer to the official documentation.

As you have undoubtedly noticed, in the current template project, the docs/ folder comes with a few *.rst files (e.g., index.rst). Those files represent the pages of your documentation in reStructuredText syntax (you'll read something about it in a while).

For now, open index.rst and focus on the following lines:

.. toctree::
   :maxdepth: 1
   :hidden:

   installing.rst
   API.rst

This is how *.rst files specify the creation of a sidebar menu. Other directives in the same file are easy to grasp; therefore, I'll leave them to you to check.

Conversely, if you open docs/API.rst, you'll see an example of "how" magic unfolds.

.. current module:: package_name
.. autoclass:: Profile
    :members:
    :inherited-members:

This snippet tells sphinx to import package_name and to generate the Profile class documentation (starting from its methods docstrings).

Similarly,

.. automodule:: package_name.algorithms

.. autosummary::
    :toctree: algs/

    sort_profiles_by_age
    sort_profiles_by_name

tells sphinx to import package_name.algorithms and generate a summary of two functions it defines (Note: all functions implemented in a sub-module but not listed in the autosummary directive are not reported in the docs).

As a result:

  • a table with a synthetic description of the listed functions is generated and
  • for each listed function, a dedicated HTML page with additional details and use cases (if present in the docstring) is generated (and stored under docs/algs/ and linked to the table entry.

Of course, there is much more to learn on sphinx; However, understanding how those two snippets work should be enough to organize minimal documentation for your first project.

Now, let's talk briefly about writing your documentation pages.

Docstrings: reStructuredText

reStructuredText is an easy-to-read, what-you-see-is-what-you-get plaintext markup syntax and parser system. It is useful for in-line program documentation (such as Python docstrings), for quickly creating simple web pages, and for standalone documents.

The primary goal of reStructuredText is to define and implement a markup syntax for use in Python docstrings and other documentation domains that is readable and simple yet powerful enough for non-trivial use.

To integrate it into your code, use the simple syntax and fields shown in the official documentation when describing the behavior of your functions, as in the following example:

def to_json(profiles: Profiles) -> str:
    """
    Convert the Profiles object to JSON.

    :param profiles: The Profiles object to convert.
    :return: The JSON string.

    :Example:
    >>> from package_name import Profiles, Profile
    >>> from package_name.readwrite import *
    >>> pls = Profiles()
    >>> pls.add_profile(Profile("John", 20, "M"))
    >>> pls.add_profile(Profile("Jane", 25, "F"))
    >>> pls.add_profile(Profile("Jack", 22, "M"))
    >>> json_str = to_json(pls)
    """
    return json.dumps(profiles.__dict__())

The documentation specifies the function rationale, required inputs, expected outputs, and a simple example of Usage. All those information will be automatically integrated within the documentation pages (still written in reStructuredText format) by the sphinx engine. For a working example, refer to the docs/*.rst files.

Generate documentation

To generate the documentation locally (after having installed sphinx, sphinx_rtd_theme and mock using pip) move to the docs folder and run:

make HTML

To clean the generated documentation, run:

make clean

A new folder docs/build will be generated containing the HTML files of the package documentation.

Distribute documentation (on ReadTheDocs)

The main steps for deploying your project documentation on ReadTheDocs are the following:

  • Configure the .readthedocs.yaml configuration file (specifying which requirements files the service needs to use when installing the package and where to find the sphinx configuration file;
  • Check the docs/conf.py sphinx configuration file (with particular care to the dependencies to mock);
  • Create a ReadTheDocs account and import the GitHub repository of your project (it needs to be a public one);
  • Check that the configuration on the readthedocs website for your project makes sense (i.e., when to build the documentation, which requirement file to use...);
  • If everything is correctly set, the documentation will be automatically updated whenever a new push on the main branch takes place.

The most common errors you'll be facing will be due to a wrong setup of your docs/conf.py file. Among them, a typical family of errors is tied to the failed generation of the documentation due to dependencies that cannot be satisfied when installing your package from the repository.

ReadTheDocs needs to be able to locally install your package and its dependency to run sphinx and generate documentation starting from the docstring. However, some of the dependencies your package relies on (mainly ones with C/C++ bindings) might be installed by the service due to configuration limitations. To overcome this issue, you can mock such packages by listing them in the MOCK_MODULES variable exposed in docs/conf.py. For more details on mock, here's the official documentation.