1 # A generic Python debugger base class.
2 # This class takes care of details of the trace facility;
3 # a derived class should implement user interaction.
4 # There are two debuggers based upon this:
5 # 'pdb', a text-oriented debugger not unlike dbx or gdb;
6 # and 'wdb', a window-oriented debugger.
7 # And of course... you can roll your own!
12 BdbQuit
= 'bdb.BdbQuit' # Exception to give up completely
15 class Bdb
: # Basic Debugger
23 linecache
.checkcache()
26 self
.returnframe
= None
29 def trace_dispatch(self
, frame
, event
, arg
):
33 return self
.dispatch_line(frame
)
35 return self
.dispatch_call(frame
, arg
)
37 return self
.dispatch_return(frame
, arg
)
38 if event
== 'exception':
39 return self
.dispatch_exception(frame
, arg
)
40 print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
41 return self
.trace_dispatch
43 def dispatch_line(self
, frame
):
44 if self
.stop_here(frame
) or self
.break_here(frame
):
46 if self
.quitting
: raise BdbQuit
47 return self
.trace_dispatch
49 def dispatch_call(self
, frame
, arg
):
50 frame
.f_locals
['__args__'] = arg
51 if self
.botframe
is None:
52 # First call of dispatch since reset()
54 return self
.trace_dispatch
55 if not (self
.stop_here(frame
) or self
.break_anywhere(frame
)):
56 # No need to trace this function
58 self
.user_call(frame
, arg
)
59 if self
.quitting
: raise BdbQuit
60 return self
.trace_dispatch
62 def dispatch_return(self
, frame
, arg
):
63 if self
.stop_here(frame
) or frame
== self
.returnframe
:
64 self
.user_return(frame
, arg
)
65 if self
.quitting
: raise BdbQuit
67 def dispatch_exception(self
, frame
, arg
):
68 if self
.stop_here(frame
):
69 self
.user_exception(frame
, arg
)
70 if self
.quitting
: raise BdbQuit
71 return self
.trace_dispatch
73 # Normally derived classes don't override the following
74 # methods, but they may if they want to redefine the
75 # definition of stopping and breakpoints.
77 def stop_here(self
, frame
):
78 if self
.stopframe
is None:
80 if frame
is self
.stopframe
:
82 while frame
is not None and frame
is not self
.stopframe
:
83 if frame
is self
.botframe
:
88 def break_here(self
, frame
):
89 filename
=frame
.f_code
.co_filename
90 if not self
.breaks
.has_key(filename
):
93 if not lineno
in self
.breaks
[filename
]:
95 if self
.cbreaks
.has_key((filename
, lineno
)):
96 cond
=self
.cbreaks
[filename
, lineno
]
97 return eval(cond
, frame
.f_globals
,
101 def break_anywhere(self
, frame
):
102 return self
.breaks
.has_key(frame
.f_code
.co_filename
)
104 # Derived classes should override the user_* methods
107 def user_call(self
, frame
, argument_list
):
108 # This method is called when there is the remote possibility
109 # that we ever need to stop in this function
112 def user_line(self
, frame
):
113 # This method is called when we stop or break at this line
116 def user_return(self
, frame
, return_value
):
117 # This method is called when a return trap is set here
120 def user_exception(self
, frame
, (exc_type
, exc_value
, exc_traceback
)):
121 # This method is called if an exception occurs,
122 # but only if we are to stop at or just below this level
125 # Derived classes and clients can call the following methods
126 # to affect the stepping state.
129 # Stop after one line of code
130 self
.stopframe
= None
131 self
.returnframe
= None
134 def set_next(self
, frame
):
135 # Stop on the next line in or below the given frame
136 self
.stopframe
= frame
137 self
.returnframe
= None
140 def set_return(self
, frame
):
141 # Stop when returning from the given frame
142 self
.stopframe
= frame
.f_back
143 self
.returnframe
= frame
147 # Start debugging from here
151 frame
= sys
.exc_info()[2].tb_frame
.f_back
154 frame
.f_trace
= self
.trace_dispatch
155 self
.botframe
= frame
158 sys
.settrace(self
.trace_dispatch
)
160 def set_continue(self
):
161 # Don't stop except at breakpoints or when finished
162 self
.stopframe
= self
.botframe
163 self
.returnframe
= None
166 # no breakpoints; run without debugger overhead
169 1 + '' # raise an exception
171 frame
= sys
.exc_info()[2].tb_frame
.f_back
172 while frame
and frame
is not self
.botframe
:
177 self
.stopframe
= self
.botframe
178 self
.returnframe
= None
182 # Derived classes and clients can call the following methods
183 # to manipulate breakpoints. These methods return an
184 # error message is something went wrong, None if all is well.
185 # Call self.get_*break*() to see the breakpoints.
187 def set_break(self
, filename
, lineno
, cond
=None):
188 import linecache
# Import as late as possible
189 line
= linecache
.getline(filename
, lineno
)
191 return 'That line does not exist!'
192 if not self
.breaks
.has_key(filename
):
193 self
.breaks
[filename
] = []
194 list = self
.breaks
[filename
]
196 return 'There is already a breakpoint there!'
198 if cond
is not None: self
.cbreaks
[filename
, lineno
]=cond
200 def clear_break(self
, filename
, lineno
):
201 if not self
.breaks
.has_key(filename
):
202 return 'There are no breakpoints in that file!'
203 if lineno
not in self
.breaks
[filename
]:
204 return 'There is no breakpoint there!'
205 self
.breaks
[filename
].remove(lineno
)
206 if not self
.breaks
[filename
]:
207 del self
.breaks
[filename
]
208 try: del self
.cbreaks
[filename
, lineno
]
211 def clear_all_file_breaks(self
, filename
):
212 if not self
.breaks
.has_key(filename
):
213 return 'There are no breakpoints in that file!'
214 del self
.breaks
[filename
]
215 for f
,l
in self
.cbreaks
.keys():
216 if f
==filename
: del self
.cbreaks
[f
,l
]
218 def clear_all_breaks(self
):
220 return 'There are no breakpoints!'
224 def get_break(self
, filename
, lineno
):
225 return self
.breaks
.has_key(filename
) and \
226 lineno
in self
.breaks
[filename
]
228 def get_file_breaks(self
, filename
):
229 if self
.breaks
.has_key(filename
):
230 return self
.breaks
[filename
]
234 def get_all_breaks(self
):
237 # Derived classes and clients can call the following method
238 # to get a data structure representing a stack trace.
240 def get_stack(self
, f
, t
):
242 if t
and t
.tb_frame
is f
:
245 stack
.append((f
, f
.f_lineno
))
246 if f
is self
.botframe
:
250 i
= max(0, len(stack
) - 1)
252 stack
.append((t
.tb_frame
, t
.tb_lineno
))
258 def format_stack_entry(self
, frame_lineno
, lprefix
=': '):
259 import linecache
, repr, string
260 frame
, lineno
= frame_lineno
261 filename
= frame
.f_code
.co_filename
262 s
= filename
+ '(' + `lineno`
+ ')'
263 if frame
.f_code
.co_name
:
264 s
= s
+ frame
.f_code
.co_name
267 if frame
.f_locals
.has_key('__args__'):
268 args
= frame
.f_locals
['__args__']
272 s
= s
+ repr.repr(args
)
275 if frame
.f_locals
.has_key('__return__'):
276 rv
= frame
.f_locals
['__return__']
278 s
= s
+ repr.repr(rv
)
279 line
= linecache
.getline(filename
, lineno
)
280 if line
: s
= s
+ lprefix
+ string
.strip(line
)
283 # The following two methods can be called by clients to use
284 # a debugger to debug a statement, given as a string.
286 def run(self
, cmd
, globals=None, locals=None):
289 globals = __main__
.__dict
__
293 sys
.settrace(self
.trace_dispatch
)
294 if type(cmd
) <> types
.CodeType
:
298 exec cmd
in globals, locals
305 def runeval(self
, expr
, globals=None, locals=None):
308 globals = __main__
.__dict
__
312 sys
.settrace(self
.trace_dispatch
)
313 if type(expr
) <> types
.CodeType
:
317 return eval(expr
, globals, locals)
324 def runctx(self
, cmd
, globals, locals):
326 self
.run(cmd
, globals, locals)
328 # This method is more useful to debug a single function call.
330 def runcall(self
, func
, *args
):
332 sys
.settrace(self
.trace_dispatch
)
336 res
= apply(func
, args
)
348 # -------------------- testing --------------------
351 def user_call(self
, frame
, args
):
352 name
= frame
.f_code
.co_name
353 if not name
: name
= '???'
354 print '+++ call', name
, args
355 def user_line(self
, frame
):
356 import linecache
, string
357 name
= frame
.f_code
.co_name
358 if not name
: name
= '???'
359 fn
= frame
.f_code
.co_filename
360 line
= linecache
.getline(fn
, frame
.f_lineno
)
361 print '+++', fn
, frame
.f_lineno
, name
, ':', string
.strip(line
)
362 def user_return(self
, frame
, retval
):
363 print '+++ return', retval
364 def user_exception(self
, frame
, exc_stuff
):
365 print '+++ exception', exc_stuff
371 print 'bar returned', x
379 t
.run('import bdb; bdb.foo(10)')