Ned Batchelder's updated .pdbrc
The great
Ned Batchelder's .pdbrc
has a bunch of helpful debugging aliases. Ned's post, from
2007, uses Python 2 syntax which I've updated and tweaked slightly below
to use Python 3 syntax and the more modern f-strings for printing. F-strings
were introduced in Python 3.6 so this won't work in earlier versions. If using
Python <= 3.5, you can simply update Ned's original alias p_
line to
place parantheses around the print statement.
# Ned's .pdbrc (updated)
# Print a dictionary, sorted. %1 is the dict, %2 is the prefix for the names.
alias p_ for k in sorted(%1.keys()): print(f"%2{k.ljust(max(len(s) for s in %1.keys()))} = {%1[k]}")
# Print the member variables of a thing.
alias pi p_ %1.__dict__ %1.
# Print the member variables of self.
alias ps pi self
# Print the locals.
alias pl p_ locals() local:
# Next and list, and step and list.
alias nl n;;l
alias sl s;;l
I'll walk through how the aliases work at the end, but first
check out a few examples. Place the above at ~/.pdbrc
and try them out.
Print local variables with pl¶
def add(a, b):
breakpoint()
return a + b
result = add(1, 2)
Print the attributes of an object with pi¶
from random import uniform
from types import SimpleNamespace
def random_lat_long():
lat_ = uniform(-90, 90)
long_ = uniform(-180, 180)
lat_long = SimpleNamespace(latitude=lat_, longitude=long_)
breakpoint()
return lat_long
lat_long = random_lat_long()
Print the attributes of self with ps¶
class AwesomeNumbers:
def __init__(self):
self.pi = 3.14
self.eulers_number = 2.718
breakpoint()
awesome_numbers = AwesomeNumbers()
How it works¶
Most of the heavy lifting is done in the alias p_
line. Here's a function that
does the same thing, but broken up for readability.
def p_(dict_, prefix):
# find the length of the longest key in the dictionary
max_key_len = max(len(key) for key in dict_.keys())
for key in sorted(dict_.keys()):
value = dict_[key]
# adjust width of the key's string to the length of
# the longest key
key_repr = key.ljust(max_key_len)
value = dict_[key]
print(f"{prefix}{key_repr} = {value}")
awesome_numbers = {"pi": 3.14, "eulers_number": 2.718}
p_(dict_=awesome_numbers, prefix="awesome_numbers.")
To build on this, the pl
alias calls p_
with the handy locals()
built-in,
which stores a dictionary containing variables on the stack.
The pi
and ps
command build on p_
by passing an object's __dict__
attribute which stores the object's named attributes. For example
awesome_numbers = SimpleNamespace(pi=3.14, eulers_number=2.718)
p_(awesome_numbers.__dict__, "awesome_numbers.")
Changes from Ned's pdbrc¶
The only change that I've made is from this line in Ned's .pdbrc
alias p_ for k in sorted(%1.keys()): print "%s%-15s= %-80.80s" % ("%2",k,repr(%1[k]))
This line uses Python 2's print
syntax, but it also uses the
older %
formatting to fix the width of the key's name to
a constant 15-characters. A modest improvement is to instead fix
the width of the key names to the length of the longest key, making
it slightly more readable if all of the key names are very
short or if there any keys longer than 15 characters. This comes at
the expense of the readability of our .pdbrc
which now has an almost
incomprehensible alias p_
line.
alias p_ for k in sorted(%1.keys()): print(f"%2{k.ljust(max(len(s) for s in %1.keys()))} = {%1[k]}")
I'd encourage you to try out aliases for tasks that you find yourself doing repeatedly while debugging. I find myself looking for the pid of my Python process often enough that I also use
alias pid import os; os.getpid()
The inspect module could probably form the basis for a bunch of interesting aliases.