How to print all variables values when debugging Python with pdb, without specifying each variable?

renatov picture renatov · Feb 22, 2014 · Viewed 76.6k times · Source

I'm debugging my Python scripts using pdb and the manual says I can use p variables command to print the values of the specified variables at a certain point. But what if I had lots of variables, like 20 variables, and I would like to track the value of all of them? How do I print all of them without specifying each one manually? Take for example this script:

a = 1
b = 2
c = 3

I can debug it with pdb and print all of them using p a, b, c like this:

$ python -m pdb test.py 
> /media/test.py(1)<module>()
-> a = 1
(Pdb) n
> /media/test.py(2)<module>()
-> b = 2
(Pdb) n
> /media/test.py(3)<module>()
(Pdb) n
--Return--
> /media/test.py(3)<module>()->None
-> c = 3
(Pdb) p a, b, c
(1, 2, 3)
(Pdb) 

But I have to manually specify each variable. Is there a way of print all variables at once, without specifying each one of them?

Answer

munk picture munk · Feb 23, 2014

pdb is a fully featured python shell, so you can execute arbitrary commands.

locals() and globals() will display all the variables in scope with their values.

You can use dir() if you're not interested in the values.

When you declare a variable in Python, it's put into locals or globals as appropriate, and there's no way to distinguish a variable you defined and something that's in your scope for another reason.

When you use dir(), it's likely that the variables you're interested in are at the beginning or end of that list. If you want to get the key, value pairs

Filtering locals() might look something like:

>>> x = 10
>>> y = 20
>>> {k: v for k,v in locals().iteritems() if '__' not in k and 'pdb' not in k}
{'y': 20, 'x': 10}

If your locals() is a real mess, you'll need something a little more heavy handed. You can put the following function in a module on your pythonpath and import it during your debugging session.

def debug_nice(locals_dict, keys=[]):
    globals()['types'] = `__import__`('types')
    exclude_keys = ['copyright', 'credits', 'False', 
                    'True', 'None', 'Ellipsis', 'quit']
    exclude_valuetypes = [types.BuiltinFunctionType,
                          types.BuiltinMethodType,
                          types.ModuleType,
                          types.TypeType,
                          types.FunctionType]
    return {k: v for k,v in locals_dict.iteritems() if not
               (k in keys or
                k in exclude_keys or
                type(v) in exclude_valuetypes) and
               k[0] != '_'}

I've added an example session on pastebin

There are a couple of cases this misses. And you might want to extend it to allow you to pass in types too. But it should let you filter most everything but the variables you defined.

dir()

If you just want the last 20 values so you get output like >>> p var1 var2 ... varn would give you, then you're better off slicing dir() like dir()[-20:], but you won't easily see the relationship between the variables and values. eg: "Did I declare foo before or after bar?"

If you want to see that relationship, you can try something like this, which assumes that your variables are at the end of dir(). You can slice differently if they're at the beginning. This won't work well if your variables aren't contiguous.

>>> zip(dir(), [eval(var) for var in dir()])[-4:]
[('a', 10), ('var', 'var'), ('x', 30), ('y', 50)]