7 __all__
= ["BdbQuit","Bdb","Breakpoint"]
9 BdbQuit
= 'bdb.BdbQuit' # Exception to give up completely
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.
25 def canonic(self
, filename
):
26 if filename
== "<" + filename
[1:-1] + ">":
28 canonic
= self
.fncache
.get(filename
)
30 canonic
= os
.path
.abspath(filename
)
31 canonic
= os
.path
.normcase(canonic
)
32 self
.fncache
[filename
] = canonic
37 linecache
.checkcache()
40 self
.returnframe
= None
43 def trace_dispatch(self
, frame
, event
, arg
):
47 return self
.dispatch_line(frame
)
49 return self
.dispatch_call(frame
, arg
)
51 return self
.dispatch_return(frame
, arg
)
52 if event
== 'exception':
53 return self
.dispatch_exception(frame
, arg
)
54 print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
55 return self
.trace_dispatch
57 def dispatch_line(self
, frame
):
58 if self
.stop_here(frame
) or self
.break_here(frame
):
60 if self
.quitting
: raise BdbQuit
61 return self
.trace_dispatch
63 def dispatch_call(self
, frame
, arg
):
64 # XXX 'arg' is no longer used
65 if self
.botframe
is None:
66 # First call of dispatch since reset()
68 return self
.trace_dispatch
69 if not (self
.stop_here(frame
) or self
.break_anywhere(frame
)):
70 # No need to trace this function
72 self
.user_call(frame
, arg
)
73 if self
.quitting
: raise BdbQuit
74 return self
.trace_dispatch
76 def dispatch_return(self
, frame
, arg
):
77 if self
.stop_here(frame
) or frame
== self
.returnframe
:
78 self
.user_return(frame
, arg
)
79 if self
.quitting
: raise BdbQuit
80 return self
.trace_dispatch
82 def dispatch_exception(self
, frame
, arg
):
83 if self
.stop_here(frame
):
84 self
.user_exception(frame
, arg
)
85 if self
.quitting
: raise BdbQuit
86 return self
.trace_dispatch
88 # Normally derived classes don't override the following
89 # methods, but they may if they want to redefine the
90 # definition of stopping and breakpoints.
92 def stop_here(self
, frame
):
93 if self
.stopframe
is None:
95 if frame
is self
.stopframe
:
97 while frame
is not None and frame
is not self
.stopframe
:
98 if frame
is self
.botframe
:
103 def break_here(self
, frame
):
104 filename
= self
.canonic(frame
.f_code
.co_filename
)
105 if not self
.breaks
.has_key(filename
):
107 lineno
= frame
.f_lineno
108 if not lineno
in self
.breaks
[filename
]:
110 # flag says ok to delete temp. bp
111 (bp
, flag
) = effective(filename
, lineno
, frame
)
113 self
.currentbp
= bp
.number
114 if (flag
and bp
.temporary
):
115 self
.do_clear(str(bp
.number
))
120 def do_clear(self
, arg
):
121 raise NotImplementedError, "subclass of bdb must implement do_clear()"
123 def break_anywhere(self
, frame
):
124 return self
.breaks
.has_key(
125 self
.canonic(frame
.f_code
.co_filename
))
127 # Derived classes should override the user_* methods
130 def user_call(self
, frame
, argument_list
):
131 """This method is called when there is the remote possibility
132 that we ever need to stop in this function."""
135 def user_line(self
, frame
):
136 """This method is called when we stop or break at this line."""
139 def user_return(self
, frame
, return_value
):
140 """This method is called when a return trap is set here."""
143 def user_exception(self
, frame
, (exc_type
, exc_value
, exc_traceback
)):
144 """This method is called if an exception occurs,
145 but only if we are to stop at or just below this level."""
148 # Derived classes and clients can call the following methods
149 # to affect the stepping state.
152 """Stop after one line of code."""
153 self
.stopframe
= None
154 self
.returnframe
= None
157 def set_next(self
, frame
):
158 """Stop on the next line in or below the given frame."""
159 self
.stopframe
= frame
160 self
.returnframe
= None
163 def set_return(self
, frame
):
164 """Stop when returning from the given frame."""
165 self
.stopframe
= frame
.f_back
166 self
.returnframe
= frame
170 """Start debugging from here."""
174 frame
= sys
.exc_info()[2].tb_frame
.f_back
177 frame
.f_trace
= self
.trace_dispatch
178 self
.botframe
= frame
181 sys
.settrace(self
.trace_dispatch
)
183 def set_continue(self
):
184 # Don't stop except at breakpoints or when finished
185 self
.stopframe
= self
.botframe
186 self
.returnframe
= None
189 # no breakpoints; run without debugger overhead
192 1 + '' # raise an exception
194 frame
= sys
.exc_info()[2].tb_frame
.f_back
195 while frame
and frame
is not self
.botframe
:
200 self
.stopframe
= self
.botframe
201 self
.returnframe
= None
205 # Derived classes and clients can call the following methods
206 # to manipulate breakpoints. These methods return an
207 # error message is something went wrong, None if all is well.
208 # Set_break prints out the breakpoint line and file:lineno.
209 # Call self.get_*break*() to see the breakpoints or better
210 # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
212 def set_break(self
, filename
, lineno
, temporary
=0, cond
= None):
213 filename
= self
.canonic(filename
)
214 import linecache
# Import as late as possible
215 line
= linecache
.getline(filename
, lineno
)
217 return 'Line %s:%d does not exist' % (filename
,
219 if not self
.breaks
.has_key(filename
):
220 self
.breaks
[filename
] = []
221 list = self
.breaks
[filename
]
222 if not lineno
in list:
224 bp
= Breakpoint(filename
, lineno
, temporary
, cond
)
226 def clear_break(self
, filename
, lineno
):
227 filename
= self
.canonic(filename
)
228 if not self
.breaks
.has_key(filename
):
229 return 'There are no breakpoints in %s' % filename
230 if lineno
not in self
.breaks
[filename
]:
231 return 'There is no breakpoint at %s:%d' % (filename
,
233 # If there's only one bp in the list for that file,line
234 # pair, then remove the breaks entry
235 for bp
in Breakpoint
.bplist
[filename
, lineno
][:]:
237 if not Breakpoint
.bplist
.has_key((filename
, lineno
)):
238 self
.breaks
[filename
].remove(lineno
)
239 if not self
.breaks
[filename
]:
240 del self
.breaks
[filename
]
242 def clear_bpbynumber(self
, arg
):
246 return 'Non-numeric breakpoint number (%s)' % arg
248 bp
= Breakpoint
.bpbynumber
[number
]
250 return 'Breakpoint number (%d) out of range' % number
252 return 'Breakpoint (%d) already deleted' % number
253 self
.clear_break(bp
.file, bp
.line
)
255 def clear_all_file_breaks(self
, filename
):
256 filename
= self
.canonic(filename
)
257 if not self
.breaks
.has_key(filename
):
258 return 'There are no breakpoints in %s' % filename
259 for line
in self
.breaks
[filename
]:
260 blist
= Breakpoint
.bplist
[filename
, line
]
263 del self
.breaks
[filename
]
265 def clear_all_breaks(self
):
267 return 'There are no breakpoints'
268 for bp
in Breakpoint
.bpbynumber
:
273 def get_break(self
, filename
, lineno
):
274 filename
= self
.canonic(filename
)
275 return self
.breaks
.has_key(filename
) and \
276 lineno
in self
.breaks
[filename
]
278 def get_breaks(self
, filename
, lineno
):
279 filename
= self
.canonic(filename
)
280 return self
.breaks
.has_key(filename
) and \
281 lineno
in self
.breaks
[filename
] and \
282 Breakpoint
.bplist
[filename
, lineno
] or []
284 def get_file_breaks(self
, filename
):
285 filename
= self
.canonic(filename
)
286 if self
.breaks
.has_key(filename
):
287 return self
.breaks
[filename
]
291 def get_all_breaks(self
):
294 # Derived classes and clients can call the following method
295 # to get a data structure representing a stack trace.
297 def get_stack(self
, f
, t
):
299 if t
and t
.tb_frame
is f
:
302 stack
.append((f
, f
.f_lineno
))
303 if f
is self
.botframe
:
307 i
= max(0, len(stack
) - 1)
309 stack
.append((t
.tb_frame
, t
.tb_lineno
))
315 def format_stack_entry(self
, frame_lineno
, lprefix
=': '):
316 import linecache
, repr
317 frame
, lineno
= frame_lineno
318 filename
= self
.canonic(frame
.f_code
.co_filename
)
319 s
= filename
+ '(' + `lineno`
+ ')'
320 if frame
.f_code
.co_name
:
321 s
= s
+ frame
.f_code
.co_name
324 if frame
.f_locals
.has_key('__args__'):
325 args
= frame
.f_locals
['__args__']
329 s
= s
+ repr.repr(args
)
332 if frame
.f_locals
.has_key('__return__'):
333 rv
= frame
.f_locals
['__return__']
335 s
= s
+ repr.repr(rv
)
336 line
= linecache
.getline(filename
, lineno
)
337 if line
: s
= s
+ lprefix
+ line
.strip()
340 # The following two methods can be called by clients to use
341 # a debugger to debug a statement, given as a string.
343 def run(self
, cmd
, globals=None, locals=None):
346 globals = __main__
.__dict
__
350 sys
.settrace(self
.trace_dispatch
)
351 if not isinstance(cmd
, types
.CodeType
):
355 exec cmd
in globals, locals
362 def runeval(self
, expr
, globals=None, locals=None):
365 globals = __main__
.__dict
__
369 sys
.settrace(self
.trace_dispatch
)
370 if not isinstance(expr
, types
.CodeType
):
374 return eval(expr
, globals, locals)
381 def runctx(self
, cmd
, globals, locals):
383 self
.run(cmd
, globals, locals)
385 # This method is more useful to debug a single function call.
387 def runcall(self
, func
, *args
):
389 sys
.settrace(self
.trace_dispatch
)
393 res
= apply(func
, args
)
410 Implements temporary breakpoints, ignore counts, disabling and
411 (re)-enabling, and conditionals.
413 Breakpoints are indexed by number through bpbynumber and by
414 the file,line tuple using bplist. The former points to a
415 single instance of class Breakpoint. The latter points to a
416 list of such instances since there may be more than one
421 # XXX Keeping state in the class is a mistake -- this means
422 # you cannot have more than one active Bdb instance.
424 next
= 1 # Next bp to be assigned
425 bplist
= {} # indexed by (file, lineno) tuple
426 bpbynumber
= [None] # Each entry is None or an instance of Bpt
427 # index 0 is unused, except for marking an
428 # effective break .... see effective()
430 def __init__(self
, file, line
, temporary
=0, cond
= None):
431 self
.file = file # This better be in canonical form!
433 self
.temporary
= temporary
438 self
.number
= Breakpoint
.next
439 Breakpoint
.next
= Breakpoint
.next
+ 1
440 # Build the two lists
441 self
.bpbynumber
.append(self
)
442 if self
.bplist
.has_key((file, line
)):
443 self
.bplist
[file, line
].append(self
)
445 self
.bplist
[file, line
] = [self
]
449 index
= (self
.file, self
.line
)
450 self
.bpbynumber
[self
.number
] = None # No longer in list
451 self
.bplist
[index
].remove(self
)
452 if not self
.bplist
[index
]:
453 # No more bp for this f:l combo
454 del self
.bplist
[index
]
471 print '%-4dbreakpoint %s at %s:%d' % (self
.number
, disp
,
472 self
.file, self
.line
)
474 print '\tstop only if %s' % (self
.cond
,)
476 print '\tignore next %d hits' % (self
.ignore
)
478 if (self
.hits
> 1): ss
= 's'
480 print ('\tbreakpoint already hit %d time%s' %
483 # -----------end of Breakpoint class----------
485 # Determines if there is an effective (active) breakpoint at this
486 # line of code. Returns breakpoint number or 0 if none
487 def effective(file, line
, frame
):
488 """Determine which breakpoint for this file:line is to be acted upon.
490 Called only if we know there is a bpt at this
491 location. Returns breakpoint that was triggered and a flag
492 that indicates if it is ok to delete a temporary bp.
495 possibles
= Breakpoint
.bplist
[file,line
]
496 for i
in range(0, len(possibles
)):
500 # Count every hit when bp is enabled
503 # If unconditional, and ignoring,
504 # go on to next, else break
506 b
.ignore
= b
.ignore
-1
509 # breakpoint and marker that's ok
510 # to delete if temporary
514 # Ignore count applies only to those bpt hits where the
515 # condition evaluates to true.
517 val
= eval(b
.cond
, frame
.f_globals
,
521 b
.ignore
= b
.ignore
-1
528 # if eval fails, most conservative
529 # thing is to stop on breakpoint
530 # regardless of ignore count.
531 # Don't delete temporary,
532 # as another hint to user.
536 # -------------------- testing --------------------
539 def user_call(self
, frame
, args
):
540 name
= frame
.f_code
.co_name
541 if not name
: name
= '???'
542 print '+++ call', name
, args
543 def user_line(self
, frame
):
545 name
= frame
.f_code
.co_name
546 if not name
: name
= '???'
547 fn
= self
.canonic(frame
.f_code
.co_filename
)
548 line
= linecache
.getline(fn
, frame
.f_lineno
)
549 print '+++', fn
, frame
.f_lineno
, name
, ':', line
.strip()
550 def user_return(self
, frame
, retval
):
551 print '+++ return', retval
552 def user_exception(self
, frame
, exc_stuff
):
553 print '+++ exception', exc_stuff
559 print 'bar returned', x
567 t
.run('import bdb; bdb.foo(10)')