Documentation Guide

Documentation that is up-to-date and understandable is vital to the health of a software project. This page describes the documentation requirements and guidelines to be followed during the development of PlasmaPy and affiliated packages.

Tip

Updating documentation is one of the best ways to make a first contribution to an open source software project.

Note

If you discover areas within PlasmaPy’s documentation that are confusing or incomplete, please raise an issue! This really helps PlasmaPy not only by helping us improve the documentation for all, but also by creating opportunities for new contributors to make their first contribution to the project.

PlasmaPy’s documentation is hosted by Read the Docs and is available at these locations:

Tip

A preview of the documentation is generated every time a pull request is created or updated. You can access this preview by scrolling down to the checks at the bottom of a pull request, and clicking on Details next to docs/readthedocs.org:plasmapy.

Building documentation

There are two methods for building the documentation: make and tox.

  • Using make will build the documentation based off of what is in the current directory structure. make is quicker for local builds than tox but requires you to install and setup all dependencies.

  • Using tox does not require setting up all dependencies ahead of time, but is more computationally intensive since it creates a virtual environment and builds the package before building the documentation. Consequently, PlasmaPy uses tox for building the documentation on continuous integration testing platforms.

Prerequisites

To install all dependencies required to develop PlasmaPy on your local computer, enter the top-level directory of the cloned repository and run:

pip install -r requirements.txt

Building documentation with make

If you have make installed, then you can build the documentation by entering the docs/ directory and running:

make html

Including the -j n flag in the make command will enable a parallel build, where n is replaced with the number of processes.

You can access the documentation landing page by opening docs/_build/html/index.html with your browser of choice.

To remove all files previously generated by make, run:

make clean

This command is needed when you make a change to a file that does not trigger Sphinx to rebuild the file that you altered, for example modifying a CSS file.

Building documentation with tox

You can use tox to locally build the documentation by running:

tox -e build_docs

You can access the documentation landing page by opening docs/_build/html/index.html with your browser of choice.

When writing documentation, please make sure to fix any warnings that arise. To enforce this, the build_docs environment is set to fail on encountering any warnings via the -W flag to sphinx-build.

You can shorten the tox documentation build by running:

tox -e build_docs_no_examples

in order to build the documentation without executing the example notebooks. This command will pass even if there are warnings. A tox command may also include the --parallel flag to allow a parallel build.

Documentation tools

ReStructuredText

PlasmaPy’s documentation is written using the reStructuredText (reST) markup language. reST is human readable when viewed within a source code file or when printed out using help. reST also contains markup that allows the text to be transformed into PlasmaPy’s documentation. reST files use the file extension .rst. Documentation contained within .py files are in the form of docstrings, which are written in reST.

ReStructuredText Examples

Here we show some examples of commonly used reST syntax in PlasmaPy. Please refer to the documentation for Sphinx and reST for a list of available directives and syntax.

This is an example of including headings for the document title, sections, subsections, and so on. The lines surrounding each heading are the same length as that heading.

==============
Document title
==============

Heading 1
=========

Heading 2
---------

Heading 3
~~~~~~~~~

We can link to code objects by enclosing them in single back ticks. This linking will work for Python commands as well as certain packages like NumPy, SciPy, Astropy, and pandas. This linking is described in the section on Cross-referencing external packages. In-line code examples are enclosed in double back ticks.

Here `plasmapy.particles` provides a linked reference to the
module's documentation.

Adding a tilde at the beginning `~plasmapy.particles` still
provides a linked reference to the associated documentation
but shortens the display so only "particles" is displayed.

Double backticks are used to show inline code that is not
cross-referenced: ``import astropy.units as u``.

This reST block renders as:

Here plasmapy.particles provides a linked reference to the module’s documentation.

Adding a tilde at the beginning particles still provides a linked reference to the associated documentation but shortens the display so only “particles” is displayed.

Double backticks are used to show inline code that is not cross-referenced: import astropy.units as u.

