Document the code in your package’s API using docstrings#

What is an API?#

API stands for Applied Programming Interface. When discussed in the context of a (Python) package, the API refers to the functions, methods and classes that a package maintainer creates for users.

A simple example of a package API element: For instance, a package might have a function called add_numbers() that adds up a bunch of numbers. To add up numbers, you as the user simply call add_numbers(1,2,3) and the package function calculates the value and returns 6. By calling the add_numbers function, you are using the package’s API.

Package APIs consist of functions and/or classes, methods and attributes that create a user interface (known as the API).

What is a docstring and how does it relate to documentation?#

In Python a docstring refers to text in a function, method or class that describes what the function does and its inputs and outputs. Python programmers usually refer to the inputs to functions as “parameters” or “arguments”, and the outputs are often called “return values”

The docstring is thus important for:

  • When you call help() in Python, for example, help(add_numbers), the text of the function’s docstring is printed. The docstring thus helps a user better understand how to applying the function more effectively to their workflow.

  • When you build your package’s documentation, the docstrings can be also used to automagically create full API documentation that provides a clean view of all its functions, methods, attributes, and classes.

Python package API documentation#

If you have a descriptive docstring for every user-facing class, method, attribute and/or function in your package (within reason), then your package’s API is considered well-documented.

In Python, this means that you need to add a docstring for every user-facing class, method, attribute and/or function in your package (within reason) that:

  • Explains what the function, method, attribute or class does

  • Defines the type inputs and outputs (ie. string, int, np.array)

  • Explains the expected output return of the object, method or function.

Three Python docstring formats and why we like NumPy style#

There are several Python docstring formats that you can chose to use when documenting your package including:

We suggest using NumPy-style docstrings for your Python documentation because:

  • NumPy style docstrings are core to the scientific Python ecosystem and defined in the NumPy style guide. Thus you will find them widely used there.

  • The Numpy style docstring is simplified and thus easier-to-read both in the code and when calling help() in Python. In contrast, some feel that reST style docstrings is harder to quickly scan, and can take up more lines of code in modules.

Tip

If you are using NumPy style docstrings, be sure to include the sphinx napoleon extension in your documentation conf.py file. This extension allows Sphinx to properly read and format NumPy format docstrings.

Docstring examples Better and Best#

Below is a good example of a well documented function. Notice that this function’s docstring describes the function’s inputs and the function’s output (or return value). The initial description of the function is short (one line). Following that single line description there is a slightly longer description of what the function does (2 to 3 sentences). The return of the function is also specified.

def extent_to_json(ext_obj):
    """Convert bounds to a shapely geojson like spatial object.

    This format is what shapely uses. The output object can be used
    to crop a raster image.

    Parameters
    ----------
    ext_obj : list or geopandas.GeoDataFrame
        If provided with a `geopandas.GeoDataFrame`, the extent
        will be generated from that. Otherwise, extent values
        should be in the order: minx, miny, maxx, maxy.

    Returns
    -------
    extent_json: A GeoJSON style dictionary of corner coordinates
    for the extent
        A GeoJSON style dictionary of corner coordinates representing
        the spatial extent of the provided spatial object.
    """

Best: a docstring with example use of the function#

This example contains an example of using the function that is also tested in sphinx using doctest.

def extent_to_json(ext_obj):
    """Convert bounds to a shapely geojson like spatial object.

    This format is what shapely uses. The output object can be used
    to crop a raster image.

    Parameters
    ----------
    ext_obj : list or geopandas.GeoDataFrame
        If provided with a `geopandas.GeoDataFrame`, the extent
        will be generated from that. Otherwise, extent values
        should be in the order: minx, miny, maxx, maxy.

    Returns
    -------
    extent_json : A GeoJSON style dictionary of corner coordinates
    for the extent
        A GeoJSON style dictionary of corner coordinates representing
        the spatial extent of the provided spatial object.

    Example
    -------
    Convert a `geopandas.GeoDataFrame` to an extent dictionary:

    >>> import geopandas as gpd
    >>> import earthpy.spatial as es
    >>> from earthpy.io import path_to_example

	We start by loading a Shapefile.

    >>> rmnp = gpd.read_file(path_to_example('rmnp.shp'))

	And then use `extent_to_json` to do the conversion from `shp` to
    `geopandas.GeoDataFrame`.

    >>> es.extent_to_json(rmnp)
    {'type': 'Polygon', 'coordinates': (((-105.4935937, 40.1580827), ...),)}

    """
../../_images/sphinx-rendering-extent-to-json-earthpy.png

Using the above NumPy format docstring in sphinx, the autodoc extension will create the about documentation section for the extent_to_json function. The output of the es.extent_to_json(rmnp) command can even be tested using doctest adding another quality check to your package.#

Using doctest to run docstring examples in your package’s methods and functions#

Above, we provided some examples of good, better, best docstring formats. If you are using Sphinx to create your docs, you can add the doctest extension to your Sphinx build. Doctest provides an additional; check for docstrings with example code in them. Doctest runs the example code in your docstring Examples checking that the expected output is correct. Similar to running tutorials in your documentation, doctest can be a useful step that assures that your package’s code (API) runs as you expect it to.

Note

It’s important to keep in mind that examples in your docstrings help users using your package. Running doctest on those examples provides a check of your package’s API. doctest ensures that the functions and methods in your package run as you expect them to. Neither of these items replace a separate, stand-alone test suite that is designed to test your package’s core functionality across operating systems and Python versions.

Below is an example of a docstring with an example. doctest will run the example below and test that if you provide add_me with the values 1 and 3 it will return 4.

def add_me(aNum, aNum2):
    """A function that prints a number that it is provided.

    Parameters
    ----------
    aNum : int
        An integer value to be printed

    Returns
    -------
        Prints the integer that you provide the function.

    Examples
    --------
    Below you can see how the `print_me` function will print a number that
    you provide it.

    >>> add_me(1+3)
    4

    """

    return aNum + aNum2