7 BdbQuit
= 'bdb.BdbQuit' # Exception to give up completely
12 """Generic Python debugger base class.
14 This class takes care of details of the trace facility;
15 a derived class should implement user interaction.
16 The standard debugger class (pdb.Pdb) is an example.
23 def canonic(self
, filename
):
24 canonic
= self
.fncache
.get(filename
)
26 canonic
= os
.path
.abspath(filename
)
27 self
.fncache
[filename
] = canonic
32 linecache
.checkcache()
35 self
.returnframe
= None
38 def trace_dispatch(self
, frame
, event
, arg
):
42 return self
.dispatch_line(frame
)
44 return self
.dispatch_call(frame
, arg
)
46 return self
.dispatch_return(frame
, arg
)
47 if event
== 'exception':
48 return self
.dispatch_exception(frame
, arg
)
49 print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
50 return self
.trace_dispatch
52 def dispatch_line(self
, frame
):
53 if self
.stop_here(frame
) or self
.break_here(frame
):
55 if self
.quitting
: raise BdbQuit
56 return self
.trace_dispatch
58 def dispatch_call(self
, frame
, arg
):
59 # XXX 'arg' is no longer used
60 if self
.botframe
is None:
61 # First call of dispatch since reset()
63 return self
.trace_dispatch
64 if not (self
.stop_here(frame
) or self
.break_anywhere(frame
)):
65 # No need to trace this function
67 self
.user_call(frame
, arg
)
68 if self
.quitting
: raise BdbQuit
69 return self
.trace_dispatch
71 def dispatch_return(self
, frame
, arg
):
72 if self
.stop_here(frame
) or frame
== self
.returnframe
:
73 self
.user_return(frame
, arg
)
74 if self
.quitting
: raise BdbQuit
76 def dispatch_exception(self
, frame
, arg
):
77 if self
.stop_here(frame
):
78 self
.user_exception(frame
, arg
)
79 if self
.quitting
: raise BdbQuit
80 return self
.trace_dispatch
82 # Normally derived classes don't override the following
83 # methods, but they may if they want to redefine the
84 # definition of stopping and breakpoints.
86 def stop_here(self
, frame
):
87 if self
.stopframe
is None:
89 if frame
is self
.stopframe
:
91 while frame
is not None and frame
is not self
.stopframe
:
92 if frame
is self
.botframe
:
97 def break_here(self
, frame
):
98 filename
= self
.canonic(frame
.f_code
.co_filename
)
99 if not self
.breaks
.has_key(filename
):
101 lineno
= frame
.f_lineno
102 if not lineno
in self
.breaks
[filename
]:
104 # flag says ok to delete temp. bp
105 (bp
, flag
) = effective(filename
, lineno
, frame
)
107 self
.currentbp
= bp
.number
108 if (flag
and bp
.temporary
):
109 self
.do_clear(str(bp
.number
))
114 def break_anywhere(self
, frame
):
115 return self
.breaks
.has_key(
116 self
.canonic(frame
.f_code
.co_filename
))
118 # Derived classes should override the user_* methods
121 def user_call(self
, frame
, argument_list
):
122 """This method is called when there is the remote possibility
123 that we ever need to stop in this function."""
126 def user_line(self
, frame
):
127 """This method is called when we stop or break at this line."""
130 def user_return(self
, frame
, return_value
):
131 """This method is called when a return trap is set here."""
134 def user_exception(self
, frame
, (exc_type
, exc_value
, exc_traceback
)):
135 """This method is called if an exception occurs,
136 but only if we are to stop at or just below this level."""
139 # Derived classes and clients can call the following methods
140 # to affect the stepping state.
143 """Stop after one line of code."""
144 self
.stopframe
= None
145 self
.returnframe
= None
148 def set_next(self
, frame
):
149 """Stop on the next line in or below the given frame."""
150 self
.stopframe
= frame
151 self
.returnframe
= None
154 def set_return(self
, frame
):
155 """Stop when returning from the given frame."""
156 self
.stopframe
= frame
.f_back
157 self
.returnframe
= frame
161 """Start debugging from here."""
165 frame
= sys
.exc_info()[2].tb_frame
.f_back
168 frame
.f_trace
= self
.trace_dispatch
169 self
.botframe
= frame
172 sys
.settrace(self
.trace_dispatch
)
174 def set_continue(self
):
175 # Don't stop except at breakpoints or when finished
176 self
.stopframe
= self
.botframe
177 self
.returnframe
= None
180 # no breakpoints; run without debugger overhead
183 1 + '' # raise an exception
185 frame
= sys
.exc_info()[2].tb_frame
.f_back
186 while frame
and frame
is not self
.botframe
:
191 self
.stopframe
= self
.botframe
192 self
.returnframe
= None
196 # Derived classes and clients can call the following methods
197 # to manipulate breakpoints. These methods return an
198 # error message is something went wrong, None if all is well.
199 # Set_break prints out the breakpoint line and file:lineno.
200 # Call self.get_*break*() to see the breakpoints or better
201 # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
203 def set_break(self
, filename
, lineno
, temporary
=0, cond
= None):
204 filename
= self
.canonic(filename
)
205 import linecache
# Import as late as possible
206 line
= linecache
.getline(filename
, lineno
)
208 return 'Line %s:%d does not exist' % (filename
,
210 if not self
.breaks
.has_key(filename
):
211 self
.breaks
[filename
] = []
212 list = self
.breaks
[filename
]
213 if not lineno
in list:
215 bp
= Breakpoint(filename
, lineno
, temporary
, cond
)
217 def clear_break(self
, filename
, lineno
):
218 filename
= self
.canonic(filename
)
219 if not self
.breaks
.has_key(filename
):
220 return 'There are no breakpoints in %s' % filename
221 if lineno
not in self
.breaks
[filename
]:
222 return 'There is no breakpoint at %s:%d' % (filename
,
224 # If there's only one bp in the list for that file,line
225 # pair, then remove the breaks entry
226 for bp
in Breakpoint
.bplist
[filename
, lineno
][:]:
228 if not Breakpoint
.bplist
.has_key((filename
, lineno
)):
229 self
.breaks
[filename
].remove(lineno
)
230 if not self
.breaks
[filename
]:
231 del self
.breaks
[filename
]
233 def clear_bpbynumber(self
, arg
):
237 return 'Non-numeric breakpoint number (%s)' % arg
239 bp
= Breakpoint
.bpbynumber
[number
]
241 return 'Breakpoint number (%d) out of range' % number
243 return 'Breakpoint (%d) already deleted' % number
244 self
.clear_break(bp
.file, bp
.line
)
246 def clear_all_file_breaks(self
, filename
):
247 filename
= self
.canonic(filename
)
248 if not self
.breaks
.has_key(filename
):
249 return 'There are no breakpoints in %s' % filename
250 for line
in self
.breaks
[filename
]:
251 blist
= Breakpoint
.bplist
[filename
, line
]
254 del self
.breaks
[filename
]
256 def clear_all_breaks(self
):
258 return 'There are no breakpoints'
259 for bp
in Breakpoint
.bpbynumber
:
264 def get_break(self
, filename
, lineno
):
265 filename
= self
.canonic(filename
)
266 return self
.breaks
.has_key(filename
) and \
267 lineno
in self
.breaks
[filename
]
269 def get_breaks(self
, filename
, lineno
):
270 filename
= self
.canonic(filename
)
271 return self
.breaks
.has_key(filename
) and \
272 lineno
in self
.breaks
[filename
] and \
273 Breakpoint
.bplist
[filename
, lineno
] or []
275 def get_file_breaks(self
, filename
):
276 filename
= self
.canonic(filename
)
277 if self
.breaks
.has_key(filename
):
278 return self
.breaks
[filename
]
282 def get_all_breaks(self
):
285 # Derived classes and clients can call the following method
286 # to get a data structure representing a stack trace.
288 def get_stack(self
, f
, t
):
290 if t
and t
.tb_frame
is f
:
293 stack
.append((f
, f
.f_lineno
))
294 if f
is self
.botframe
:
298 i
= max(0, len(stack
) - 1)
300 stack
.append((t
.tb_frame
, t
.tb_lineno
))
306 def format_stack_entry(self
, frame_lineno
, lprefix
=': '):
307 import linecache
, repr, string
308 frame
, lineno
= frame_lineno
309 filename
= self
.canonic(frame
.f_code
.co_filename
)
310 s
= filename
+ '(' + `lineno`
+ ')'
311 if frame
.f_code
.co_name
:
312 s
= s
+ frame
.f_code
.co_name
315 if frame
.f_locals
.has_key('__args__'):
316 args
= frame
.f_locals
['__args__']
320 s
= s
+ repr.repr(args
)
323 if frame
.f_locals
.has_key('__return__'):
324 rv
= frame
.f_locals
['__return__']
326 s
= s
+ repr.repr(rv
)
327 line
= linecache
.getline(filename
, lineno
)
328 if line
: s
= s
+ lprefix
+ string
.strip(line
)
331 # The following two methods can be called by clients to use
332 # a debugger to debug a statement, given as a string.
334 def run(self
, cmd
, globals=None, locals=None):
337 globals = __main__
.__dict
__
341 sys
.settrace(self
.trace_dispatch
)
342 if not isinstance(cmd
, types
.CodeType
):
346 exec cmd
in globals, locals
353 def runeval(self
, expr
, globals=None, locals=None):
356 globals = __main__
.__dict
__
360 sys
.settrace(self
.trace_dispatch
)
361 if not isinstance(expr
, types
.CodeType
):
365 return eval(expr
, globals, locals)
372 def runctx(self
, cmd
, globals, locals):
374 self
.run(cmd
, globals, locals)
376 # This method is more useful to debug a single function call.
378 def runcall(self
, func
, *args
):
380 sys
.settrace(self
.trace_dispatch
)
384 res
= apply(func
, args
)
401 Implements temporary breakpoints, ignore counts, disabling and
402 (re)-enabling, and conditionals.
404 Breakpoints are indexed by number through bpbynumber and by
405 the file,line tuple using bplist. The former points to a
406 single instance of class Breakpoint. The latter points to a
407 list of such instances since there may be more than one
412 # XXX Keeping state in the class is a mistake -- this means
413 # you cannot have more than one active Bdb instance.
415 next
= 1 # Next bp to be assigned
416 bplist
= {} # indexed by (file, lineno) tuple
417 bpbynumber
= [None] # Each entry is None or an instance of Bpt
418 # index 0 is unused, except for marking an
419 # effective break .... see effective()
421 def __init__(self
, file, line
, temporary
=0, cond
= None):
422 self
.file = file # This better be in canonical form!
424 self
.temporary
= temporary
429 self
.number
= Breakpoint
.next
430 Breakpoint
.next
= Breakpoint
.next
+ 1
431 # Build the two lists
432 self
.bpbynumber
.append(self
)
433 if self
.bplist
.has_key((file, line
)):
434 self
.bplist
[file, line
].append(self
)
436 self
.bplist
[file, line
] = [self
]
440 index
= (self
.file, self
.line
)
441 self
.bpbynumber
[self
.number
] = None # No longer in list
442 self
.bplist
[index
].remove(self
)
443 if not self
.bplist
[index
]:
444 # No more bp for this f:l combo
445 del self
.bplist
[index
]
462 print '%-4dbreakpoint %s at %s:%d' % (self
.number
, disp
,
463 self
.file, self
.line
)
465 print '\tstop only if %s' % (self
.cond
,)
467 print '\tignore next %d hits' % (self
.ignore
)
469 if (self
.hits
> 1): ss
= 's'
471 print ('\tbreakpoint already hit %d time%s' %
474 # -----------end of Breakpoint class----------
476 # Determines if there is an effective (active) breakpoint at this
477 # line of code. Returns breakpoint number or 0 if none
478 def effective(file, line
, frame
):
479 """Determine which breakpoint for this file:line is to be acted upon.
481 Called only if we know there is a bpt at this
482 location. Returns breakpoint that was triggered and a flag
483 that indicates if it is ok to delete a temporary bp.
486 possibles
= Breakpoint
.bplist
[file,line
]
487 for i
in range(0, len(possibles
)):
491 # Count every hit when bp is enabled
494 # If unconditional, and ignoring,
495 # go on to next, else break
497 b
.ignore
= b
.ignore
-1
500 # breakpoint and marker that's ok
501 # to delete if temporary
505 # Ignore count applies only to those bpt hits where the
506 # condition evaluates to true.
508 val
= eval(b
.cond
, frame
.f_globals
,
512 b
.ignore
= b
.ignore
-1
519 # if eval fails, most conservative
520 # thing is to stop on breakpoint
521 # regardless of ignore count.
522 # Don't delete temporary,
523 # as another hint to user.
527 # -------------------- testing --------------------
530 def user_call(self
, frame
, args
):
531 name
= frame
.f_code
.co_name
532 if not name
: name
= '???'
533 print '+++ call', name
, args
534 def user_line(self
, frame
):
535 import linecache
, string
536 name
= frame
.f_code
.co_name
537 if not name
: name
= '???'
538 fn
= self
.canonic(frame
.f_code
.co_filename
)
539 line
= linecache
.getline(fn
, frame
.f_lineno
)
540 print '+++', fn
, frame
.f_lineno
, name
, ':', string
.strip(line
)
541 def user_return(self
, frame
, retval
):
542 print '+++ return', retval
543 def user_exception(self
, frame
, exc_stuff
):
544 print '+++ exception', exc_stuff
550 print 'bar returned', x
558 t
.run('import bdb; bdb.foo(10)')