Debugging Python Code
When I started learning Python, my super-scientific investigation method for debugging scripts was to insert
print(my_variable) into my code (and occasionally printing
my_variable.__dict__ where I expected that method to be implemented), run tests locally, and check out what got printed.
Soon, a more experienced colleague of mine introduced me to the
pdb debugger in Python, and it was definitely a game changer. The fact that you can inspect your code interactively, line by line, can be extremely useful, and I regularly use it while writing tests for my code.
Note: PyCharm and other IDEs can provide advanced, user-friendly interfaces for debugging. However, I don’t think that what they can offer is substantially different from the built-in debugging options. I use
pdb++which is an extension of the built-in
pdbpackage - it adds syntax highlighting, auto-complete, and some additional commands, but otherwise it’s the same as
pdb. You can install it with:
$ pip install pdbpp
At the problematic code part where you suspect a bug, you can just type
import pdb; pdb.set_trace() (or the built-in
breakpoint() if you’re above
3.7), save the file and run some tests. Execution will stop at that line (called the breakpoint), and you will be dropped into an interactive
The following commands can come in handy during that session.
l- list => lists the source code surrounding your breakpoint.
pdbpp-specific command, and it’s extremely useful. It makes your context “sticky”, meaning that as you navigate through the code, the source code will “stick” around your breakpoint - it’s like if
llwas executed every time you take a step in the code.
c- continue => continue until the next
breakpoint()call. Pretty straightforward!
s- step => execute the current line, and if it’s a function call, step into the function definition. This can come very handy if you aren’t sure where your bug is occurring.
n- next => execute the current line, without stepping into the called function.
- until => continue execution until a line number great that the current line is reached. This is useful if you encounter a loop (
while) when debugging some code, eg. if the loop has 100 iterations, you would have to press
nquite a few times to reach the loop’s end. You can use
uas a shortcut to let the loop finish, and it will stop ant the first line after the loop.
and so on - however, with
u, you can just skip the loop:
until can receive an argument, in which case execution will continue until reaching that line.
display is a pretty neat command if you are wanting to track down where a variable is mutated. You can give it an expression, and it displays the value of the expression if it changed, each time execution stops. In this example, we are tracking the value of the variable called
number. The debugger shows what it changed to, and what was the previous value - pretty neat!
number: <undefined> --> 0 and
number: 0 --> 76 parts in the
pdb console, which tell you the sequences of events:
[expression]=> pretty-prints the value of a variable. Useful in combination with
__dict__to make the output more human-readable (
[expression]=> tries to get the source code of
[expression]- neat if you don’t want to leave your terminal and quickly want to retrieve a function/class definition.
There are a lot of other useful commands provided by
pdb - but these are the ones that I’m using most often. Checkout out the additional sources for more details.