Doctests
Basic Examples
Including interactive examples in the documentation is helpful to users. They can be tested automatically and these tests are called doctests. We use pytest to run doctests, which in turn uses the Python’s doctest. Some examples:
def f():
"""Description.
Examples
--------
>>> 13 * 2
26
"""
pass
and
.. rst file
>>> print("Hello ddg")
Hello ddg
>>> 13 * 2
26
or
.. rst file
.. doctest::
>>> print("Hello ddg")
Hello ddg
>>> 13 * 2
26
A multiline example:
.. doctest::
>>> import ddg.datastructures.indexedfaceset.ifs_generator as gen
>>> cylinder = gen.cylinder(
... resolution=20,
... top_radius=1,
... bot_radius=1,
... length=1,
... center=(0, 0, 0),
... normal=(0, 0, 1),
... )
>>> cylinder
<ddg.datastructures.indexedfaceset.ifs.IndexedFaceSet object at 0x...>
For more details consult the doctest documentation.
Note
Avoid # doctest: +SKIP comments if at all possible.
They used to be necessary in order to run the doctests outside of Blender without failures (as bpy isn’t available).
This is no longer the case — nowadays Blender doctests are run within Blender.
Every Python docstring that has examples counts as one doctest.
This means that one *.py file can have multiple doctests.
In contrast, every *.rst file counts as one doctest.
Doctest Conventions
Use absolute paths when importing something in a doctest, don’t use * imports.
Do
.. doctest::
>>> from ddg.math.grids import triangle_grid
>>> triangle_grid
<function triangle_grid at 0x...>
or
.. doctest::
>>> import ddg.math.grids
>>> ddg.math.grids.triangle_grid
<function triangle_grid at 0x...>
Do NOT
.. doctest::
>>> from ddg.math.grids import *
>>> triangle_grid
<function triangle_grid at 0x...>
In general, it is best to show both the input and output like so
.. doctest::
>>> import numpy as np
>>> a = np.arange(10).reshape(2, 5)
>>> a
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
>>> b = a * a
>>> b
array([[ 0, 1, 4, 9, 16],
[25, 36, 49, 64, 81]])
This not only makes it easier to understand, but also asserts that the input a is what you
expect it to be. If the input depends on the library and it changes in the
future, then the doctest will fail which makes it easier to debug errors.
Warnings
Python’s doctest will fail if even a single character doesn’t match the expected output. Sometimes this is undesirable, because
output may not be deterministic, for example
reprandstrmay include memory addresses which change every time the program is executed,reprandstrmay not guarantee any particular order forsetand other unordered containers (note thatdictis ordered as of Python 3.7),
output may be very long which decreases readability,
output may have tracebacks which show file paths that depend on where the program is stored.
See doctest’s documentation on directives and warnings on how to deal with these issues.