Xdoctest
A rewrite of Python's builtin doctest module (with pytest plugin integration) with AST instead of RegEx.
Install / Use
/learn @Erotemic/XdoctestREADME
|GithubActions| |CircleCI| |Appveyor| |Codecov| |Pypi| |PypiDownloads| |ReadTheDocs|
.. The large version wont work because github strips rst image rescaling. .. image:: https://i.imgur.com/u0tYYxM.png :height: 100px :align: left
Xdoctest - Execute doctests. A Python package for executing tests in documentation strings!
What is a doctest <https://en.wikipedia.org/wiki/Doctest>?
It is example code you write in a docstring!
What is a docstring <https://en.wikipedia.org/wiki/Docstring>?
Its a string you use as a comment! They get attached to Python functions and
classes as metadata. They are often used to auto-generate documentation.
Why is it cool?
Because you can write tests while you code!
Tests are good. Documentation is good. Examples are good. Doctests have low boilerplate, you write them in the same file you write your code. It often can help you write the function. Write down how to construct minimal demo inputs (it helps to have tools to create these) in your file. Copy that code into IPython/Jupyter, and play with your implementation. Copy your finished code into the body. Write down how to call the function with the demo inputs. If you feel inclined, check that the result matches an expected result (while asserts and checks are nice, a test that just shows how to run the code is better than no test at all).
.. code:: python
def an_algorithm(data, config):
"""
Example:
>>> data = '([()[]])[{}([[]])]'
>>> config = {'outer': sum, 'inner': ord}
>>> an_algorithm(data, config)
1411
"""
# I wrote this function by first finding some interesting demodata
# then I wrote the body in IPython and copied it back in.
# Now I can reuse this test code I wrote in development as a test!
# Covered Code is much easier to debug.
# We have a Minimal Working Example (MWE)!
result = config['outer'](map(config['inner'], data))
return result
The problem? How do you run the code in your doctest?
Xdoctest finds and executes your doctests for you.
Just run xdoctest <path-to-my-module>.
It plugs into pytest to make it easy to run on a CI. Install and run
pytest --xdoctest.
The xdoctest package is a re-write of Python's builtin doctest
module. It replaces the old regex-based parser with a new
abstract-syntax-tree based parser (using Python's ast module). The
goal is to make doctests easier to write, simpler to configure, and
encourage the pattern of test driven development.
+------------------+----------------------------------------------+
| Read the docs | https://xdoctest.readthedocs.io |
+------------------+----------------------------------------------+
| Github | https://github.com/Erotemic/xdoctest |
+------------------+----------------------------------------------+
| Pypi | https://pypi.org/project/xdoctest |
+------------------+----------------------------------------------+
| PyCon 2020 | Youtube Video_ and Google Slides_ |
+------------------+----------------------------------------------+
.. _Youtube Video: https://www.youtube.com/watch?v=CUjCqOw_oFk .. _Google Slides: https://docs.google.com/presentation/d/1563XL-n7534QmktrkLSjVqX36z5uhjUFrPw8wIO6z1c
Quick Start
Installation: from pypi ^^^^^^^^^^^^^^^^^^^^^^^
Xdoctest is distributed on pypi as a universal wheel and can be pip installed on Python 3.8+ (Python 2.7 and 3.4 / 3.5 support was removed in Version 1.1.0, 3.6 / 3.7 support was removed in Version 1.2.0). Installations are tested on CPython and PyPy implementations.
::
pip install xdoctest
Github releases are signed with a GPG public key: 59A34380 (note: this
incorrectly was listed as D297D757 for before 2024-11-15, which was an
older CI signing key). If you care enough to check the gpg signature, you
should also verify this agrees with the contents of dev/public_gpg_key.
Usage: run your doctests ^^^^^^^^^^^^^^^^^^^^^^^^
After installing, the fastest way to run all doctests in your project is:
::
python -m xdoctest /path/to/your/pkg-or-module.py
or if your module has been pip-installed / is in the PYTHONPATH run
::
python -m xdoctest yourmodname
Getting Started
There are two ways to use xdoctest: via pytest or via the native
interface. The native interface is less opaque and implicit, but its
purpose is to run doctests. The other option is to use the widely used
pytest package. This allows you to run both unit tests and doctests
with the same command and has many other advantages.
It is recommended to use pytest for automatic testing (e.g. in your
CI scripts), but for debugging it may be easier to use the native
interface.
Check if xdoctest will work on your package ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can quickly check if xdoctest will work on your package
out-of-the box by installing it via pip and running
python -m xdoctest <pkg> all, where <pkg> is the path to your
python package / module (or its name if it is installed in your
PYTHONPATH).
For example with you might test if xdoctest works on networkx or
sklearn as such: python -m xdoctest networkx all /
python -m xdoctest sklearn all.
Using the pytest interface ^^^^^^^^^^^^^^^^^^^^^^^^^^
When pytest is run, xdoctest is automatically discovered, but is
disabled by default. This is because xdoctest needs to replace the builtin
doctest plugin.
To enable this plugin, run pytest with --xdoctest or --xdoc.
This can either be specified on the command line or added to your
addopts options in the [pytest] section of your pytest.ini
or tox.ini.
To run a specific doctest, xdoctest sets up pytest node names
for these doctests using the following pattern:
<path/to/file.py>::<callname>:<num>. For example a doctest for a
function might look like this mymod.py::funcname:0, and a class
method might look like this: mymod.py::ClassName::method:0
Using the native interface. ^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to the pytest plugin, xdoctest has a native doctest runner.
You can use the xdoctest command line tool that is installed with the
package and point it a module directory or a particular file.
You can also make it such that invoking your module as __main__ invokes the
xdoctest native runner using the using the xdoctest.doctest_module(path)
method, which can be placed in the __main__ section of any module as such:
.. code:: python
if __name__ == '__main__':
import xdoctest
xdoctest.doctest_module(__file__)
This sets up the ability to invoke the xdoctest command line
interface. python -m <modname> <command>.
However, it is typically preferred to just use the xdoctest executable and
pass it the path to your file, or the name of an installed module. In this case
it is invoked like xdoctest -m <modname> <command>.
Using either of these methods you can natively invoke xdoctest on a module or package, which exposes the command line interface. Both of these expose the command line interface, allowing you to pass a command to xdoctest.
-
If
<command>isall, then each enabled doctest in the module is executed:python -m <modname> all -
If
<command>islist, then the names of each enabled doctest is listed. -
If
<command>isdump, then all doctests are converted into a format suitable for unit testing, and dumped to stdout (new in 0.4.0). -
If
<command>is acallname(name of a function or a class and method), then that specific doctest is executed:python -m <modname> <callname>. Note: you can execute disabled doctests or functions without any arguments (zero-args) this way.
For example if you created a module mymod.py with the following
code:
.. code:: python
def func1():
"""
Example:
>>> assert func1() == 1
"""
return 1
def func2(a):
"""
Example:
>>> assert func2(1) == 2
>>> assert func2(2) == 3
"""
return a + 1
You could
- Use the command
xdoctest -m mymod listto list the names of all functions with doctests - Use the command
xdoctest -m mymod allto run all functions with doctests - Use the command
xdoctest -m mymod func1to run only func1's doctest - Use the command
xdoctest -m mymod func2to run only func2's doctest
Passing --help to either way of invoking the native runner will result in
something similar to the following that outlines what other options are
available:
.. code::
usage: xdoctest [-h] [--version] [-m MODNAME] [-c COMMAND] [--style {auto,google,freeform}] [--analysis {auto,static,dynamic}] [--durations DURATIONS] [--time]
[--colored COLORED] [--nocolor] [--offset] [--report {none,cdiff,ndiff,udiff,only_first_failure}] [--options OPTIONS] [--global-exec GLOBAL_EXEC]
[--verbose VERBOSE] [--quiet] [--silent]
[arg ...]
Xdoctest 1.0.1 - on Python - 3.9.9 (main, Jun 10 2022, 17:45:11)
[GCC 11.2.0] - discover and run doctests within a python package
positional arguments:
arg Ignored if optional arguments are specified, otherwise: Defaults --modname to arg.pop(0). Defaults --command to arg.pop(0). (default: None)
optional arguments:
-h, --help show this help message and exit
--version Display version info and quit (default: False)
-m MODNAME, --modname MODNAME
Module name or path. If specified positional modules are ignored (default: None)
-c COMMAND, --command COMMAND
A doctest name or a command (list|all|<callname>). Defaults to a
Related Skills
node-connect
347.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
claude-opus-4-5-migration
107.8kMigrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5
frontend-design
107.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
model-usage
347.0kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.
