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!
11 BdbQuit
= 'bdb.BdbQuit' # Exception to give up completely
14 class Bdb
: # Basic Debugger
22 self
.returnframe
= None
25 def trace_dispatch(self
, frame
, event
, arg
):
29 return self
.dispatch_line(frame
)
31 return self
.dispatch_call(frame
, arg
)
33 return self
.dispatch_return(frame
, arg
)
34 if event
== 'exception':
35 return self
.dispatch_exception(frame
, arg
)
36 print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
37 return self
.trace_dispatch
39 def dispatch_line(self
, frame
):
40 if self
.stop_here(frame
) or self
.break_here(frame
):
42 if self
.quitting
: raise BdbQuit
43 return self
.trace_dispatch
45 def dispatch_call(self
, frame
, arg
):
46 frame
.f_locals
['__args__'] = arg
47 if self
.botframe
is None:
48 # First call of dispatch since reset()
50 return self
.trace_dispatch
51 if not (self
.stop_here(frame
) or self
.break_anywhere(frame
)):
52 # No need to trace this function
54 self
.user_call(frame
, arg
)
55 if self
.quitting
: raise BdbQuit
56 return self
.trace_dispatch
58 def dispatch_return(self
, frame
, arg
):
59 if self
.stop_here(frame
) or frame
== self
.returnframe
:
60 self
.user_return(frame
, arg
)
61 if self
.quitting
: raise BdbQuit
63 def dispatch_exception(self
, frame
, arg
):
64 if self
.stop_here(frame
):
65 self
.user_exception(frame
, arg
)
66 if self
.quitting
: raise BdbQuit
67 return self
.trace_dispatch
69 # Normally derived classes don't override the following
70 # methods, but they may if they want to redefine the
71 # definition of stopping and breakpoints.
73 def stop_here(self
, frame
):
74 if self
.stopframe
is None:
76 if frame
is self
.stopframe
:
78 while frame
is not None and frame
is not self
.stopframe
:
79 if frame
is self
.botframe
:
84 def break_here(self
, frame
):
85 if not self
.breaks
.has_key(frame
.f_code
.co_filename
):
87 if not frame
.f_lineno
in \
88 self
.breaks
[frame
.f_code
.co_filename
]:
92 def break_anywhere(self
, frame
):
93 return self
.breaks
.has_key(frame
.f_code
.co_filename
)
95 # Derived classes should override the user_* methods
98 def user_call(self
, frame
, argument_list
):
99 # This method is called when there is the remote possibility
100 # that we ever need to stop in this function
103 def user_line(self
, frame
):
104 # This method is called when we stop or break at this line
107 def user_return(self
, frame
, return_value
):
108 # This method is called when a return trap is set here
111 def user_exception(self
, frame
, (exc_type
, exc_value
, exc_traceback
)):
112 # This method is called if an exception occurs,
113 # but only if we are to stop at or just below this level
116 # Derived classes and clients can call the following methods
117 # to affect the stepping state.
120 # Stop after one line of code
121 self
.stopframe
= None
122 self
.returnframe
= None
125 def set_next(self
, frame
):
126 # Stop on the next line in or below the given frame
127 self
.stopframe
= frame
128 self
.returnframe
= None
131 def set_return(self
, frame
):
132 # Stop when returning from the given frame
133 self
.stopframe
= frame
.f_back
134 self
.returnframe
= frame
137 def set_continue(self
):
138 # Don't stop except at breakpoints or when finished
139 self
.stopframe
= self
.botframe
140 self
.returnframe
= None
144 self
.stopframe
= self
.botframe
145 self
.returnframe
= None
149 # Derived classes and clients can call the following methods
150 # to manipulate breakpoints. These methods return an
151 # error message is something went wrong, None if all is well.
152 # Call self.get_*break*() to see the breakpoints.
154 def set_break(self
, filename
, lineno
):
155 import linecache
# Import as late as possible
156 line
= linecache
.getline(filename
, lineno
)
158 return 'That line does not exist!'
159 if not self
.breaks
.has_key(filename
):
160 self
.breaks
[filename
] = []
161 list = self
.breaks
[filename
]
163 return 'There is already a breakpoint there!'
166 def clear_break(self
, filename
, lineno
):
167 if not self
.breaks
.has_key(filename
):
168 return 'There are no breakpoints in that file!'
169 if lineno
not in self
.breaks
[filename
]:
170 return 'There is no breakpoint there!'
171 self
.breaks
[filename
].remove(lineno
)
172 if not self
.breaks
[filename
]:
173 del self
.breaks
[filename
]
175 def clear_all_file_breaks(self
, filename
):
176 if not self
.breaks
.has_key(filename
):
177 return 'There are no breakpoints in that file!'
178 del self
.breaks
[filename
]
180 def clear_all_breaks(self
, filename
, lineno
):
182 return 'There are no breakpoints!'
185 def get_break(self
, filename
, lineno
):
186 return self
.breaks
.has_key(filename
) and \
187 lineno
in self
.breaks
[filename
]
189 def get_file_breaks(self
, filename
):
190 if self
.breaks
.has_key(filename
):
191 return self
.breaks
[filename
]
195 def get_all_breaks(self
):
198 # Derived classes and clients can call the following method
199 # to get a data structure representing a stack trace.
201 def get_stack(self
, f
, t
):
203 if t
and t
.tb_frame
is f
:
206 stack
.append((f
, f
.f_lineno
))
207 if f
is self
.botframe
:
211 i
= max(0, len(stack
) - 1)
213 stack
.append((t
.tb_frame
, t
.tb_lineno
))
219 def format_stack_entry(self
, frame_lineno
):
220 import codehack
, linecache
, repr, string
221 frame
, lineno
= frame_lineno
222 filename
= frame
.f_code
.co_filename
223 s
= filename
+ '(' + `lineno`
+ ')'
224 s
= s
+ codehack
.getcodename(frame
.f_code
)
225 if frame
.f_locals
.has_key('__args__'):
226 args
= frame
.f_locals
['__args__']
228 s
= s
+ repr.repr(args
)
229 if frame
.f_locals
.has_key('__return__'):
230 rv
= frame
.f_locals
['__return__']
232 s
= s
+ repr.repr(rv
)
233 line
= linecache
.getline(filename
, lineno
)
234 if line
: s
= s
+ ': ' + string
.strip(line
)
237 # The following two methods can be called by clients to use
238 # a debugger to debug a statement, given as a string.
242 dict = __main__
.__dict
__
243 self
.runctx(cmd
, dict, dict)
245 def runctx(self
, cmd
, globals, locals):
247 sys
.settrace(self
.trace_dispatch
)
250 exec(cmd
+ '\n', globals, locals)
257 # This method is more useful to debug a single function call.
259 def runcall(self
, func
, *args
):
261 sys
.settrace(self
.trace_dispatch
)
272 # -------------------- testing --------------------
275 def user_call(self
, frame
, args
):
277 name
= codehack
.getcodename(frame
.f_code
)
278 if not name
: name
= '???'
279 print '+++ call', name
, args
280 def user_line(self
, frame
):
281 import linecache
, string
, codehack
282 name
= codehack
.getcodename(frame
.f_code
)
283 if not name
: name
= '???'
284 fn
= frame
.f_code
.co_filename
285 line
= linecache
.getline(fn
, frame
.f_lineno
)
286 print '+++', fn
, frame
.f_lineno
, name
, ':', string
.strip(line
)
287 def user_return(self
, frame
, retval
):
288 print '+++ return', retval
289 def user_exception(self
, frame
, exc_stuff
):
290 print '+++ exception', exc_stuff
296 print 'bar returned', x
304 linecache
.checkcache()
306 t
.run('import bdb; bdb.foo(10)')