6 BdbQuit
= 'bdb.BdbQuit' # Exception to give up completely
11 """Generic Python debugger base class.
13 This class takes care of details of the trace facility;
14 a derived class should implement user interaction.
15 The standard debugger class (pdb.Pdb) is an example.
20 # We want to have a method self.canonic() which
21 # canonicalizes filenames before comparing them
22 # but we want the default to be a very fast no-op.
23 # Solution: the built-in str function.
24 if not hasattr(self
, "canonic"):
29 linecache
.checkcache()
32 self
.returnframe
= None
35 def trace_dispatch(self
, frame
, event
, arg
):
39 return self
.dispatch_line(frame
)
41 return self
.dispatch_call(frame
, arg
)
43 return self
.dispatch_return(frame
, arg
)
44 if event
== 'exception':
45 return self
.dispatch_exception(frame
, arg
)
46 print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
47 return self
.trace_dispatch
49 def dispatch_line(self
, frame
):
50 if self
.stop_here(frame
) or self
.break_here(frame
):
52 if self
.quitting
: raise BdbQuit
53 return self
.trace_dispatch
55 def dispatch_call(self
, frame
, arg
):
56 # XXX 'arg' is no longer used
57 if self
.botframe
is None:
58 # First call of dispatch since reset()
60 return self
.trace_dispatch
61 if not (self
.stop_here(frame
) or self
.break_anywhere(frame
)):
62 # No need to trace this function
64 self
.user_call(frame
, arg
)
65 if self
.quitting
: raise BdbQuit
66 return self
.trace_dispatch
68 def dispatch_return(self
, frame
, arg
):
69 if self
.stop_here(frame
) or frame
== self
.returnframe
:
70 self
.user_return(frame
, arg
)
71 if self
.quitting
: raise BdbQuit
73 def dispatch_exception(self
, frame
, arg
):
74 if self
.stop_here(frame
):
75 self
.user_exception(frame
, arg
)
76 if self
.quitting
: raise BdbQuit
77 return self
.trace_dispatch
79 # Normally derived classes don't override the following
80 # methods, but they may if they want to redefine the
81 # definition of stopping and breakpoints.
83 def stop_here(self
, frame
):
84 if self
.stopframe
is None:
86 if frame
is self
.stopframe
:
88 while frame
is not None and frame
is not self
.stopframe
:
89 if frame
is self
.botframe
:
94 def break_here(self
, frame
):
95 filename
= self
.canonic(frame
.f_code
.co_filename
)
96 if not self
.breaks
.has_key(filename
):
98 lineno
= frame
.f_lineno
99 if not lineno
in self
.breaks
[filename
]:
101 # flag says ok to delete temp. bp
102 (bp
, flag
) = effective(filename
, lineno
, frame
)
104 self
.currentbp
= bp
.number
105 if (flag
and bp
.temporary
):
106 self
.do_clear(str(bp
.number
))
111 def break_anywhere(self
, frame
):
112 return self
.breaks
.has_key(
113 self
.canonic(frame
.f_code
.co_filename
))
115 # Derived classes should override the user_* methods
118 def user_call(self
, frame
, argument_list
):
119 # This method is called when there is the remote possibility
120 # that we ever need to stop in this function
123 def user_line(self
, frame
):
124 # This method is called when we stop or break at this line
127 def user_return(self
, frame
, return_value
):
128 # This method is called when a return trap is set here
131 def user_exception(self
, frame
, (exc_type
, exc_value
, exc_traceback
)):
132 # This method is called if an exception occurs,
133 # but only if we are to stop at or just below this level
136 # Derived classes and clients can call the following methods
137 # to affect the stepping state.
140 # Stop after one line of code
141 self
.stopframe
= None
142 self
.returnframe
= None
145 def set_next(self
, frame
):
146 # Stop on the next line in or below the given frame
147 self
.stopframe
= frame
148 self
.returnframe
= None
151 def set_return(self
, frame
):
152 # Stop when returning from the given frame
153 self
.stopframe
= frame
.f_back
154 self
.returnframe
= frame
158 # Start debugging from here
162 frame
= sys
.exc_info()[2].tb_frame
.f_back
165 frame
.f_trace
= self
.trace_dispatch
166 self
.botframe
= frame
169 sys
.settrace(self
.trace_dispatch
)
171 def set_continue(self
):
172 # Don't stop except at breakpoints or when finished
173 self
.stopframe
= self
.botframe
174 self
.returnframe
= None
177 # no breakpoints; run without debugger overhead
180 1 + '' # raise an exception
182 frame
= sys
.exc_info()[2].tb_frame
.f_back
183 while frame
and frame
is not self
.botframe
:
188 self
.stopframe
= self
.botframe
189 self
.returnframe
= None
193 # Derived classes and clients can call the following methods
194 # to manipulate breakpoints. These methods return an
195 # error message is something went wrong, None if all is well.
196 # Set_break prints out the breakpoint line and file:lineno.
197 # Call self.get_*break*() to see the breakpoints or better
198 # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
200 def set_break(self
, filename
, lineno
, temporary
=0, cond
= None):
201 filename
= self
.canonic(filename
)
202 import linecache
# Import as late as possible
203 line
= linecache
.getline(filename
, lineno
)
205 return 'Line %s:%d does not exist' % (filename
,
207 if not self
.breaks
.has_key(filename
):
208 self
.breaks
[filename
] = []
209 list = self
.breaks
[filename
]
210 if not lineno
in list:
212 bp
= Breakpoint(filename
, lineno
, temporary
, cond
)
214 def clear_break(self
, filename
, lineno
):
215 filename
= self
.canonic(filename
)
216 if not self
.breaks
.has_key(filename
):
217 return 'There are no breakpoints in %s' % filename
218 if lineno
not in self
.breaks
[filename
]:
219 return 'There is no breakpoint at %s:%d' % (filename
,
221 # If there's only one bp in the list for that file,line
222 # pair, then remove the breaks entry
223 for bp
in Breakpoint
.bplist
[filename
, lineno
][:]:
225 if not Breakpoint
.bplist
.has_key((filename
, lineno
)):
226 self
.breaks
[filename
].remove(lineno
)
227 if not self
.breaks
[filename
]:
228 del self
.breaks
[filename
]
230 def clear_bpbynumber(self
, arg
):
234 return 'Non-numeric breakpoint number (%s)' % arg
236 bp
= Breakpoint
.bpbynumber
[number
]
238 return 'Breakpoint number (%d) out of range' % number
240 return 'Breakpoint (%d) already deleted' % number
241 self
.clear_break(bp
.file, bp
.line
)
243 def clear_all_file_breaks(self
, filename
):
244 filename
= self
.canonic(filename
)
245 if not self
.breaks
.has_key(filename
):
246 return 'There are no breakpoints in %s' % filename
247 for line
in self
.breaks
[filename
]:
248 blist
= Breakpoint
.bplist
[filename
, line
]
251 del self
.breaks
[filename
]
253 def clear_all_breaks(self
):
255 return 'There are no breakpoints'
256 for bp
in Breakpoint
.bpbynumber
:
261 def get_break(self
, filename
, lineno
):
262 filename
= self
.canonic(filename
)
263 return self
.breaks
.has_key(filename
) and \
264 lineno
in self
.breaks
[filename
]
266 def get_breaks(self
, filename
, lineno
):
267 filename
= self
.canonic(filename
)
268 return self
.breaks
.has_key(filename
) and \
269 lineno
in self
.breaks
[filename
] and \
270 Breakpoint
.bplist
[filename
, lineno
] or []
272 def get_file_breaks(self
, filename
):
273 filename
= self
.canonic(filename
)
274 if self
.breaks
.has_key(filename
):
275 return self
.breaks
[filename
]
279 def get_all_breaks(self
):
282 # Derived classes and clients can call the following method
283 # to get a data structure representing a stack trace.
285 def get_stack(self
, f
, t
):
287 if t
and t
.tb_frame
is f
:
290 stack
.append((f
, f
.f_lineno
))
291 if f
is self
.botframe
:
295 i
= max(0, len(stack
) - 1)
297 stack
.append((t
.tb_frame
, t
.tb_lineno
))
303 def format_stack_entry(self
, frame_lineno
, lprefix
=': '):
304 import linecache
, repr, string
305 frame
, lineno
= frame_lineno
306 filename
= self
.canonic(frame
.f_code
.co_filename
)
307 s
= filename
+ '(' + `lineno`
+ ')'
308 if frame
.f_code
.co_name
:
309 s
= s
+ frame
.f_code
.co_name
312 if frame
.f_locals
.has_key('__args__'):
313 args
= frame
.f_locals
['__args__']
317 s
= s
+ repr.repr(args
)
320 if frame
.f_locals
.has_key('__return__'):
321 rv
= frame
.f_locals
['__return__']
323 s
= s
+ repr.repr(rv
)
324 line
= linecache
.getline(filename
, lineno
)
325 if line
: s
= s
+ lprefix
+ string
.strip(line
)
328 # The following two methods can be called by clients to use
329 # a debugger to debug a statement, given as a string.
331 def run(self
, cmd
, globals=None, locals=None):
334 globals = __main__
.__dict
__
338 sys
.settrace(self
.trace_dispatch
)
339 if type(cmd
) <> types
.CodeType
:
343 exec cmd
in globals, locals
350 def runeval(self
, expr
, globals=None, locals=None):
353 globals = __main__
.__dict
__
357 sys
.settrace(self
.trace_dispatch
)
358 if type(expr
) <> types
.CodeType
:
362 return eval(expr
, globals, locals)
369 def runctx(self
, cmd
, globals, locals):
371 self
.run(cmd
, globals, locals)
373 # This method is more useful to debug a single function call.
375 def runcall(self
, func
, *args
):
377 sys
.settrace(self
.trace_dispatch
)
381 res
= apply(func
, args
)
398 Implements temporary breakpoints, ignore counts, disabling and
399 (re)-enabling, and conditionals.
401 Breakpoints are indexed by number through bpbynumber and by
402 the file,line tuple using bplist. The former points to a
403 single instance of class Breakpoint. The latter points to a
404 list of such instances since there may be more than one
409 # XXX Keeping state in the class is a mistake -- this means
410 # you cannot have more than one active Bdb instance.
412 next
= 1 # Next bp to be assigned
413 bplist
= {} # indexed by (file, lineno) tuple
414 bpbynumber
= [None] # Each entry is None or an instance of Bpt
415 # index 0 is unused, except for marking an
416 # effective break .... see effective()
418 def __init__(self
, file, line
, temporary
=0, cond
= None):
419 self
.file = file # This better be in canonical form!
421 self
.temporary
= temporary
426 self
.number
= Breakpoint
.next
427 Breakpoint
.next
= Breakpoint
.next
+ 1
428 # Build the two lists
429 self
.bpbynumber
.append(self
)
430 if self
.bplist
.has_key((file, line
)):
431 self
.bplist
[file, line
].append(self
)
433 self
.bplist
[file, line
] = [self
]
437 index
= (self
.file, self
.line
)
438 self
.bpbynumber
[self
.number
] = None # No longer in list
439 self
.bplist
[index
].remove(self
)
440 if not self
.bplist
[index
]:
441 # No more bp for this f:l combo
442 del self
.bplist
[index
]
459 print '%-4dbreakpoint %s at %s:%d' % (self
.number
, disp
,
460 self
.file, self
.line
)
462 print '\tstop only if %s' % (self
.cond
,)
464 print '\tignore next %d hits' % (self
.ignore
)
466 if (self
.hits
> 1): ss
= 's'
468 print ('\tbreakpoint already hit %d time%s' %
471 # -----------end of Breakpoint class----------
473 # Determines if there is an effective (active) breakpoint at this
474 # line of code. Returns breakpoint number or 0 if none
475 def effective(file, line
, frame
):
476 """Determine which breakpoint for this file:line is to be acted upon.
478 Called only if we know there is a bpt at this
479 location. Returns breakpoint that was triggered and a flag
480 that indicates if it is ok to delete a temporary bp.
483 possibles
= Breakpoint
.bplist
[file,line
]
484 for i
in range(0, len(possibles
)):
488 # Count every hit when bp is enabled
491 # If unconditional, and ignoring,
492 # go on to next, else break
494 b
.ignore
= b
.ignore
-1
497 # breakpoint and marker that's ok
498 # to delete if temporary
502 # Ignore count applies only to those bpt hits where the
503 # condition evaluates to true.
505 val
= eval(b
.cond
, frame
.f_globals
,
509 b
.ignore
= b
.ignore
-1
516 # if eval fails, most conservative
517 # thing is to stop on breakpoint
518 # regardless of ignore count.
519 # Don't delete temporary,
520 # as another hint to user.
524 # -------------------- testing --------------------
527 def user_call(self
, frame
, args
):
528 name
= frame
.f_code
.co_name
529 if not name
: name
= '???'
530 print '+++ call', name
, args
531 def user_line(self
, frame
):
532 import linecache
, string
533 name
= frame
.f_code
.co_name
534 if not name
: name
= '???'
535 fn
= self
.canonic(frame
.f_code
.co_filename
)
536 line
= linecache
.getline(fn
, frame
.f_lineno
)
537 print '+++', fn
, frame
.f_lineno
, name
, ':', string
.strip(line
)
538 def user_return(self
, frame
, retval
):
539 print '+++ return', retval
540 def user_exception(self
, frame
, exc_stuff
):
541 print '+++ exception', exc_stuff
547 print 'bar returned', x
555 t
.run('import bdb; bdb.foo(10)')