Sphinx can format code blocks for Python and the Python console.

.. code-block:: python

   def sample_function():
       return 42

.. code-block:: pycon

   >>> print(6 * 9)
   54

This reST block renders as:

def sample_function():
    return 42
>>> print(6 * 9)
54

Here are some examples for linking to websites.

`PlasmaPy Enhancement Proposals <https://github.com/PlasmaPy/PlasmaPy-PLEPs>`_
are used to propose major changes to PlasmaPy.

`Write the Docs`_ has a guide_ on writing software documentation.

.. _`Write the Docs`: https://www.writethedocs.org/
.. _guide: https://www.writethedocs.org/

This reST block renders as:

PlasmaPy Enhancement Proposals are used to propose major changes to PlasmaPy.

Write the Docs has a guide on writing software documentation.

Math can typically be written using LaTeX commands.

.. math::

   \alpha = \beta + \gamma

This reST block renders as:

\[\alpha = \beta + \gamma\]

Math can be in-line.

An example of in-line math is :math:`x`. Using Unicode characters
like :math:`α + β + γ` makes math easier to read in the source code.

This reST block renders as:

An example of in-line math is \(x\). Using Unicode characters like \(α + β + γ\) makes math easier to read in the source code.

Markdown

A few of PlasmaPy’s files are written using Markdown, such as README files and licenses from other packages. Markdown is simpler but more limited than reST. Markdown files use the file extension .md. Posts on GitHub are written in GitHub Flavored Markdown. The following code block contains a few common examples of Markdown formatting.

# Header 1

## Header 2

