This repo contains Emacs package pydor, which is short for “Python doctest runner”. This package lets you execute the Python doctests in the docstring at point. The following animated GIF shows how it works in Spacemacs:
I use Emacs configuration framework Spacemacs - its develop branch to be precise. That framework configures several key bindings to run Python unit tests, such as “run test at point” and “run tests in current module”. However, it lacks functions to run doctests. This package provides a function to run the doctests in the docstring at point.
pydor is implemented in Emacs Lisp and Python. It uses
- Python to find & run all doctests in a docstring in a given Python module and at a given line, and
- Emacs Lisp to call that Python code for the docstring at point.
pydor has been developed using Emacs 28.1, Python 3.11.3 on Manjaro Linux 23.0.0. Probably it will work with older (or newer) Emacs and Python versions, but that hasn’t been tested explicitly.
As mentioned, I use Spacemacs so I provide the installation instructions for that context. It should be relatively straightforward to adapt these instructions for “plain Emacs”.
Spacemacs will automatically retrieve the package from its GitHub repo if you add the following entry to the list of “additional packages” in ~~/.spacemacs~:
dotspacemacs-additional-packages
`(
(pydor :location (recipe :fetcher github :repo "swinkels/pydor" :files ("pydor.el" "execute_doctests.py"))
)
At this point the package will be available to Spacemacs but will not yet be loaded. The following use-package expression for dotspacemacs/user-config in ~~/.spacemacs~ takes care of that:
(use-package pydor
:commands (pydor-execute-doctest)
:init
(spacemacs/set-leader-keys-for-major-mode 'python-mode "td" 'pydor-execute-doctest))
This expression establishes two things:
- pydor will be loaded when you call pydor-execute-doctest for the first time;
- function
pydor-execute-doctest
is bound to, t d
in Python mode.
pydor imports the module that contains the docstring at point. For this to
succeed, the module should be in a directory of the Python sys.path
or it
should be part of a package in such a directory. If that is not the case, the
import will fail and pydor will abort. This is where variable
pydor-pythonpath-directories
comes in: before pydor tries to import the
module, it extends sys.path
with the directories in this list.
To give an example, I often have several Python scripts in the root of my repo
to try out things in the Python REPL. In general, that directory is not a
directory of sys.path
and pydor won’t be able to import these scripts. To help
pydor, I set pydor-pythonpath-directories
as a directory-local variable in
file .dir-locals.el
in the root of the repo:
;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")
((python-mode . ((pydor-pythonpath-directories . ("/home/swinkels/repo/oss/pydor")))))
Note that the paths on pydor-pythonpath-directories
should be absolute.
Finally, pydor tries to import the module with its full module name, so
package.subpackage.module
. To determine that name, it assumes that each
directory in the Python package has an __init__.py
. This means that pydor will
only work with regular Python packages and not with namespace packages, which
don’t have that file.
The Emacs part of pydor is developed using Emacs Eask, a “CLI for building, running, testing, and managing your Emacs Lisp dependencies”. For information about the installation of Eask I refer to its documentation. In this README I only show how to use Eask for the development of pydor.
In the directory of your clone of pydor, execute the following command to download its Emacs Lisp development dependencies and run the Emacs Lisp unit tests:
$> eask test ert-runner -L . -L test
If you want to add unit tests to pydor, place them in subdirectory test/
.
There is a Makefile in the root of the repo that offers several targets as
shortcuts to these Eask commands. The Eask command above is accessible as target
unit-test
of the Makefile. This is the default target, so just calling make
runs the unit tests.
If you develop pydor, you will want to work with a local Git checkout instead of
the installed version. One way to do so is by pointing the pydor entry in
dotspacemacs-additional-packages
to your local checkout:
dotspacemacs-additional-packages
`(
(pydor :location local)
)
Whith this entry, Spacemacs expects the Git checkout to be present as ~~/.emacs.d/private/local/pydor~. That means you either checkout pydor in that directory, or use a link to the right directory.
If you change pydor.el
and want to test these changes, you have to evaluate
that file again. To do so, either load that file in Emacs and execute command
eval-buffer
, or restart Emacs.
There is another approach if you want to use a local Git checkout for the only
for your current Emacs session: you evaluate the checked out pydor.el
before
your first call to main function pydor-execute-doctest
[fn:1]. Don’t forget to
re-evaluate pydor.el
when you want to test your changes.
[fn:1] If you evaluate pydor.el
after Emacs has evaluated the installed
version, Emacs will still use the installed Python script instead of the one in
your checkout.