Documenting your Package

There are two main ways to document your project, both of which are essential: Docstrings and Narrative Documentation.

Docstrings

First, public functions, methods, and classes in your package should include docstrings, which are strings attached to those objects which the user can access interactively using e.g. help() and which can also be retrieved by automated tools. See PEP 257 - Docstring Conventions for a high-level overview of what docstrings are. We recommend adopting the numpydoc format for docstrings. An example of such a docstring is:

def foo(var, long_var_name='hi'):
    """A one-line summary that does not use variable names.

    Several sentences providing an extended description. Refer to
    variables using back-ticks, e.g. `var`.

    Parameters
    ----------
    var : int
        The type above can either refer to an actual Python type
        (e.g. ``int``), or describe the type of the variable in more
        detail, e.g. ``(N,) ndarray`` or ``array_like``.
    long_var_name : {'hi', 'ho'}, optional
        Choices in brackets, default first when optional.

    Returns
    -------
    out : type
        Explanation of `out`.
    """

These docstrings should be included in the Python files alongside the Python objects they document.

Narrative Documentation

Second, you should write a set of narrative documentation which functions as a user guide, such as http://docs.astropy.org or http://docs.sunpy.org. This package template will create the basis for this. You will find that in the docs directory under the top level of your repo. You will want to edit the following files to make them correct for your package:

  • installation.rst – with luck, the stuff that is there already is sufficient, but you should review it.

  • usage.rst – This is where you will write your main documentation.

  • index.rst – This one may be OK as is, but you will need to edit it if your documentation is long (see below).

If your documentation is short enough, you can write it all on the usage.rst page. If it’s long, you might want to divide it into sections. Give each section its own .rst file. Then, list the files you’ve added underneath the ..tocktree:: directive in the file index.rst.

All of these files are in Sphinx reStructuredText format. If you haven’t used Sphinx before, you maybe interested in their Getting Started guide.

Including docstrings in the narrative documentation

As part of the narrative documentation, it is also common practice to include an Application programming interface (API) page which lists the available classes, methods, and functions in your package. Thankfully, if you’ve defined your docstrings as described in Docstrings, then this can be automated using the sphinx-automodapi package. See the documentation of that package for more details, but a repo created from this package template should already be set up to automatically include this.

First, you need to make sure to declare which classes and variables you want to include in the documentation. In each source file, make sure to include a variable __all__ which is a list of classes and functions that you want documented. (Really, this is a list of things that get imported from your package if you do from my_module import *; see the Python modules tutorial. Of course, you should never do from <anything> import *, but Sphinx does it to find which things in your module need to be documented.) For example, snappl might have the following at the top of snappl/image.py:

__all__ = [ 'Image', 'Numpy2dImage', 'FITSImage', 'OpenUniverse2024FITSImage',
            'ManualFITSImage', 'RomanDatamodelImage' ]

to indicate the classes that should be documented.

If you don’t include an __all__ variable, then way too much gets included in the documentation; everything you import with something like from foo import bar will be included as part of your package’s documentation, which of course isn’t right. You may also want to omit some internal functions that aren’t intended for use outside of the module.

In order to actually include the API documentation, you need to put some directives somewhere in your documentation. You may wish to put this in docs/index.rst, or (better) you may wish to make a separate docs/api.rst documentation file that you then link to in index.rst by just adding the line api.rst after changes.rst underneath the .. toctree:: directive.

Wherever you put your API documentation, for each module whose documentation you want to include, add a line:

..automodapi:: modulename.submodulename

So, if your module (i.e. the top level thing you import) is snpit_utils, and you want to include documentation for snpit_utils.config, you’d just put the line:

..automodapi:: snpit_utils.config

where you want it to show up.

WARNING : If you build the documentation locally (see Previewing your documentation), it will create a subdirectory underneath docs with a .rst file for each class or function whose api documentation was generated. Do not add these to your github archive! You may wish to edit the .gitignore file at the root of your checkout to add these working directories.

Declaring dependencies for documentation

To make it easier for contributors to get set up with the dependencies required to build the documentation, as well as to make it easier to configure automated builds (whether for ReadTheDocs or tox), you should define an [project.optional-dependencies] section in your pyproject.toml file named docs which lists the dependencies required to build the documentation (not including dependencies already mentioned in dependencies):

[project.optional-dependencies]
docs = [
    "sphinx",
    "sphinx-automodapi",
    "numpydoc",
]

This will then allow contributors to type:

pip install -e .[docs]

