How do you watch a variable in pdb How do you watch a variable in pdb python python

How do you watch a variable in pdb


data breakpoints with pdb

...much like you can watch a memory address in gdb...

  • GDB uses data breakpoints, this is made easy with hardware support (hardware watchpoints), this typically involves marking the memory pages read-only which then trips an exception handler on memory access. When hardware watchpoints are not available it uses software watchpoints, these are only useful in single threads and are much slower.
  • PDB does not support data breakpoints, so the short answer is NO, you cannot do it with PDB out of the box.

printing variables when hitting breakpoints in pdb

For watching a variable when you are hitting a breakpoint, you can use the commands command. E.g. printing some_variable when hitting breakpoint #1 (canonical example from pdb doc).

(Pdb) commands 1(com) print(some_variable)(com) end(Pdb)

Additionally, you can use the condition command to ensure the breakpoint is only hit whenever the variable takes a certain value.

eg:

(Pdb) condition 1 some_variable==some_value

other solutions

You can use tracing / profiling functions to examine things step by step using sys.settrace and checking out the opcodes being executed.

https://docs.python.org/3/library/sys.html#sys.settrace

Here is some code to get you started:

import sysimport disdef tracefn(frame, event, arg):    if event == 'call':        print("## CALL", frame)        frame.f_trace_opcodes = True    elif event == 'opcode':        opcode = frame.f_code.co_code[frame.f_lasti]        opname = dis.opname[opcode]        print("## OPCODE", opname)    return tracefnwatchme = 123def foo():    global watchme    watchme = 122sys.settrace(tracefn)foo()

You will probably need to spy on all the STORE_* opcodes.https://docs.python.org/3/library/dis.html


For Python 3:

you can use display functionality of pdb

Once you hit the breakpoint just type

ipdb> display expression

example:

ipdb> display instancedisplay instance: <AppUser: dmitry4>ipdb> display instance.iddisplay instance.id: 9ipdb> display instance.universitydisplay instance.university: <University: @domain.com>ipdb> displayCurrently displaying:instance.university: <University: @domain.com>instance.id: 9instance: <AppUser: dmitry4>ipdb> 

as you can see, each time you type display - it will print all of your watches (expressions). You can use builtin function undisplay to remove certain watch.

You can also use pp expression to prettyprint the expression (very useful)


Here is a really hacky way to do this with pdb. These commands can be put in your ~/.pdbrc for automatic loading every time you use pdb.

!global __currentframe, __stack; from inspect import currentframe as __currentframe, stack as __stack!global __copy; from copy import copy as __copy!global __Pdb; from pdb import Pdb as __Pdb!global __pdb; __pdb = [__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self") for __framerec in __stack() if (__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self")).__class__ == __Pdb][-1]alias _setup_watchpoint !global __key, __dict, __val; __key = '%1'; __dict = __currentframe().f_locals if __currentframe().f_locals.has_key(__key) else __currentframe().f_globals; __val = __copy(%1)alias _nextwatch_internal next;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_nextwatch_internal %1")alias _stepwatch_internal step;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_stepwatch_internal %1")alias nextwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_nextwatch_internal"])alias stepwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_stepwatch_internal"])

This adds two commands, nextwatch and stepwatch which each take a variable name varname as an argument. They will make a shallow copy of the current frame's local variable for varname if possible, and keep executing next or step respectively until what that name points to changes.

This works in CPython 2.7.2 but relies on some pdb internals so it will probably break elsewhere.