Add doctests to the test suite

- use xdoctest to validate code snippets in docstrings
- make xdoctest part of the nox session "test" via
  the new `test_docstrings()` test case
- add nox session "test-docstrings" for convenience;
  also, `xdoctest.doctest_module()` does not discover
  docstrings that are imported at the root of the package
  => each new module with docstrings must be added to
     `test_docstrings()` by hand, which is likely forgotten
  => the nox session "test-docstrings" should run on CI
This commit is contained in:
Alexander Hess 2024-09-10 01:57:02 +02:00
parent 4100a7f3f5
commit 01d270e39c
Signed by: alexander
GPG key ID: 344EA5AB10D868E0
5 changed files with 76 additions and 2 deletions

View file

@ -76,6 +76,7 @@ nox.options.reuse_venv = "no"
nox.options.sessions = ( # run by default when invoking `nox` on the CLI nox.options.sessions = ( # run by default when invoking `nox` on the CLI
"format", "format",
"lint", "lint",
"test-docstrings",
f"test-{MAIN_PYTHON}", f"test-{MAIN_PYTHON}",
) )
nox.options.stop_on_first_error = True nox.options.stop_on_first_error = True
@ -156,6 +157,7 @@ def test(session: nox.Session) -> None:
"pytest", "pytest",
"pytest-cov", "pytest-cov",
"semver", "semver",
"xdoctest",
) )
args = session.posargs or ( args = session.posargs or (
@ -167,6 +169,16 @@ def test(session: nox.Session) -> None:
session.run("pytest", *args) session.run("pytest", *args)
@nox_session(name="test-docstrings", python=MAIN_PYTHON)
def test_docstrings(session: nox.Session) -> None:
"""Test docstrings with `xdoctest`."""
start(session)
install_pinned(session, "xdoctest[colors]")
session.run("xdoctest", "--version")
session.run("xdoctest", "src/lalib")
def start(session: nox.Session) -> None: def start(session: nox.Session) -> None:
"""Show generic info about a session.""" """Show generic info about a session."""
if session.posargs: if session.posargs:

33
poetry.lock generated
View file

@ -1014,7 +1014,38 @@ files = [
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
] ]
[[package]]
name = "xdoctest"
version = "1.2.0"
description = "A rewrite of the builtin doctest module"
optional = false
python-versions = ">=3.8"
files = [
{file = "xdoctest-1.2.0-py3-none-any.whl", hash = "sha256:0f1ecf5939a687bd1fc8deefbff1743c65419cce26dff908f8b84c93fbe486bc"},
{file = "xdoctest-1.2.0.tar.gz", hash = "sha256:d8cfca6d8991e488d33f756e600d35b9fdf5efd5c3a249d644efcbbbd2ed5863"},
]
[package.dependencies]
colorama = {version = ">=0.4.1", optional = true, markers = "platform_system == \"Windows\" and extra == \"colors\""}
Pygments = {version = ">=2.4.1", optional = true, markers = "python_version >= \"3.5.0\" and extra == \"colors\""}
[package.extras]
all = ["IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "tomli (>=0.2.0)"]
all-strict = ["IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "tomli (==0.2.0)"]
colors = ["Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "colorama (>=0.4.1)"]
colors-strict = ["Pygments (==2.0.0)", "Pygments (==2.4.1)", "colorama (==0.4.1)"]
docs = ["Pygments (>=2.9.0)", "myst-parser (>=0.18.0)", "sphinx (>=5.0.1)", "sphinx-autoapi (>=1.8.4)", "sphinx-autobuild (>=2021.3.14)", "sphinx-reredirects (>=0.0.1)", "sphinx-rtd-theme (>=1.0.0)", "sphinxcontrib-napoleon (>=0.7)"]
docs-strict = ["Pygments (==2.9.0)", "myst-parser (==0.18.0)", "sphinx (==5.0.1)", "sphinx-autoapi (==1.8.4)", "sphinx-autobuild (==2021.3.14)", "sphinx-reredirects (==0.0.1)", "sphinx-rtd-theme (==1.0.0)", "sphinxcontrib-napoleon (==0.7)"]
jupyter = ["IPython (>=7.23.1)", "attrs (>=19.2.0)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)"]
jupyter-strict = ["IPython (==7.23.1)", "attrs (==19.2.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)"]
optional = ["IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "tomli (>=0.2.0)"]
optional-strict = ["IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"]
tests = ["pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)"]
tests-binary = ["cmake (>=3.21.2)", "cmake (>=3.25.0)", "ninja (>=1.10.2)", "ninja (>=1.11.1)", "pybind11 (>=2.10.3)", "pybind11 (>=2.7.1)", "scikit-build (>=0.11.1)", "scikit-build (>=0.16.1)"]
tests-binary-strict = ["cmake (==3.21.2)", "cmake (==3.25.0)", "ninja (==1.10.2)", "ninja (==1.11.1)", "pybind11 (==2.10.3)", "pybind11 (==2.7.1)", "scikit-build (==0.11.1)", "scikit-build (==0.16.1)"]
tests-strict = ["pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "4ba46548b380fe7c34cf4ed48492253b2d3a1866f86c8d8bf569eb18829281c2" content-hash = "3648b3426753e3e36e575d6b08fe8ab1b5f5443dcf2d1effaed5e921cdb40933"

View file

@ -65,6 +65,7 @@ pytest = "^8.3"
pytest-cov = "^5.0" pytest-cov = "^5.0"
semver = "^3.0" # to test the version identifier semver = "^3.0" # to test the version identifier
tomli = [ { python = "<3.11", version = "^2.0" } ] tomli = [ { python = "<3.11", version = "^2.0" } ]
xdoctest = { extras = ["colors"], version = "^1.2" }
[tool.poetry.urls] [tool.poetry.urls]
@ -264,6 +265,7 @@ module = [
"pytest", "pytest",
"semver", "semver",
"tomli", "tomli",
"xdoctest",
] ]
ignore_missing_imports = true ignore_missing_imports = true

View file

@ -1,4 +1,10 @@
"""A Python library to study linear algebra.""" """A Python library to study linear algebra.
First, verify that your installation of `lalib` works:
>>> import lalib
>>> lalib.__version__ != '0.0.0'
True
"""
from importlib import metadata from importlib import metadata

23
tests/test_docstrings.py Normal file
View file

@ -0,0 +1,23 @@
"""Integrate `xdoctest` into the test suite.
Ensure all code snippets in docstrings are valid and functioning code.
Important: All modules with docstrings containing code snippets
must be put on the parameter list below by hand!
"""
import pytest
import xdoctest
@pytest.mark.parametrize(
"module",
[
"lalib",
],
)
def test_docstrings(module):
"""Test code snippets within the package with `xdoctest`."""
result = xdoctest.doctest_module(module, "all")
assert result["n_failed"] == 0