Skip to main content
Documenting your code
This course material was developed as part of UNIVERSE-HPC, which was funded through the SPF ExCALIBUR programme under grant number EP/W035731/1

This course material was developed as part of UNIVERSE-HPC, which was funded through the SPF ExCALIBUR programme under grant number EP/W035731/1

Creative Commons License

Documenting your code

Another kind of coverage

Code coverage asked: which lines do the tests run?
There is a second question worth asking of the same codebase: which functions has anybody bothered to document? Nothing we have run so far tells us. The tests pass whether or not a function is documented, and so does the coverage report.
In the next section you will generate API documentation automatically from the docstrings in your code. That only works for the functions that have one.

Checking docstrings with ruff

Fortunately, we can use a tool we're already familiar with. Ruff's rule set includes pydocstyle, under the D prefix, which checks docstrings rather than code.
These rules are deliberately not part of the lint step you wrote earlier. Linting the code and checking the documentation are deliberately done at different points. Mixing them means a missing docstring could block you while you are trying to fix a real bug.
Run the check:
ruff check --select D src/
Two points to note.
--select D picks the docstring rules instead of the default selection in pyproject.toml.
src/ scopes the check to the package. Run it over the whole repository and it reports your tests as well, which do not necessarily need docstrings: a test's name is usually a good-enough description, and """Test that it works.""" above test_that_it_works doesn't add much.

Configuring the rules

Look in pyproject.toml:
[tool.ruff.lint.pydocstyle] convention = "numpy"
There are several common docstring styles, and the rules for one contradict the rules for another. convention tells ruff which one this project uses, and switches off the rules belonging to the others.
This matters quite a bit. Without that line the same command reports 15 problems on this project, nearly all of them complaining that perfectly good docstrings are laid out in the wrong style. With it, you get the one that is real. It's a good idea to pick a style and stick with it: don't spend ages agonising over which style you like the most. The most important thing is that you're running a tool that will tell you useful information about jobs you've forgotten to do.

Automating it

Add a docstrings workflow

Write a workflow, docstrings.yml, that runs this check on every push and pull request.
One job, one Python version, no matrix: you are checking the source, and the source does not change between Python versions.

Fixing the finding

The check fails with one finding:
D103 Missing docstring in public function --> src/ci_course/stats.py:41:5

Document the undocumented function

Write a docstring for count_missing, in the same style as the rest of the package, and get the check passing.

Why this finding mattered

count_missing was not dead code. It is exported from the package, it has a test, and that test passes. By every measure you have applied so far, it was fine.
Keep that in mind during the next section, where we will use a tool called Sphinx to generate an API reference from these docstrings. Sphinx documents the things that have a docstring. A function without one is not flagged, not marked incomplete, and not listed: it is simply absent, and the generated documentation looks complete.

Next steps

  1. [optional] read the ruff pydocstyle rules, particularly the D1 group, which is the "is there one at all" family.
  2. [optional] look at numpydoc, the docstring style this project uses.