Wilma is a Python debugging tool intended to solve some of the issues that come with the traditional practice of print-statement debugging (also known as Caveman Debugging).
"The most effective debugging tool is still careful thought, coupled with judiciously placed print statements."
— Brian Kernighan, "Unix for Beginners" (1979)
One problem with this approach is that it requires changing the source code to add new print statements. Most of them would probably need to be removed before the code goes to production, and there is always a chance that some might be go unnoticed and forgotten. Furthermore, it is not easy to toggle them on or off while debugging, as they tend to be scattered across multiple sources.
Wilma lets you define print statements in a separate configuration file, which are then injected into the bytecode at runtime. This means that there is no longer the need to make changes to source files (not only that, but one can easily add print statement to third-party libraries too!). Consequently, there is no risk of forgetting print statements in sources, and switching them off is as easy as commenting them out in just a single place!
Suppose that we have a function that takes a single argument, e.g.
# File: test.py
def foo(secret):
print("I'm not telling you the secret!")
return None
foo("Wilma rox!")
There is no way of knowing what was passed to the function when it is called by just looking at its output:
$ python -m test
I'm not telling you the secret!
So we can use Wilma to inject a print statement at the beginning of line 4 that
prints the value of the secret
argument:
# File: wilma.toml
[probes]
"test.py:4" = "print('The secret is: ', secret)"
If we now run the same script through Wilma, this time we get:
$ wilma python -m test
I'm not telling you the secret!
The secret is: Wilma rox!
By default, Wilma looks for the file wilma.toml
in the current working
directory. You can specify a custom Wilma file with the -c/--config
option.
NOTE Wilma should be installed within the same environment of the target application to work properly. For examole, you may want to list Wilma amongst the developent dependencies of your project.
Wilma comes with a set of useful tools to quickly perform debug operations. For
example, to change the value of the secret
local variable, you can use the
following configuration
# File: wilma.toml
[probes]
"test.py:3" = """
with wilma.locals() as ls:
ls["secret"] = "new secret"
"""
"test.py:4" = "print('The secret is: ', secret)"
When you run the test
module you should now see new secret
instead of the
original value.
The
wilma
module is imported automatically, so there is no need to add it explicitly to the imports.
You can also inject extra dependencies that you perhaps would include in your
project only for debugging purposes. For example, if you want to use the rich
library to pretty-print, you can add it to the dependencies
section of the
configuration file:
imports = [ "rich as r" ]
[dependencies]
rich = "latest"
[probes]
"test.py:3" = "r.print(f'secret=\"{secret}\"')"
In this example, we import rich
and give it the alias r
. We then add
rich
to the dependencies that we want Wilma to install (assuming that
rich
is not already available from the target environment). In this case we
request the latest version, but the string after the =
sign can be any valid
version specifier, e.g. ~=10.4.0
.