Writing documentation¶
This page explains how to add to these docs. There are two kinds of content:
Prose pages — hand-written guides like this one, in reStructuredText (
.rst).API reference — generated automatically from the docstrings in the
semi_crsource code.
Building the docs locally¶
Run the docs server from the repo root:
uv run docs
This regenerates the API reference from docstrings, builds the HTML, serves it
at http://127.0.0.1:8000, opens a browser tab, and rebuilds on every save.
Press Ctrl-C to stop.
The built HTML lands in docs/_build/html and is not committed to the
repo (it is .gitignored). Rendered Jupyter notebooks additionally require
the pandoc binary; without it the notebook pages are skipped locally but the
surrounding prose still builds.
Writing prose pages (reStructuredText)¶
Prose pages are .rst files under docs/. Sphinx uses
reStructuredText,
not Markdown. To make a new page appear, create the file and add it to a
toctree (see Wiring a page into the site below).
reST cheat sheet¶
Headings are text underlined (and optionally overlined) with punctuation.
The character you use defines the level; be consistent within a file. A common
order is * (with overline) for the title, then =, -, ~:
*****
Title
*****
Section
=======
Subsection
-----------
Important
The underline must be at least as long as the text, or Sphinx emits a “Title underline too short” warning and may misparse the page.
Inline markup:
*italic* **bold** ``literal / code``
Lists — blank line before the list, indentation for continuation:
- bullet one
- bullet two
#. auto-numbered
#. auto-numbered
Code blocks — a :: at the end of a paragraph starts a literal block;
the block must be indented:
Here is some Python::
x = greeter("world")
print(x)
Use .. code-block:: python when you want syntax highlighting for a specific
language:
.. code-block:: python
def f(x):
return x + 1
Links:
`link text <https://example.com>`_ external link
:doc:`api/index` link to another page
:ref:`wiring-a-page` link to a labelled target
:func:`semi_cr.core.helpers.deprecated` link to an API object
Admonitions draw attention to a note:
.. note::
Blank line, then indented body.
.. warning::
Same shape: ``note``, ``warning``, ``important``, ``tip``, ...
Wiring a page into the site¶
A page is invisible until it is listed in a toctree. Add its filename
(without .rst) to the toctree in docs/index.rst:
.. toctree::
:maxdepth: 2
api/index
writing_docs
example_notebooks/index
changes/index
Nested sections get their own index.rst with a local toctree — see
docs/example_notebooks/index.rst for an example that globs its contents.
Documenting code for the API reference¶
The API pages under semi_cr API Documentation are generated, not written by hand. On
every build the sphinx.ext.apidoc extension scans the source tree and
autodoc imports each module and reads its docstrings. So “adding API
docs” means writing good docstrings; nothing else is required.
Important
Only the semi_cr.core subtree is included in the API reference. The
apps, setup, research and portable subtrees are excluded
because they run hardware connections / interactive prompts at import time,
which autodoc cannot do during a build. If you move reusable, safely
importable code into core, it shows up automatically. See the
apidoc_modules setting in docs/conf.py for the exclusion list.
Docstring style¶
This project uses Google-style docstrings (enforced by ruff’s
pydocstyle config) which Sphinx understands via the napoleon extension.
A function docstring looks like this:
def greeter(name: str, *, loud: bool = False) -> str:
"""Generate a greeting message.
A longer description can go here, spanning multiple lines. Keep the
one-line summary on the first line, then a blank line, then details.
Args:
name: The name of the person to greet. Types come from the
annotation, so you don't need to repeat them here.
loud: If ``True``, shout the greeting.
Returns:
The formatted greeting.
Raises:
ValueError: If ``name`` is empty.
Example:
>>> greeter("world")
'Hello, world'
"""
Classes follow the same shape. Because autoclass_content = "both" is set,
both the class docstring and the __init__ docstring are shown, so document
constructor arguments in __init__:
class Chip:
"""A device under test.
Attributes:
name: Human-readable identifier for the chip.
"""
def __init__(self, name: str) -> None:
"""Initialise the chip.
Args:
name: Human-readable identifier for the chip.
"""
self.name = name
Common sections recognised by napoleon: Args, Returns, Yields,
Raises, Attributes, Example/Examples, Note, Warning.
What gets picked up¶
Modules, classes, functions and methods are documented from their docstrings automatically once the module lives under
semi_cr.core.Undocumented members still appear (
undoc-membersis on), just without descriptions — so a docstring is always an improvement.Private names (leading underscore) are omitted, except dunder methods that carry a docstring.
A new module or subpackage under
coreis included on the next build;sphinx-apidocrediscovers the tree each time, so you don’t edit any.rstby hand.
Avoiding docstring warnings¶
autodoc re-parses docstrings as reST, so the reST rules above apply inside
them. The two most common warnings:
“Unexpected indentation” — usually a continuation line under
Args:that isn’t indented consistently, or a code example not introduced with::.“Title underline too short” — a
Returns/Argsline written as a reST heading (underlined with---) instead of the GoogleReturns:section label. Use theSection:form, not underlines, inside docstrings.
Build with uv run docs and read the warnings it prints; each one names the
file and line so you can fix it at the source.