Skip to content

Documentation standards

Rodrigo Girão Serrão edited this page Jan 30, 2023 · 17 revisions

This document is currently a draft and nothing of what is written here must be taken for a fact or as an actual recommendation.

This document describes the documentation standards followed when documenting the Textual codebase.

This document serves as a set of guidelines aimed at removing the burden of making decisions from the person writing documentation. Above all, use your best judgement and don't be afraid to “go against” these recommendations when it makes sense.

Documentation in code

General guidelines

  • Keep the docstrings as close as possible to the entity being documented.

  • Do not write type hints in comments. Our tooling generally picks it up from the actual signatures in the code and that deduplicates things that need to be maintained.

  • Docstrings are always under what they are documenting, regardless of whether you are documenting a class or a variable.

Special sections

The plugin mkdocstrings recognises some special sections, depending on the style we use for our docstrings (Google style) and depending on the context (are we documenting a class attribute? A function? A property?).

Args

Special section to document the arguments of a callable. Each item corresponds to an argument and you don't need to type them explicitly because the code is inspected to figure out the types of the arguments.

Example:

def my_function(x: int, b: bool) -> int:
    """
    Args:
        x: Description of what `x` is for. This can extend a bit if needed,
            in which case it must be indented to distinguish from the documentation
            for the other arguments.
        b: A super useful Boolean.
    """

Attributes

Special section to document the attributes of a class. This creates a summary table with attribute names, types, and descriptions. For attributes to be adequately typed in the Attributes section, they must be typed explicitly (either in the body or in __init__).

Example:

class MyClass:
    """
    Attributes:
        attr1: This attribute gets its type hint from the body of the class.
        attr2: This attribute gets its type hint from the method `__init__`.
            This is an extended description of the attribute.
    """

    attr1: bool

    def __init__(self):
        self.attr1 = True
        self.attr2: int = 42  # The explicit type hint here is for `Attributes` to pick it up.

Examples

Special section to write down usage examples. Only useful when the examples you want to write out make sense in a REPL session.

Example:

def add_with_a_twist(a: int, b: int) -> int:
    """Adds two integers with a twist.

    Examples:
        You can write prose and also snippets from a REPL session.

        >>> add_with_a_twist(10, 5)
        5

        >>> add_with_a_twist(5, 10)
        -5
    """

    return a - b

Raises

Special section to document exceptions the code may raise. This creates a summary table with all the exceptions that the code might raise and the reasons that may lead to those exceptions.

class MyException(Exception):
    pass


def my_division(a: int, b: int) -> float:
    """
    Raises:
        MyException: When the result of the division would be negative.
        ZeroDivisionError: When `b` is zero.
    """

    if a < 0 and b > 0 or a > 0 and b < 0:
        raise MyException("Why?!")
    return a / b

Returns

Special section to document the return value(s) of methods and functions. Do not add explicit type hints to the docstring. (Use Yields for generators.)

import random


def dice_roll() -> int:
    """
    Returns:
        A random integer between 1 and 6, inclusive.
    """
    return random.randint(1, 6)

If the return type is a tuple, each value can be documented separately:

def split_name(full_name: str) -> tuple[str, str, str]:
    """
    Returns:
        The first name of the person.
        All the middle names of the person.
            This may be an empty string if no middle names are present.
        The last name of the person.
            This may be an empty string if there is no last name.
    """
    first, *others = full_name.split(" ", maxsplit=1)
    *middle, last = (others[0] if others else "").rsplit(" ", maxsplit=1)
    return first, (middle[0] if middle else ""), last

Documenting a function

  1. Short function description in the first line.
  2. Empty blank line.
  3. Longer description with further details, examples, notes, nuances, things to consider, etc.
  4. A section
def my_function(x: int, b: bool) -> int:
    """Short function description.

    Longer function description.
    This is a good place to show examples (if needed) or to talk about
    nuances or idiosyncrasies of this function.
    I suggest you start new sentences in new lines.

    Note:
        Any thing that starts with a string and then contains a colon will
        render a new collapsible admonition in our docs.
        Some of these blocks have special treatment, like the ones that follow.

    Args:
        x: The important integer. If the description becomes really long,
            you can wrap it without any issues BUT you need to make sure you
            indent it.
        b: The important Boolean.

    Raises:
        SomeError: Why/when is this error raised?
        AnotherError: How about this one?

    Returns:
        Description of what is returned.
    """

    return x if b else -x
"""Short module description.

Write a longer description here.
Any examples worth showing?
I suggest you start new sentences in new lines.
Always.
Easier to read, easier to maintain, and generates better diffs.
"""


GLOBAL: type = value
"""Short variable description.

Do you need to add more detail?
Do so here, without any problem!
"""

Documentation outside source code

General style guidelines

  • In long-form comments/descriptions, start new sentences in new lines:

    • they are easier to read;
    • they are easier to maintain; and
    • they generate better diffs.
  • Write full sentences in description columns in tables:

    • upper-case first letter; and
    • full stop at the end.
  • If list items start with upper case, they end with full-stop. Otherwise, they end with semicolon. Keep it homogenous in the same list/document.

  • Use sass for Textual CSS code blocks.

References

Clone this wiki locally