.. _documentation:
Documenting your Package
========================
There are two main ways to document your project, both of
which are essential: :ref:`docstrings` and :ref:`narrative`.
.. _docstrings:
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:
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 :ref:`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 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 :ref:`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.
.. _previewing_docs:
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 :ref:`ReadTheDocs `
or :ref:`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``):
.. code-block:: toml
[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
.. _readthedocs:
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".
.. image:: _static/github_pages_activate.png
:alt: 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//``.
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.
.. _plot_directive:
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:
.. code-block:: python
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:
.. code-block:: ini
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:
.. code-block:: rst
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 `_.