Change the sense of a test in how the profiler interprets exception events.
[python/dscho.git] / Lib / bdb.py
blob7326a72d5532fe5ff9237ce1995feea8010a2f95
1 """Debugger basics"""
3 import sys
4 import os
5 import types
7 __all__ = ["BdbQuit","Bdb","Breakpoint"]
9 BdbQuit = 'bdb.BdbQuit' # Exception to give up completely
12 class Bdb:
14 """Generic Python debugger base class.
16 This class takes care of details of the trace facility;
17 a derived class should implement user interaction.
18 The standard debugger class (pdb.Pdb) is an example.
19 """
21 def __init__(self):
22 self.breaks = {}
23 self.fncache = {}
25 def canonic(self, filename):
26 canonic = self.fncache.get(filename)
27 if not canonic:
28 canonic = os.path.abspath(filename)
29 self.fncache[filename] = canonic
30 return canonic
32 def reset(self):
33 import linecache
34 linecache.checkcache()
35 self.botframe = None
36 self.stopframe = None
37 self.returnframe = None
38 self.quitting = 0
40 def trace_dispatch(self, frame, event, arg):
41 if self.quitting:
42 return # None
43 if event == 'line':
44 return self.dispatch_line(frame)
45 if event == 'call':
46 return self.dispatch_call(frame, arg)
47 if event == 'return':
48 return self.dispatch_return(frame, arg)
49 if event == 'exception':
50 return self.dispatch_exception(frame, arg)
51 print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
52 return self.trace_dispatch
54 def dispatch_line(self, frame):
55 if self.stop_here(frame) or self.break_here(frame):
56 self.user_line(frame)
57 if self.quitting: raise BdbQuit
58 return self.trace_dispatch
60 def dispatch_call(self, frame, arg):
61 # XXX 'arg' is no longer used
62 if self.botframe is None:
63 # First call of dispatch since reset()
64 self.botframe = frame
65 return self.trace_dispatch
66 if not (self.stop_here(frame) or self.break_anywhere(frame)):
67 # No need to trace this function
68 return # None
69 self.user_call(frame, arg)
70 if self.quitting: raise BdbQuit
71 return self.trace_dispatch
73 def dispatch_return(self, frame, arg):
74 if self.stop_here(frame) or frame == self.returnframe:
75 self.user_return(frame, arg)
76 if self.quitting: raise BdbQuit
77 return self.trace_dispatch
79 def dispatch_exception(self, frame, arg):
80 if self.stop_here(frame):
81 self.user_exception(frame, arg)
82 if self.quitting: raise BdbQuit
83 return self.trace_dispatch
85 # Normally derived classes don't override the following
86 # methods, but they may if they want to redefine the
87 # definition of stopping and breakpoints.
89 def stop_here(self, frame):
90 if self.stopframe is None:
91 return 1
92 if frame is self.stopframe:
93 return 1
94 while frame is not None and frame is not self.stopframe:
95 if frame is self.botframe:
96 return 1
97 frame = frame.f_back
98 return 0
100 def break_here(self, frame):
101 filename = self.canonic(frame.f_code.co_filename)
102 if not self.breaks.has_key(filename):
103 return 0
104 lineno = frame.f_lineno
105 if not lineno in self.breaks[filename]:
106 return 0
107 # flag says ok to delete temp. bp
108 (bp, flag) = effective(filename, lineno, frame)
109 if bp:
110 self.currentbp = bp.number
111 if (flag and bp.temporary):
112 self.do_clear(str(bp.number))
113 return 1
114 else:
115 return 0
117 def do_clear(self, arg):
118 raise NotImplementedError, "subclass of bdb must implement do_clear()"
120 def break_anywhere(self, frame):
121 return self.breaks.has_key(
122 self.canonic(frame.f_code.co_filename))
124 # Derived classes should override the user_* methods
125 # to gain control.
127 def user_call(self, frame, argument_list):
128 """This method is called when there is the remote possibility
129 that we ever need to stop in this function."""
130 pass
132 def user_line(self, frame):
133 """This method is called when we stop or break at this line."""
134 pass
136 def user_return(self, frame, return_value):
137 """This method is called when a return trap is set here."""
138 pass
140 def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
141 """This method is called if an exception occurs,
142 but only if we are to stop at or just below this level."""
143 pass
145 # Derived classes and clients can call the following methods
146 # to affect the stepping state.
148 def set_step(self):
149 """Stop after one line of code."""
150 self.stopframe = None
151 self.returnframe = None
152 self.quitting = 0
154 def set_next(self, frame):
155 """Stop on the next line in or below the given frame."""
156 self.stopframe = frame
157 self.returnframe = None
158 self.quitting = 0
160 def set_return(self, frame):
161 """Stop when returning from the given frame."""
162 self.stopframe = frame.f_back
163 self.returnframe = frame
164 self.quitting = 0
166 def set_trace(self):
167 """Start debugging from here."""
168 try:
169 1 + ''
170 except:
171 frame = sys.exc_info()[2].tb_frame.f_back
172 self.reset()
173 while frame:
174 frame.f_trace = self.trace_dispatch
175 self.botframe = frame
176 frame = frame.f_back
177 self.set_step()
178 sys.settrace(self.trace_dispatch)
180 def set_continue(self):
181 # Don't stop except at breakpoints or when finished
182 self.stopframe = self.botframe
183 self.returnframe = None
184 self.quitting = 0
185 if not self.breaks:
186 # no breakpoints; run without debugger overhead
187 sys.settrace(None)
188 try:
189 1 + '' # raise an exception
190 except:
191 frame = sys.exc_info()[2].tb_frame.f_back
192 while frame and frame is not self.botframe:
193 del frame.f_trace
194 frame = frame.f_back
196 def set_quit(self):
197 self.stopframe = self.botframe
198 self.returnframe = None
199 self.quitting = 1
200 sys.settrace(None)
202 # Derived classes and clients can call the following methods
203 # to manipulate breakpoints. These methods return an
204 # error message is something went wrong, None if all is well.
205 # Set_break prints out the breakpoint line and file:lineno.
206 # Call self.get_*break*() to see the breakpoints or better
207 # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
209 def set_break(self, filename, lineno, temporary=0, cond = None):
210 filename = self.canonic(filename)
211 import linecache # Import as late as possible
212 line = linecache.getline(filename, lineno)
213 if not line:
214 return 'Line %s:%d does not exist' % (filename,
215 lineno)
216 if not self.breaks.has_key(filename):
217 self.breaks[filename] = []
218 list = self.breaks[filename]
219 if not lineno in list:
220 list.append(lineno)
221 bp = Breakpoint(filename, lineno, temporary, cond)
223 def clear_break(self, filename, lineno):
224 filename = self.canonic(filename)
225 if not self.breaks.has_key(filename):
226 return 'There are no breakpoints in %s' % filename
227 if lineno not in self.breaks[filename]:
228 return 'There is no breakpoint at %s:%d' % (filename,
229 lineno)
230 # If there's only one bp in the list for that file,line
231 # pair, then remove the breaks entry
232 for bp in Breakpoint.bplist[filename, lineno][:]:
233 bp.deleteMe()
234 if not Breakpoint.bplist.has_key((filename, lineno)):
235 self.breaks[filename].remove(lineno)
236 if not self.breaks[filename]:
237 del self.breaks[filename]
239 def clear_bpbynumber(self, arg):
240 try:
241 number = int(arg)
242 except:
243 return 'Non-numeric breakpoint number (%s)' % arg
244 try:
245 bp = Breakpoint.bpbynumber[number]
246 except IndexError:
247 return 'Breakpoint number (%d) out of range' % number
248 if not bp:
249 return 'Breakpoint (%d) already deleted' % number
250 self.clear_break(bp.file, bp.line)
252 def clear_all_file_breaks(self, filename):
253 filename = self.canonic(filename)
254 if not self.breaks.has_key(filename):
255 return 'There are no breakpoints in %s' % filename
256 for line in self.breaks[filename]:
257 blist = Breakpoint.bplist[filename, line]
258 for bp in blist:
259 bp.deleteMe()
260 del self.breaks[filename]
262 def clear_all_breaks(self):
263 if not self.breaks:
264 return 'There are no breakpoints'
265 for bp in Breakpoint.bpbynumber:
266 if bp:
267 bp.deleteMe()
268 self.breaks = {}
270 def get_break(self, filename, lineno):
271 filename = self.canonic(filename)
272 return self.breaks.has_key(filename) and \
273 lineno in self.breaks[filename]
275 def get_breaks(self, filename, lineno):
276 filename = self.canonic(filename)
277 return self.breaks.has_key(filename) and \
278 lineno in self.breaks[filename] and \
279 Breakpoint.bplist[filename, lineno] or []
281 def get_file_breaks(self, filename):
282 filename = self.canonic(filename)
283 if self.breaks.has_key(filename):
284 return self.breaks[filename]
285 else:
286 return []
288 def get_all_breaks(self):
289 return self.breaks
291 # Derived classes and clients can call the following method
292 # to get a data structure representing a stack trace.
294 def get_stack(self, f, t):
295 stack = []
296 if t and t.tb_frame is f:
297 t = t.tb_next
298 while f is not None:
299 stack.append((f, f.f_lineno))
300 if f is self.botframe:
301 break
302 f = f.f_back
303 stack.reverse()
304 i = max(0, len(stack) - 1)
305 while t is not None:
306 stack.append((t.tb_frame, t.tb_lineno))
307 t = t.tb_next
308 return stack, i
312 def format_stack_entry(self, frame_lineno, lprefix=': '):
313 import linecache, repr
314 frame, lineno = frame_lineno
315 filename = self.canonic(frame.f_code.co_filename)
316 s = filename + '(' + `lineno` + ')'
317 if frame.f_code.co_name:
318 s = s + frame.f_code.co_name
319 else:
320 s = s + "<lambda>"
321 if frame.f_locals.has_key('__args__'):
322 args = frame.f_locals['__args__']
323 else:
324 args = None
325 if args:
326 s = s + repr.repr(args)
327 else:
328 s = s + '()'
329 if frame.f_locals.has_key('__return__'):
330 rv = frame.f_locals['__return__']
331 s = s + '->'
332 s = s + repr.repr(rv)
333 line = linecache.getline(filename, lineno)
334 if line: s = s + lprefix + line.strip()
335 return s
337 # The following two methods can be called by clients to use
338 # a debugger to debug a statement, given as a string.
340 def run(self, cmd, globals=None, locals=None):
341 if globals is None:
342 import __main__
343 globals = __main__.__dict__
344 if locals is None:
345 locals = globals
346 self.reset()
347 sys.settrace(self.trace_dispatch)
348 if not isinstance(cmd, types.CodeType):
349 cmd = cmd+'\n'
350 try:
351 try:
352 exec cmd in globals, locals
353 except BdbQuit:
354 pass
355 finally:
356 self.quitting = 1
357 sys.settrace(None)
359 def runeval(self, expr, globals=None, locals=None):
360 if globals is None:
361 import __main__
362 globals = __main__.__dict__
363 if locals is None:
364 locals = globals
365 self.reset()
366 sys.settrace(self.trace_dispatch)
367 if not isinstance(expr, types.CodeType):
368 expr = expr+'\n'
369 try:
370 try:
371 return eval(expr, globals, locals)
372 except BdbQuit:
373 pass
374 finally:
375 self.quitting = 1
376 sys.settrace(None)
378 def runctx(self, cmd, globals, locals):
379 # B/W compatibility
380 self.run(cmd, globals, locals)
382 # This method is more useful to debug a single function call.
384 def runcall(self, func, *args):
385 self.reset()
386 sys.settrace(self.trace_dispatch)
387 res = None
388 try:
389 try:
390 res = apply(func, args)
391 except BdbQuit:
392 pass
393 finally:
394 self.quitting = 1
395 sys.settrace(None)
396 return res
399 def set_trace():
400 Bdb().set_trace()
403 class Breakpoint:
405 """Breakpoint class
407 Implements temporary breakpoints, ignore counts, disabling and
408 (re)-enabling, and conditionals.
410 Breakpoints are indexed by number through bpbynumber and by
411 the file,line tuple using bplist. The former points to a
412 single instance of class Breakpoint. The latter points to a
413 list of such instances since there may be more than one
414 breakpoint per line.
418 # XXX Keeping state in the class is a mistake -- this means
419 # you cannot have more than one active Bdb instance.
421 next = 1 # Next bp to be assigned
422 bplist = {} # indexed by (file, lineno) tuple
423 bpbynumber = [None] # Each entry is None or an instance of Bpt
424 # index 0 is unused, except for marking an
425 # effective break .... see effective()
427 def __init__(self, file, line, temporary=0, cond = None):
428 self.file = file # This better be in canonical form!
429 self.line = line
430 self.temporary = temporary
431 self.cond = cond
432 self.enabled = 1
433 self.ignore = 0
434 self.hits = 0
435 self.number = Breakpoint.next
436 Breakpoint.next = Breakpoint.next + 1
437 # Build the two lists
438 self.bpbynumber.append(self)
439 if self.bplist.has_key((file, line)):
440 self.bplist[file, line].append(self)
441 else:
442 self.bplist[file, line] = [self]
445 def deleteMe(self):
446 index = (self.file, self.line)
447 self.bpbynumber[self.number] = None # No longer in list
448 self.bplist[index].remove(self)
449 if not self.bplist[index]:
450 # No more bp for this f:l combo
451 del self.bplist[index]
453 def enable(self):
454 self.enabled = 1
456 def disable(self):
457 self.enabled = 0
459 def bpprint(self):
460 if self.temporary:
461 disp = 'del '
462 else:
463 disp = 'keep '
464 if self.enabled:
465 disp = disp + 'yes'
466 else:
467 disp = disp + 'no '
468 print '%-4dbreakpoint %s at %s:%d' % (self.number, disp,
469 self.file, self.line)
470 if self.cond:
471 print '\tstop only if %s' % (self.cond,)
472 if self.ignore:
473 print '\tignore next %d hits' % (self.ignore)
474 if (self.hits):
475 if (self.hits > 1): ss = 's'
476 else: ss = ''
477 print ('\tbreakpoint already hit %d time%s' %
478 (self.hits, ss))
480 # -----------end of Breakpoint class----------
482 # Determines if there is an effective (active) breakpoint at this
483 # line of code. Returns breakpoint number or 0 if none
484 def effective(file, line, frame):
485 """Determine which breakpoint for this file:line is to be acted upon.
487 Called only if we know there is a bpt at this
488 location. Returns breakpoint that was triggered and a flag
489 that indicates if it is ok to delete a temporary bp.
492 possibles = Breakpoint.bplist[file,line]
493 for i in range(0, len(possibles)):
494 b = possibles[i]
495 if b.enabled == 0:
496 continue
497 # Count every hit when bp is enabled
498 b.hits = b.hits + 1
499 if not b.cond:
500 # If unconditional, and ignoring,
501 # go on to next, else break
502 if b.ignore > 0:
503 b.ignore = b.ignore -1
504 continue
505 else:
506 # breakpoint and marker that's ok
507 # to delete if temporary
508 return (b,1)
509 else:
510 # Conditional bp.
511 # Ignore count applies only to those bpt hits where the
512 # condition evaluates to true.
513 try:
514 val = eval(b.cond, frame.f_globals,
515 frame.f_locals)
516 if val:
517 if b.ignore > 0:
518 b.ignore = b.ignore -1
519 # continue
520 else:
521 return (b,1)
522 # else:
523 # continue
524 except:
525 # if eval fails, most conservative
526 # thing is to stop on breakpoint
527 # regardless of ignore count.
528 # Don't delete temporary,
529 # as another hint to user.
530 return (b,0)
531 return (None, None)
533 # -------------------- testing --------------------
535 class Tdb(Bdb):
536 def user_call(self, frame, args):
537 name = frame.f_code.co_name
538 if not name: name = '???'
539 print '+++ call', name, args
540 def user_line(self, frame):
541 import linecache
542 name = frame.f_code.co_name
543 if not name: name = '???'
544 fn = self.canonic(frame.f_code.co_filename)
545 line = linecache.getline(fn, frame.f_lineno)
546 print '+++', fn, frame.f_lineno, name, ':', line.strip()
547 def user_return(self, frame, retval):
548 print '+++ return', retval
549 def user_exception(self, frame, exc_stuff):
550 print '+++ exception', exc_stuff
551 self.set_continue()
553 def foo(n):
554 print 'foo(', n, ')'
555 x = bar(n*10)
556 print 'bar returned', x
558 def bar(a):
559 print 'bar(', a, ')'
560 return a/2
562 def test():
563 t = Tdb()
564 t.run('import bdb; bdb.foo(10)')
566 # end