reStructuredText Style Guide
SweetPea’s documentation is written in a consistent style of reStructredText (reST). This helps with managing contributions and makes it easier to read in code. This section details the specifics of SweetPea’s reST style.
General
We restrict reST documentation to not go past the 80th character of a line, except where absolutely necessary due to potential formatting errors. (For example, a long hyperlink may extend past the 80th column, but additional text within the same sentence as that hyperlink should be put on separate lines to accommodate this rule to the greatest extent possible.)
Roles
Roles (reST
items surrounded by colons, like :role:
) are each preceded by one empty
line.
If the role has an argument, such as :class:`MyClass`
, then it immediately
follows the role’s declaration. If the role offsets some additional text, such
as the :returns:
role used in documenting function return values, then the
body text is set on the immediately following line, indented by 4 spaces.
Directives
Like roles, directives
(reST items preceded by double-dots and followed by double-colons, like ..
directive::
) are each preceded by one empty line.
Directives are then followed by one empty line, and the content text begins on the next line after that. Content text is indented by 4 spaces.
Commonly Used Roles and Directives
You may use any roles or directives you find useful, but there are a number of them that we already make frequent use of:
Roles
:math:`<LaTeX math code>`
Renders the given
<LaTeX math code>
. See the Sphinx documentation on how to do math.
Linking Roles
There are a number of roles that form hyperlinks. These can link within the same documentation, to other documentation (via InterSphinx), or to external websites. All of these roles can be used in two ways:
:<role>:`<link>`
Creates a hyperlink to resource
<link>
.:<role>:`link text <link>`
Creates a hyperlink to resource
<link>
, but displays aslink text
.
We omit the second variety in the following documentation, but it is equally valid in all cases.
:ref:`<reference_link>`
Creates a cross-referencing link to the reST section titled
<reference_link>
within the same documentation.:mod:`<module_name>`
Links to the documentation on the Python module
<module_name>
.:class:`<class_name>`
Links to the documentation on the Python class
<class_name>
.:func:`<func_name>`
Links to the documentation on the Python function
<func_name>
.Note
The
:func:
role is also used for documenting methods within classes!:attr:`<attr_name>`
Links to the documentation on the Python attribute
<attr_name>
. Attributes are parts of classes that include fields and methods. Prefer:func:
for linking to methods, though, unless they are decorated with the@property
decorator, in which case use:attr:
.:term:`<term_name>`
Links to the glossary entry to the term
<term_name>
on the same page.Note
The
:term:
role can only be used in conjunction with theglossary
directive (documented below).
In Function and Class Docstrings
These are roles that are only used inside docstrings for functions and classes.
:param <param_name>:
Documents parameter
<param_name>
of the function being documented. This is also used in class docstrings, which implicitly document the__init__
method. The description should begin on the next line, indented by four spaces.:type <param_name>: <type>
Documents the type of parameter
<param_name>
as being<type>
. Usually only necessary in class docstrings. The type should go on the same line, unless it is very long (in which case it goes on the next line, indented by four spaces).Note
You may put
:type:
roles on the line immediately following a:param:
’s description, instead of separating with a line as would usually be done. There should still be a blank line after the:type:
line, though.:returns:
Documents the return value of a function. The description should begin on the next line, indented by four spaces.
:rtype: <type>
Documents the return type of a function as being
<type>
. Follows the same conventions as:type:
, documented above.
Directives
.. code-block:: <optional_language_specification>
Begins a literal code block. If a language is specified, syntax highlighting will be used. (We only use
python
andrest
so far.)The
code-block
directive is smart and can handle multiple line breaks... glossary::
Creates a glossary of terms where each term has a definition and can be linked to with the
:term:
role... toctree::
Creates a table of contents tree. The
toctree
directive is used on any page that collects other pages of documentation, such as docs/_source/index.rst.
In the Python Code
There is generally not need for use of directives within the Python docstrings, but we do make regular use of admonitions. Admonitions are directives which get styled as a colored box of text and are useful for making warnings or pointing out particularly noteworthy bits of information. The common admonitions are:
.. deprecated:: <deprecated_version>
Documents when the code has been deprecated. The
<deprecated_version>
is a required argument, and should indicate the version of SweetPea in which the code was deprecated. The description on the next line is optional, but if present should be short and tell what to use instead... note::
Generally used to document information for a user of the code. Notes should convey interesting information, including behavior that may be slightly unintuitive or mildly surprising. Notes can also be used for clarification when that seems useful.
Note
Notes should not be used for behavior that is potentially dangerous or especially misleading. Prefer the
warning
directive for those cases.Note
The
note
directive is for documentation. Prefer# NOTE: ...
block comments for notes about the code itself... tip::
A handy tip about using the code being discussed. These are generally gentle reminders of something a user may have forgotten, but should not contain critical information.
.. todo::
Incomplete code or documentation. We generally prefer block comments for code to-dos, but a
todo
directive is more appropriate when it’s marking documentation that needs to be finished in the middle of a larger docstring... warning::
Similar to
note
, but more severe. Warnings should be used to document cases where the code may behave surprisingly, such as unexpected side-effects.
Note that there are more admonitions than these, and custom admonitions can be
written with the admonition
directive, but these are the ones we currently
use in the Python code.
Docstrings
Python docstrings are triple-quoted strings that are inserted immediately
after the construct they document. For example, if we wanted to document a
function foo
:
def foo(arg1: Type1, arg2: Type2) -> ReturnType:
"""Returns the result of fooing ``arg1`` with ``arg2``.
:param arg1:
A good argument.
:param arg2:
Another good argument.
:returns:
Some nifty thing or other.
"""
# This is the actual function implementation.
return do_a_foo(arg1, arg2)
Even in docstrings, we restrict reST code to go no further than the 80th column in a line.
Functions
Function docstrings should be written in the present tense with active voice, giving the function agency. For example, a function that adds two numbers might be documented as:
def add(lhs: int, rhs: int) -> int:
"""Adds ``lhs`` to ``rhs`` and returns the result.
.. note::
This function is just a big wrapper for ``+``.
:param lhs:
The left-hand side of the addition.
:param rhs:
The right-hand side of the addition.
:returns:
The result of adding the numbers.
"""
return lhs + rhs
Style Notes
We use the double quotation mark
"
.The documentation begins on the same line as the initial triple quote
"""
.If the content is short enough to fit on one line and also fit the closing triple quote within the 80-character line length limit, the closing triple quote goes on the same line.
If the content itself fits within the 80-character line length limit but the closing triple quote does not, the closing triple quote is placed on its own line.
In all other cases, the closing triple quote goes on its own line.
The function body begins on the line immediately after the closing triple quote in every case. (Note that code comments beginning with
#
are counted as part of the function body.)
Content Notes
The description of the function should be thorough enough to fully explain the function’s purpose and how to use it, but it should not degrade into examples or detailed explanations of experimental designs unless such examples are absolutely necessary. Broader discussion of experimental design should be relegated to the guide.
Parameters are documented after the function description.
All parameters are explicitly documented separately with
:param ___:
.The function’s return value is documented with
:returns:
after the parameters.All reST roles (such as
:param ___:
and:returns:
) are preceded by an empty line.
Classes
Class docstrings begin just after the class definition begins, and should describe what an abstract instance of that class is for. Note that the rules for placing the closing triple quote are the same as those for functions.
Class docstrings should be written in place of docstrings on __init__
methods, because many classes are written without an __init__
(such as
those made with dataclasses.dataclass()
). The class docstrings should
document all of the parameters to the initialization process, including
InitVars
and the like.
Unlike docstrings for functions, class docstrings should always be followed by an empty line.
Attributes
In most cases, all public attributes (fields and methods) should be documented.
Fields cannot be documented with docstrings, and instead are documented with a
modified form of the Python line comment using #:
instead of #
:
from dataclasses import dataclass
@dataclass
class FieldExample:
"""An example for documenting a field.
:param field:
The field we're making an example of.
:type field: int
"""
#: An integer field in the :class:`.FieldExample`.
field: int
The style of these comments is identical to that of docstrings, except that each
line must begin with #:
. If you want to include a regular (non-reST) comment
about a field, it must go before the #:
or else Sphinx will not build the
documentation correctly.
If a field is overriding some default value set by a superclass, the child class’s field does not necessarily need to be documented. Consider the following example:
from dataclasses import dataclass
@dataclass
class Parent:
"""The parent class in this example.
:param field:
The field we're making an example of.
:type field: int
"""
#: An integer field for example.
field: int
@dataclass
class Child(Parent):
"""The child class in this example. Note that the :attr:`.field` field
does not have a ``#:`` reST comment accompanying it.
:param field:
The field we're making an example of. Note the nifty new default
value!
:type field: int
"""
field: int = 42
Though not required, it is also encouraged to document atypical non-public
attributes in the same manner as if they were public. By this we mean that you
can omit documentation for common non-public attributes, like double-underscore
methods (e.g., __str__
), but we encourage documentation for custom or unique
non-public fields.
Modules
Modules should also have docstrings that explain the purpose and use of the module. Module docstrings are written beginning on the very first line of the module. All modules should have a module docstring.