Python Docstrings — reST Style
A docstring earns its place by saying what type hints cannot: meaning, constraints,
side effects, and failure conditions. reST style expresses those in Sphinx's native
language — info field lists (:param:, :returns:, :raises:) and cross-reference
roles — so write for two readers at once: a human scanning a tooltip and Sphinx
rendering API docs with real links.
Canonical shape
async def fetch_one(self, query: str, *args: Any) -> Mapping[str, Any] | None:
"""Execute a query and return the first matching row.
:param query: SQL query text with numbered placeholders.
:param args: Positional parameters bound to the placeholders.
:returns: The first row as a mapping, or ``None`` when no rows match.
:raises QueryError: If the query is malformed or the connection is closed.
"""
- Field order:
:param:→:returns:→:raises:; keep the field list as one block after the summary (and optional body), separated by a blank line. - Name varargs without stars in the field (
:param args:); the signature already shows the stars. - With annotations present, omit
:type:and:rtype:— duplicating annotations invites drift, and Sphinx autodoc can merge annotations into the output.
Summary line
- One line directly after
""", ending with a period; blank line before anything else. State what the caller gets — never "This function ...". - PEP 257 prescribes imperative mood for functions and methods: a command such as "Return the first row.", not the description "Returns the first row.".
Field lists
| Field | Use for |
|---|---|
:param name: |
Parameter description (aliases: parameter, arg, argument) |
:param type name: |
Inline type — works only when the type is a single word |
:type name: |
Parameter type, when needed and not annotated |
:returns: |
Meaning of the return value (alias: return) |
:rtype: |
Return type, when needed and not annotated |
:raises ExcName: |
Trigger condition (aliases: raise, except, exception) |
:ivar name: / :cvar name: |
Instance/class variables — prefer trailing docstrings instead |
Standardize on :param:, :returns:, and :raises: — the aliases render the same,
but one spelling keeps grep and review trivial. Sphinx cross-references the exception
name in :raises ExcName: automatically, like an implicit :exc: role. Include only
fields that add information; a restating field is noise.
Cross-references and literals
Use roles instead of plain names — they become real links in rendered docs:
:mod: (modules), :class: (classes), :meth: (methods), :func: (functions),
:attr: (attributes and properties), :exc: (exceptions), :data: (module-level
data), :obj: (anything else). Prefix a target with ~ to render only its last
component:
:meth:`initialize` -> link "initialize" (resolved within the class)
:class:`pkg.mod.Foo` -> link "pkg.mod.Foo" (cross-module: fully qualify)
:meth:`~queue.Queue.get` -> link rendered as just "get"
Unqualified targets resolve against the current class, then the current module —
so short names work locally; fully qualify cross-module targets. Use double
backticks for literal values — None, 'tuple', SQL fragments, flags,
environment variables — so they render as code, not prose.
:raises: discipline
- Document only exceptions relevant to the caller's interface, each with its trigger condition — a bare exception name tells the caller nothing actionable.
- Never document exceptions raised because the caller violated the documented contract: that would paradoxically make behavior under violation of the API part of the API.
- On protocols and other interfaces, add
:raises:only when raising is a required part of the contract, not a detail of one implementation.
Beyond plain functions
Classes and their attributes, properties, generators, type aliases, constants,
TypedDict, and @overload / Protocol stubs each have a shape of their own —
references/constructs.md. The rule that governs all
of them: document the contract at the level the caller meets it. A property
is documented as the value it yields, not as the method it happens to be; an
@overload stub documents the one signature it declares, and the implementation
carries the shared body.
Length and formatting
- Default docstring is the summary line alone. Add a body only when it earns its
place — non-obvious behavior, side effects, invariants, "why" — in 1–3 sentences.
Size
:param:/:returns:/:raises:to the real API surface. - Never restate the type in prose. Annotations carry the type; the description adds semantics: units, constraints, defaults on absence.
- Blank line between the summary (or body) and the field list.
- Wrap near 88 columns; the summary must stay on one physical line.
Anti-patterns
| Wrong | Right |
|---|---|
:param timeout: The timeout, an integer. |
:param timeout: Seconds to wait for a pool connection. |
:returns: The result. |
:returns: True if the row was inserted. |
"""Returns the pathname.""" |
"""Return the pathname.""" — PEP 257 imperative mood |
Plain-text reference PostgresClient.transaction |
:meth: role — plain names never link |
:yields: field |
Describe the iterator in :returns: — Sphinx has no yields field |
:raises ValueError: for arguments the contract already forbids |
Omit — contract violations are not interface behavior |
Related skills
- python-google-docstrings — the same rules expressed as Google-style sections via Sphinx Napoleon
readable-code— better names and structure shrink what a docstring must explain. Absent it, the boundary still holds: a docstring states the contract, not the implementation.- altitude-docs — deciding what belongs in docstrings vs higher-level documentation