to install the package in developer/editable mode along with the documentation dependencies.

Previewing Your Documentation

You should make sure you can get this to work. If you can’t, then it’s likely not to work on github actions either. (Getting it to work locally is a probably-necessary, but not sufficient, condition for it working on github actions.)

Getting set up to build your documentation

If you want to build your documentation locally, you need to make sure you’re in an environment with the right packages installed. This includes the package whose documentation you want to build. You may just do this in the environment you’re working in. However, if you want to have a contained environment to do this in (e.g. if you don’t want to install the current state of the package you’re working on into your environment), then you can make a new environment at the top level of your checkout with:

python -mvenv venv
source venv/bin/activate

The “activate” command puts you in that environment; your prompt should change so that it has (venv) at the beginning to let you know you’re in this environment. When you’re ready to leave it, just run deactivate.

To install the things you need, run, at the top level of your checkout:

pip install -e .[build,docs]

Next, install the current working version of your package into your current environment:

pip install -e .

Building and viewing the documentation

Now that you’re set up to build the documentation, just go into the docs subdirectory of your checkout and run:

make html

That will create a subdirectory _build/html underneath the docs directory. Open the file index.html in that subdirectory in your web browser, and you should see a preview of your documentation.

(NOTE: In my experience, sometimes when I run this, I get an error, and then when I run it a second time, the error goes away. Also try:

make clean
make html

and see if that works.)

If it doesn’t work because of a missing import

It may be that the requirements listed in pyproject.toml are not sufficient to actually run your package, or even import modules from your package. This is the case for phrosty, for example, which requires a version of roman_imsim pulled from Troxel’s git repository, not the one that’s on PyPI. That repository is in the SNPIT docker image (which is, at least currently, the only way it’s possible to run, at least, phrosty).

If this is the case for your package, then you have more work to do in order to get your docs to build. Edit the file docs/conf.py, and search for “Getting docs to build outside the SNPIT docker image”. Try uncommenting the few lines underneath that. That adds your repo to the python path, so it will be able to find your module there even if it’s not installed. It also adds roman_imsim to a list of modules that sphinx knows aren’t importable, and so won’t try to import. You may need to add other things to the things_to_mock list.

Example

As an example, ROB PUT IN SNPIT_UTILS EXAMPLE

Automatically deploying your documentation to github pages

The package template includes a github workflow that will automatically use sphinx to build documentation from things in your docs subdirectory. The results are put on a branch, gh-pages, in your repo. (Do not merge this branch with your main branch, or any other working branch, because all of your code has been deleted from it!)

To enable automatic deployment, go to the settings for your github archive. In the left sidebar, in the “Code and automation” section, find “Pages”. Under the section “Branch” on that page, select the branch “gh-pages”. (If that branch is not there, then your documentation failed to automatically build. See “Debugging…” below. Finally, click “Save”.

github pages activation

Thereafter, next time you merge to main (or otherwise cause the documentation deployment workflow to run), the documentation should be updated at https://roman-supernova-pit.github.io/<your_repo_name>/.

Debugging automatic documentation on github pages

At your repo home page, click on “Actions” at the top. Find in the let sidebar, under “Actions”, “Deploy Package Documentation to…”. Click on that link. That will show you all the times this workflow has been run. You can click on a run to see any errors, and you can manually launch this workflow on any branch. WARNING: if you launch this workflow on a branch other than main, that branch’s documentation is what will be deployed to github pages! While in development and test mode, this is not a big deal, but as we get close to production we’re going to want to be very careful with this.

Add plots to your documentation

A plot is worth many words, and sometimes documentation can demonstrate the uses and advantages of using a given package much more efficiently than narrative docs. Matplotlib, for example, has made this quite straightforward with the plot directive.

To add a plot to your Sphinx documentation, add the following string to the extensions list in your docs/conf.py file:

extensions = [
    ...  # preserve your other extensions here, then add:
    "matplotlib.sphinxext.plot_directive"
]

To make use of this extension, you will also need to add matplotlib to your tox.ini file:

deps =
   # preserve your other deps here, then add:
   matplotlib

Now you can add plots to your Sphinx docs by adding a block like the following to your narrative docs:

Here's a plot:

.. plot::

    import matplotlib.pyplot as plt

    x, y = [1, 2, 3], [4, 5, 6]

    plt.figure()
    plt.plot(x, y)

By default, sphinx and matplotlib will render the figure defined by the Python code in the .. plot:: block, without the source code. Full documentation for the configuration settings for the plot directive can be found in the matplotlib docs.