Here is a link to [PlasmaPy's documentation](https://docs.plasmapy.org).

We can make text **bold** or *italic*.

We can write in-line code like `x = 1` or create a Python code block:

```Python
y = 2
z = 3
```

Sphinx

Sphinx is the software used to generate PlasmaPy’s documentation from reST files and Python docstrings. It was originally created to write Python’s documentation and has become the de facto software for documenting Python packages. Almost all Python open-source packages utilize Sphinx to generate their documentation.

Configuration

The docs/conf.py file contains the configuration information needed to customize Sphinx behavior. The documentation for Sphinx lists the configuration options that can be set.

The docs/_static/sphinx_rtd_overrides.css file contains style overrides for the Read the Docs Sphinx Theme to customize the look and feel of the online documentation.

Sphinx extensions

PlasmaPy’s documentation is built with the following Sphinx extensions:

These extensions are specified in extensions configuration value in docs/conf.py.

Cross-referencing external packages

Intersphinx allows the automatic generation of links to the documentation of objects in other projects. This cross-package linking is made possible with the sphinx.ext.intersphinx extension and proper package indexing by the external package using sphinx.ext.autodoc.

When we include `astropy.units.Quantity` in the documentation, it will show up as astropy.units.Quantity with a link to the appropriate page in Astropy documentation. Similarly, `~astropy.units.Quantity` will show up as Quantity.

To make cross-referencing to an external package available its mappings have to be defined in the intersphinx_mapping configuration dictionary contained in docs/conf.py. PlasmaPy has already include several packages like Python, NumPy, SciPy, Astropy, Sphinx, etc.

New source packages may be added, but please verify that references to a function or class in that package show up correctly in PlasmaPy’s documentation. The name of the package does not always link as expected.

Hint

If a cross-link is not working as expected this is usually due to one of the following reasons:

  • A typo;

  • The package not being defined in intersphinx_mapping, or

  • The referenced source package not properly or fully indexing their own code, which is common in Python packages.

Substitutions

Some functions and classes are referred to repeatedly throughout the documentation. reST allows us to define substitutions

.. |Particle| replace:: `~plasmapy.particles.particle_class.Particle`

Here whenever |Particle| is used Sphinx will replace it with `~plasmapy.particles.particle_class.Particle` during build time.

PlasmaPy has certain common substitutions pre-defined so that they can be used elsewhere in the documentation. For example, we can write |Quantity| instead of `~astropy.units.Quantity`, and |Particle| instead of `~plasmapy.particles.particle_class.Particle`. For an up-to-date list of substitutions, please refer to the docs/common_links.rst file.

Since substitutions are performed by Sphinx when the documentation is built, any substitution used in docstrings will not show up when using Python’s help function (or the like). For example, when |Particle| is used in a docstring, help will show it as |Particle| rather than `~plasmapy.particles.particle_class.Particle`. Consequently, substitutions should not be used in docstrings when it is important that users have quick access to the full path of the object (such as in the See Also section).

Templating

Sphinx uses the Jinja templating engine to generate HTML code. Jinja may be used within the documentation when templating is necessary. For more details, please refer to Sphinx’s templating page.

Writing documentation

Docstrings

A docstring is a comment at the beginning of a function or another object that provides information on how to use that function (see PEP 257). Docstrings are designated by surrounding the content with triple quotes """This is my docstring.""".

In order to improve readability and maintain consistency, PlasmaPy uses the numpydoc standard for docstrings. Docstring conventions for Python are more generally described in PEP 257.

Tip

If a docstring contains math that utilizes LaTeX syntax, begin the docstring with r""" instead of """.

In a normal string, backslashes are used to begin escape sequences, and a single backslash needs to be represented with \\. This complication is avoided by beginning the docstring with r""", which denotes the docstring as a raw string. For example, the raw string r""":math:`\alpha`""" will render the same as the normal string """:math:`\\alpha`""".

Example docstring

Here is an example docstring in the numpydoc format:

Example docstring
import numpy as np
import warnings

def subtract(a, b, *, switch_order=False):
    r"""
    Compute the difference between two integers.

    Add ∼1–3 sentences here for an extended summary of what the
    function does. This extended summary is a good place to briefly
    define the quantity that is being returned.

    .. math::

       f(a, b) = a - b

   Parameters
   ----------
   a : `float`
       The left multiplicand.

   b : `float`
       The right multiplicand.

   switch_order : `bool`, optional, keyword-only
       If `True`, return :math:`a - b`. If `False`, then return
       :math:`b - a`. Defaults to `True`.

   Returns
   -------
   difference : float
       The difference between ``a`` and ``b``.

   Raises
   ------
   `ValueError`
       If ``a`` or ``b`` is `~numpy.inf`.

   Warns
   -----
   `UserWarning`
       If ``a`` or ``b`` is `~numpy.nan`.

   See Also
   --------
   add : Add two numbers.

   Notes
   -----
   The "Notes" section provides extra information that cannot fit in
   the extended summary near the beginning of the docstring. This
   section should include a discussion of the physics behind a
   particular concept that should be understandable to someone who is
   taking their first plasma physics class. This section can include
   a derivation of the quantity being calculated or a description of
   a particular algorithm.

   The next section contains example references to a journal article
   [1]_ and a book [2]_.

   References
   ----------
   .. [1] J. E. Foster, `Plasma-based water purification: Challenges and
      prospects for the future <https://doi.org/10.1063/1.4977921>`_,
      Physics of Plasmas, 22, 05501 (2017).

   .. [2] E. Gamma, R. Helm, R. Johnson, J. Vlissides, `Design Patterns:
      Elements of Reusable Object-Oriented Software
      <https://www.oreilly.com/library/view/design-patterns-elements/0201633612/>`_

   Examples
   --------
   Include a few example usages of the function here. Start with
   simple examples and then increase complexity when necessary.

   >>> from package.subpackage.module import subtract
   >>> subtract(9, 6)
   3

   Here is an example of a multi-line function call.

   >>> subtract(
   ...     9, 6, switch_order=True,
   ... )
   -3

   PlasmaPy's test suite will check that these commands provide the
   output that follows each function call.
   """
   if np.isinf(a) or np.isinf(b):
       raise ValueError("Cannot perform substraction operations involving infinity.")

   warnings.warn("The subtract function encountered a nan value.", UserWarning)

   return b - a if switch_order else a - b

Template docstring

This template docstring may be copied into new functions. Usually only some of the sections will be necessary for a particular function, and unnecessary sections should be deleted. Any sections that are included should be in the order provided.

Docstring template
def sample_function():
    r"""
    Compute ...

    Parameters
    ----------

    Returns
    -------

    Raises
    ------

    Warns
    -----

    See Also
    --------

    Notes
    -----

    References
    ----------

    Examples
    --------

    """

Documentation guidelines

This section contains guidelines and best practices for writing documentation for PlasmaPy and affiliated packages.

  • Write documentation to be understandable to students taking their first course or beginning their first research project in plasma science. Include highly technical information only when necessary.

  • Use technical jargon sparingly. Define technical jargon when necessary.

  • Use the active voice in the present tense.

  • Keep the documentation style consistent within a file or module, and preferably across all of PlasmaPy’s documentation.

  • Update code and corresponding documentation at the same time.

  • Write sentences that are simple, concise, and direct rather than complicated, vague, or ambiguous. Prefer sentences with ≲ 20 words.

  • Avoid idioms, metaphors, and references that are specific to a particular culture.

  • Many words and software packages have more than one common spelling or acronym. Use the spelling that is used in the file you are modifying, which is preferably the spelling used throughout PlasmaPy’s documentation.

    • More generally, it is preferable to use the spelling that is used in Python’s documentation or the spelling that is used most commonly.

    • Represent names and acronyms for a software package or language as they are represented in the documentation for each project. Common examples include “Python”, “Astropy”, “NumPy”, and “reST”.

  • When referencing PlasmaPy functionality, write the full namespace path to where the functionality is defined, not where it is conveniently accessed. For example, write `~plasmapy.formulary.parameters.Alfven_speed` rather than `~plasmapy.formulary.Alfven_speed`.

    This does not necessarily need to be done when referencing external packages, since each package may have their own standard. For example, Astropy’s Quantity class is defined in `astropy.units.quantity.Quantity` but is also indexed at `~astropy.units.Quantity` so either option will link to the same documentation.

  • For readability, limit documentation line lengths to ≲ 72 characters (excluding leading spaces in docstrings). Longer line lengths may be used when necessary (e.g., for hyperlinks).

    Note

    Studies typically show that line lengths of 50–75 characters are optimal for readability.

  • Use indentations of 3 spaces for reST blocks.

  • Store images within the docs/_static directory, except for images that are generated during the Sphinx build. The docs/_static directory contains files that are used for the online documentation but are not generated during the Sphinx build.

  • Avoid linking to websites that might disappear due to link rot such as documents hosted on personal websites.

  • Include both the original references for a topic as well as accessible pedagogical references. Prefer references that are open access over references that require purchase of a subscription or are behind a paywall.

Note

Emphasize important points with admonitions like this one.

  • Start the names of all physical units with a lower case letter, except at the beginning of a sentence and for “degree Celsius”.

  • Physical unit symbols should not be formatted as math. If units are needed inside a math block, use LaTeX’s \mbox command as in the example below. A tilde will need to precede the unit so that

    The speed of light is approximately :math:`3 × 10^8` m/s or
    
    .. math::
    
       3 × 10^{10} \mbox{~cm/s}
    

    This reST block renders as:

    The speed of light is approximately \(3 × 10^8\) m/s or

    \[3 × 10^{10} \mbox{cm/s}\]
  • The names of chemical elements are lower case, except at the beginning of a sentence.

  • Particle and chemical symbols should be formatted as regular text. The :sub: and :sup: roles should be used for subscripts and superscripts, respectively.

    Because interpreted text must normally be surrounded by whitespace or punctuation, use a backslash followed by a space for the interpreted text to show up immediately next to the regular text. This is not necessary before a period or comma.

    The symbol for helium is He.
    
    The symbol for an electron is e\ :sup:`-`.
    
    An alpha particle may be represented as :sup:`4`\ He\ :sup:`1+`.
    

    This reST block renders as:

    The symbol for helium is He.

    The symbol for an electron is e-.

    An alpha particle may be represented as 4He1+.

Docstring guidelines

  • All functions, classes, and objects that are part of the public Application Programming Interface (API) must have a docstring that follows the numpydoc standard. Refer to the numpydoc standard for how to write docstrings for classes, class attributes, and constants.

  • The short summary statement at the beginning of a docstring should be one line long, but may be longer if necessary.

  • The extended summary that immediately follows the short summary should be ≲ 4 sentences long. Any additional information should included in the “Notes” section.

  • The short summary should start on the line immediately following the triple quotes. There should not be any blank lines immediately before the closing triple quotes.

  • The first line of the docstring for a function or method should begin with a word like “Calculate” or “Compute” and end with a period.

  • The first line of an object that is not callable (for example, an attribute of a class decorated with property) should not begin with a verb and should end with a period.

  • Keep the docstring indented at the same level as the r""" or """ that begins the docstring, except for reST constructs like lists, math, and code blocks. Use an indentation of four spaces more than the declaration of the object.

    def f():
        """This is indented four spaces relative to the `def` statement."""
    
  • The first sentence of a docstring of a function should include a concise definition of the quantity being calculated, as in the following example.

    def beta(T, n, B):
        """Compute the ratio of thermal pressure to magnetic pressure."""
    

    When the definition of the quantity being calculated is unable to fit on ∼1–2 lines, include the definition in the extended summary instead.

    def beta(T, n, B):
        """
        Compute plasma beta.
    
        Plasma beta is the ratio of thermal pressure to magnetic pressure.
        """
    
  • Put any necessary highly technical information in the “Notes” section of a docstring.

  • Private code objects (e.g., code objects that begin with a single underscore, like _private_object) should have docstrings. A docstring for a private code object may be a single line, and otherwise should be in numpydoc format.

    • Docstrings for private code objects do not get rendered in the online documentation, and should be intended for contributors.

  • Dunder methods (e.g., code objects like __add__ that begin and end with two underscores) only need docstrings if it is necessary to describe non-standard or potentially unexpected behavior. Custom behavior associated with dunder methods should be described in the class-level documentation.

    • Docstrings for most dunder methods are not rendered in the online documentation and should therefore be intended for contributors.

    • Docstrings for __init__, __new__, and __call__ are rendered in the documentation, and should be written for users. The docstrings for __init__ and __new__ are included in the class-level docstring, while the docstring for __call__ is included in the methods summary of a class.

  • When an attribute in a class has both a getter (which is the method decorated with property) and a setter decoration, then the getter and setter functionality should be documented in the docstring of the attribute decorated with @property.

    class Person:
        @property
        def age(self):
            """Document both getter and setter here."""
            return self._age
    
        @age.setter
        def age(self, n):
            self._age = n
    

Narrative documentation guidelines

  • Each top-level subpackage must have corresponding narrative documentation.

  • Use narrative documentation to describe how different functionality works together.

  • Narrative documentation should be used when the full scope of some functionality cannot be adequately described within only the docstrings of that functionality.

  • Use title case for page titles (e.g., “This is Title Case”) and sentence case for all other headings (e.g., “This is sentence case”).

  • When the narrative documentation does not index a subpackage (a directory) or module (a .py file) with automodule, automodapi, or the like, then it is required to create a stub file for that particular subpackage or module in docs/api_static . For example, the stub file for plasmapy.particles.atomic is placed at docs/api_static/plasampy.particles.atomic.rst and its contents look like:

    :orphan:
    
    `plasmapy.particles.atomic`
    ===========================
    
    .. currentmodule:: plasmapy.particles.atomic
    
    .. automodapi::  plasmapy.particles.atomic