# Writing sphinx-friendly docstrings
The HTML docs are built from docstrings by sphinx `autoclass`/`autodoc`, with the
`sphinx.ext.napoleon` extension enabled (see `docs/source/conf.py`). Docstrings are
rendered as reStructuredText, and napoleon additionally parses NumPy/Google-style
sections. A few conventions keep the rendered output clean. (See also
`notes/pybind11.md` for the pybind-specific version of these rules.)
## Class docstrings for classes with method injections
A pybind11-wrapped class that is extended via `ksgpu.inject_methods` (see
`notes/pybind11.md`) can carry its class docstring on either side. `inject_methods`
copies the injector class's docstring onto the C++ class *when the injector defines
one* -- and that overrides the docstring set in the pybind11 binding. If the injector
has no docstring, the pybind11 docstring stands.
To avoid confusion and accidental clobbering, put each such class's docstring on
exactly ONE side, and leave a short comment on the other side pointing to it:
- **Option 1 -- docstring in the pybind11 binding.** Write the class docstring in the
C++ `py::class_<...>(m, "Name", "docstring...")`. In the injector class, write *no*
docstring; instead put a `#` comment noting the docstring lives in pybind11 (so the
next reader knows not to add one here, since it would override).
- **Option 2 -- docstring in the injector class.** Write the class docstring as the
Python injector's docstring, and set *no* docstring in the C++ `py::class_<...>(m,
"Name")`; put a C++ comment there noting the docstring lives in the injector.
Choosing between them -- put the docstring where the class is most naturally described:
- Use **option 1** for classes that are essentially C++ objects with a thin Python
shim (a constructor/`launch`/setter that just converts dtypes, streams, or aflags),
especially when a good C++ docstring already exists. Examples: `BumpAllocator`,
`SlabAllocator`, `DedispersionConfig`, `GpuDequantizationKernel`, `CasmBeamformer`.
- Use **option 2** when the class's primary Python interface *is* the injection -- a
context-manager usage pattern, or accessors that return Python wrapper objects -- so
the narrative (and the read-only-attribute bullet list, per the next section) belongs
next to that Python code. Examples: `FrbGrouper` (the `with ... as g:` /
`g.get_output(...)` API), `GpuDedisperser` (`get_input`/`get_output` context
managers), `CudaStreamPool` (accessors returning `ksgpu.CudaStreamWrapper`).
Either way the docstring is plain rST/napoleon, so all the conventions below apply
regardless of which side it lives on.
## Simple properties / attributes: document them in the class docstring
Prefer documenting "simple" members -- typical read-only pybind11 properties
(`def_readonly`) and plain data attributes -- with a one-line bullet in the class
docstring, and leave the binding itself bare (no per-property docstring). For example,
in C++:
```
py::class_(m, "Foo",
"One-line summary of Foo.\n"
"\n"
"Attributes (read-only):\n"
"\n"
"- ``nbatches`` (int) -- beam-batches per time chunk.\n"
"- ``grouper_ip_addr`` (str) -- the grouper's listen address (``ip:port``).\n")
.def_readonly("nbatches", &Foo::nbatches)
.def_readonly("grouper_ip_addr", &Foo::grouper_ip_addr);
```
Why this, rather than a per-property docstring:
- It renders compactly (one line per member), instead of the multi-line
name / type / description block that a per-property docstring produces.
- The members are not registered as separate sphinx objects, so they stay out of
the right-hand "on this page" sidebar (a long auto-generated property list there
is mostly noise).
- It sidesteps a napoleon gotcha: napoleon reads a property docstring as
"type: description" and splits it at the first colon, which mangles any docstring
containing a colon (see "the colon gotcha" below).
Conventions for the bullets:
- Use ` -- ` (space-dash-dash-space) between the name and the description, not a
colon. (` -- ` renders as an en-dash; a colon is easier to misparse.)
- Wrap the member name -- and any token containing a colon, `*`, or `_` -- in
double backticks, so it renders as monospace and is inert in rST.
- Do not start the lead-in line with a bare section keyword followed by a colon
(`Attributes:`, `Parameters:`, ...): napoleon would treat it as a section header
and re-parse the bullets. `Attributes (read-only):` is safe (the `(` defeats the
match).
This applies to attributes set from Python too -- e.g. in `__init__`, or attached by a
context manager's `__enter__` -- not just pybind11 properties. Document the key public
ones in the same bullet style; omit attributes that exist only for the class's internal
use (conventionally underscore-prefixed).
Methods are different: leave them as normal `:members:`-documented methods, each with
its own docstring -- they belong in the sidebar.
**The colon gotcha.** If you do write a per-member docstring anyway (e.g. a pybind11
`def_readonly("x", &C::x, "...")`, so `help(obj.x)` shows text), keep a bare colon out
of its first line. napoleon reads a member/attribute docstring as `type: description`
and splits it at the first colon, so e.g. `"listen address ('ip:port')"` renders as a
mangled "Type" field in the `autoclass` output. Wrap the colon-bearing (or `*`/`_`-
bearing) token in a double-backtick inline literal, which prevents the split and renders
it as monospace:
```
Bad: "The grouper's listen address ('ip:port'), set at construction."
Good: "The grouper's listen address (``ip:port``), set at construction."
```
(This only affects member/attribute docstrings, not method docstrings.)
## Method / function docstrings
For methods with parameters or return values, use NumPy-style sections:
```
Summary line.
Extended description (free prose) goes here, ABOVE the sections.
Parameters
----------
x : int
Description of x.
Returns
-------
SomeType
Description.
```
- Put all free prose in the summary / extended description, above the first section.
Do NOT put prose paragraphs between sections (e.g. between Parameters and Returns):
napoleon parses the base-indented lines after a parameter list as more parameters,
shredding the prose.
- Recognized section names include Parameters, Returns, Yields, Raises, Notes; each
is underlined with dashes (NumPy style).
## General rST hygiene
- Keep bullet lists (and paragraphs) flush with the surrounding text. Indenting a
block more than its surroundings makes rST render it as a block quote -- a grey,
extra-indented box. Autodoc strips the docstring's common leading indent first, so
what matters is indentation *relative to* the other body lines, not the raw column.
- Inline literal (monospace): wrap the text in double backticks.
- A bare `*` or `_` can be read as rST emphasis/target markup; wrap such tokens in
double backticks if they are meant literally.
- A literal block is introduced by `::` at the end of a line, followed by an indented
block (used e.g. for the `Usage::` examples in some class docstrings).