Reputation: 26717
For some quick Python debugging I'll occasionally throw in a import pdb;pdb.set_trace()
line that will drop me into the debugger. Very handy. However, if I want to debug a loop, that may run many, many, many times, it loses its effectiveness somewhat. I could mash on c
ontinue many, many, many times, but is there a way to remove/ignore that hard-coded breakpoint so I can let it finish?
I could set a global flag and run it conditionally, but then I'd lose the 'standalone-ness' of the one-line breakpoint, also requiring another flag for each pdb.set_trace()
.
Upvotes: 6
Views: 3719
Reputation: 496
Using 'return' in pdb can pass pdb.set_trace() in the loop and jump to the last line of the current function.
Upvotes: 0
Reputation: 1
Have you tried using the "until" command that's provided by pdb?
From http://docs.python.org/2/library/pdb.html#debugger-commands:
unt(il)
Continue execution until the line with the line number greater than the current one is reached or when returning from current frame.
Upvotes: 0
Reputation: 64318
The following hack will disable all other calls to set_trace
in current run (i.e., from anywhere in your code).
Create this noop_pdb
drop-in:
# noop_pdb.py
def set_trace(*args, **kwargs):
pass
Then, once your code breaks in the real pdb.set_trace
, and you want to disable the rest of the calls to set_trace
, do:
sys.modules['pdb'] = __import__('noop_pdb')
Next time the interpreter encounters a line like:
import pdb;pdb.set_trace()
It avoids re-importing the built-in pdb
, picking up the drop-in.
EDIT: another way to implement this hack, which does not require noop_pdb
, is by replacing set_trace
with noop, instead of the whole pdb
module: pdb.set_trace = lambda: None
Upvotes: 1
Reputation: 64318
My other answer is sort of a quick hack, and is not perfect (will disable all calls to set_trace
). Here's a better solution.
We define a module wrapping pdb
. In it, set_trace
is a callable object, maintaining a list of disabled callers (identified by filename/line-number).
# mypdb.py
import inspect
import sys
try:
import ipdb as PDB
except ImportError:
import pdb as PDB
class SetTraceWrapper(object):
def __init__(self):
self.callers_disabled = set()
self.cur_caller = None
def __call__(self):
self.cur_caller = self.get_caller_id()
if self.cur_caller in self.callers_disabled:
# already disabled for caller
#print 'set_trace SKIPPED for %s' % ( self.cur_caller, )
return
#print 'set_trace BREAKING for %s' % ( self.cur_caller, )
try:
PDB.set_trace(sys._getframe().f_back)
except TypeError:
PDB.set_trace()
def disable_current(self):
#print 'set_trace DISABLING %s' % ( self.cur_caller, )
self.callers_disabled.add(self.cur_caller)
def get_caller_id(self, levels_up = 1):
f = inspect.stack()[levels_up + 1]
return ( f[1], f[2] ) # filename and line number
set_trace = SetTraceWrapper()
In your code, make sure you use the wrapper:
import mypdb as pdb; pdb.set_trace()
When you want to disable the current set_trace
-calling-line, do:
pdb.set_trace.disable_current()
Notes:
I personally prefer ipdb
over pdb
When using pdb
, since the function actually calling pdb.set_trace
is in the wrapper, the current frame when breaking will be in it. the up
command gets you the to frame you want. This will not happen if using ipdb
(the implementation in the wrapper makes sure to break in the right place).
When using ipdb
, I find it to report caller-frame line-numbers inconsistently. This means the first time you do pdb.set_trace.disable_current()
, it might not hold. Just do it again if it breaks the next time -- the second time holds.
In general, having your own pdb
wrapper is useful for other things as well. I have my own set_trace
wrapper which avoids breaking if not sys.stdout.isatty
(you never want to break if the process is not connected to a terminal, nor when you redirect the stdout to a file/pipe). That is to say, having your own pdb
wrapper and calling its set_trace
instead of pdb
's is a good practice.
Upvotes: 2