##// END OF EJS Templates
existence of old_all_completions checked in debugger exit
vivainio -
Show More
@@ -1,411 +1,415 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Pdb debugger class.
4 4
5 5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 6 the command line completion of other programs which include this isn't
7 7 damaged.
8 8
9 9 In the future, this class will be expanded with improvements over the standard
10 10 pdb.
11 11
12 12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 13 changes. Licensing should therefore be under the standard Python terms. For
14 14 details on the PSF (Python Software Foundation) standard license, see:
15 15
16 16 http://www.python.org/2.2.3/license.html
17 17
18 $Id: Debugger.py 1853 2006-10-30 17:00:39Z vivainio $"""
18 $Id: Debugger.py 1951 2006-11-29 07:56:47Z vivainio $"""
19 19
20 20 #*****************************************************************************
21 21 #
22 22 # Since this file is essentially a modified copy of the pdb module which is
23 23 # part of the standard Python distribution, I assume that the proper procedure
24 24 # is to maintain its copyright as belonging to the Python Software Foundation
25 25 # (in addition to my own, for all new code).
26 26 #
27 27 # Copyright (C) 2001 Python Software Foundation, www.python.org
28 28 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
29 29 #
30 30 # Distributed under the terms of the BSD License. The full license is in
31 31 # the file COPYING, distributed as part of this software.
32 32 #
33 33 #*****************************************************************************
34 34
35 35 from IPython import Release
36 36 __author__ = '%s <%s>' % Release.authors['Fernando']
37 37 __license__ = 'Python'
38 38
39 39 import bdb
40 40 import cmd
41 41 import linecache
42 42 import os
43 43 import sys
44 44
45 45 from IPython import PyColorize, ColorANSI
46 46 from IPython.genutils import Term
47 47 from IPython.excolors import ExceptionColors
48 48
49 49 # See if we can use pydb.
50 50 has_pydb = False
51 51 prompt = 'ipdb>'
52 52 if sys.version[:3] >= '2.5':
53 53 try:
54 54 import pydb
55 55 if hasattr(pydb.pydb, "runl"):
56 56 has_pydb = True
57 57 from pydb import Pdb as OldPdb
58 58 prompt = 'ipydb>'
59 59 except ImportError:
60 60 pass
61 61
62 62 if has_pydb:
63 63 from pydb import Pdb as OldPdb
64 64 else:
65 65 from pdb import Pdb as OldPdb
66 66
67 67 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
68 68 """Make new_fn have old_fn's doc string. This is particularly useful
69 69 for the do_... commands that hook into the help system.
70 70 Adapted from from a comp.lang.python posting
71 71 by Duncan Booth."""
72 72 def wrapper(*args, **kw):
73 73 return new_fn(*args, **kw)
74 74 if old_fn.__doc__:
75 75 wrapper.__doc__ = old_fn.__doc__ + additional_text
76 76 return wrapper
77 77
78 78 def _file_lines(fname):
79 79 """Return the contents of a named file as a list of lines.
80 80
81 81 This function never raises an IOError exception: if the file can't be
82 82 read, it simply returns an empty list."""
83 83
84 84 try:
85 85 outfile = open(fname)
86 86 except IOError:
87 87 return []
88 88 else:
89 89 out = outfile.readlines()
90 90 outfile.close()
91 91 return out
92 92
93 93 class Pdb(OldPdb):
94 94 """Modified Pdb class, does not load readline."""
95 95
96 96 if sys.version[:3] >= '2.5':
97 97 def __init__(self,color_scheme='NoColor',completekey=None,
98 98 stdin=None, stdout=None):
99 99
100 100 # Parent constructor:
101 101 OldPdb.__init__(self,completekey,stdin,stdout)
102 102
103 103 # IPython changes...
104 104 self.prompt = prompt # The default prompt is '(Pdb)'
105 105 self.is_pydb = prompt == 'ipydb>'
106 106
107 107 if self.is_pydb:
108 108
109 109 # iplib.py's ipalias seems to want pdb's checkline
110 110 # which located in pydb.fn
111 111 import pydb.fns
112 112 self.checkline = lambda filename, lineno: \
113 113 pydb.fns.checkline(self, filename, lineno)
114 114
115 115 self.curframe = None
116 116 self.do_restart = self.new_do_restart
117 117
118 118 self.old_all_completions = __IPYTHON__.Completer.all_completions
119 119 __IPYTHON__.Completer.all_completions=self.all_completions
120 120
121 121 # Do we have access to pydb's list command parser?
122 122 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
123 123 OldPdb.do_list)
124 124 self.do_l = self.do_list
125 125 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
126 126 OldPdb.do_frame)
127 127
128 128 self.aliases = {}
129 129
130 130 # Create color table: we copy the default one from the traceback
131 131 # module and add a few attributes needed for debugging
132 132 self.color_scheme_table = ExceptionColors.copy()
133 133
134 134 # shorthands
135 135 C = ColorANSI.TermColors
136 136 cst = self.color_scheme_table
137 137
138 138 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
139 139 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
140 140
141 141 cst['Linux'].colors.breakpoint_enabled = C.LightRed
142 142 cst['Linux'].colors.breakpoint_disabled = C.Red
143 143
144 144 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
145 145 cst['LightBG'].colors.breakpoint_disabled = C.Red
146 146
147 147 self.set_colors(color_scheme)
148 148
149 149 else:
150 150 # Ugly hack: for Python 2.3-2.4, we can't call the parent constructor,
151 151 # because it binds readline and breaks tab-completion. This means we
152 152 # have to COPY the constructor here.
153 153 def __init__(self,color_scheme='NoColor'):
154 154 bdb.Bdb.__init__(self)
155 155 cmd.Cmd.__init__(self,completekey=None) # don't load readline
156 156 self.prompt = 'ipdb> ' # The default prompt is '(Pdb)'
157 157 self.aliases = {}
158 158
159 159 # These two lines are part of the py2.4 constructor, let's put them
160 160 # unconditionally here as they won't cause any problems in 2.3.
161 161 self.mainpyfile = ''
162 162 self._wait_for_mainpyfile = 0
163 163
164 164 # Read $HOME/.pdbrc and ./.pdbrc
165 165 try:
166 166 self.rcLines = _file_lines(os.path.join(os.environ['HOME'],
167 167 ".pdbrc"))
168 168 except KeyError:
169 169 self.rcLines = []
170 170 self.rcLines.extend(_file_lines(".pdbrc"))
171 171
172 172 # Create color table: we copy the default one from the traceback
173 173 # module and add a few attributes needed for debugging
174 174 self.color_scheme_table = ExceptionColors.copy()
175 175
176 176 # shorthands
177 177 C = ColorANSI.TermColors
178 178 cst = self.color_scheme_table
179 179
180 180 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
181 181 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
182 182
183 183 cst['Linux'].colors.breakpoint_enabled = C.LightRed
184 184 cst['Linux'].colors.breakpoint_disabled = C.Red
185 185
186 186 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
187 187 cst['LightBG'].colors.breakpoint_disabled = C.Red
188 188
189 189 self.set_colors(color_scheme)
190 190
191 191 def set_colors(self, scheme):
192 192 """Shorthand access to the color table scheme selector method."""
193 193 self.color_scheme_table.set_active_scheme(scheme)
194 194
195 195 def interaction(self, frame, traceback):
196 196 __IPYTHON__.set_completer_frame(frame)
197 197 OldPdb.interaction(self, frame, traceback)
198 198
199 199 def new_do_up(self, arg):
200 200 OldPdb.do_up(self, arg)
201 201 __IPYTHON__.set_completer_frame(self.curframe)
202 202 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
203 203
204 204 def new_do_down(self, arg):
205 205 OldPdb.do_down(self, arg)
206 206 __IPYTHON__.set_completer_frame(self.curframe)
207 207
208 208 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
209 209
210 210 def new_do_frame(self, arg):
211 211 OldPdb.do_frame(self, arg)
212 212 __IPYTHON__.set_completer_frame(self.curframe)
213 213
214 214 def new_do_quit(self, arg):
215 __IPYTHON__.Completer.all_completions=self.old_all_completions
215
216 if hasattr(self, 'old_all_completions'):
217 __IPYTHON__.Completer.all_completions=self.old_all_completions
218
219
216 220 return OldPdb.do_quit(self, arg)
217 221
218 222 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
219 223
220 224 def new_do_restart(self, arg):
221 225 """Restart command. In the context of ipython this is exactly the same
222 226 thing as 'quit'."""
223 227 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
224 228 return self.do_quit(arg)
225 229
226 230 def postloop(self):
227 231 __IPYTHON__.set_completer_frame(None)
228 232
229 233 def print_stack_trace(self):
230 234 try:
231 235 for frame_lineno in self.stack:
232 236 self.print_stack_entry(frame_lineno, context = 5)
233 237 except KeyboardInterrupt:
234 238 pass
235 239
236 240 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
237 241 context = 3):
238 242 frame, lineno = frame_lineno
239 243 print >>Term.cout, self.format_stack_entry(frame_lineno, '', context)
240 244
241 245 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
242 246 import linecache, repr
243 247
244 248 ret = []
245 249
246 250 Colors = self.color_scheme_table.active_colors
247 251 ColorsNormal = Colors.Normal
248 252 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
249 253 tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
250 254 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
251 255 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
252 256 ColorsNormal)
253 257
254 258 frame, lineno = frame_lineno
255 259
256 260 return_value = ''
257 261 if '__return__' in frame.f_locals:
258 262 rv = frame.f_locals['__return__']
259 263 #return_value += '->'
260 264 return_value += repr.repr(rv) + '\n'
261 265 ret.append(return_value)
262 266
263 267 #s = filename + '(' + `lineno` + ')'
264 268 filename = self.canonic(frame.f_code.co_filename)
265 269 link = tpl_link % filename
266 270
267 271 if frame.f_code.co_name:
268 272 func = frame.f_code.co_name
269 273 else:
270 274 func = "<lambda>"
271 275
272 276 call = ''
273 277 if func != '?':
274 278 if '__args__' in frame.f_locals:
275 279 args = repr.repr(frame.f_locals['__args__'])
276 280 else:
277 281 args = '()'
278 282 call = tpl_call % (func, args)
279 283
280 284 # The level info should be generated in the same format pdb uses, to
281 285 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
282 286 ret.append('> %s(%s)%s\n' % (link,lineno,call))
283 287
284 288 start = lineno - 1 - context//2
285 289 lines = linecache.getlines(filename)
286 290 start = max(start, 0)
287 291 start = min(start, len(lines) - context)
288 292 lines = lines[start : start + context]
289 293
290 294 for i,line in enumerate(lines):
291 295 show_arrow = (start + 1 + i == lineno)
292 296 ret.append(self.__format_line(tpl_line_em, filename,
293 297 start + 1 + i, line,
294 298 arrow = show_arrow) )
295 299
296 300 return ''.join(ret)
297 301
298 302 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
299 303 bp_mark = ""
300 304 bp_mark_color = ""
301 305
302 306 bp = None
303 307 if lineno in self.get_file_breaks(filename):
304 308 bps = self.get_breaks(filename, lineno)
305 309 bp = bps[-1]
306 310
307 311 if bp:
308 312 Colors = self.color_scheme_table.active_colors
309 313 bp_mark = str(bp.number)
310 314 bp_mark_color = Colors.breakpoint_enabled
311 315 if not bp.enabled:
312 316 bp_mark_color = Colors.breakpoint_disabled
313 317
314 318 numbers_width = 7
315 319 if arrow:
316 320 # This is the line with the error
317 321 pad = numbers_width - len(str(lineno)) - len(bp_mark)
318 322 if pad >= 3:
319 323 marker = '-'*(pad-3) + '-> '
320 324 elif pad == 2:
321 325 marker = '> '
322 326 elif pad == 1:
323 327 marker = '>'
324 328 else:
325 329 marker = ''
326 330 num = '%s%s' % (marker, str(lineno))
327 331 line = tpl_line % (bp_mark_color + bp_mark, num, line)
328 332 else:
329 333 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
330 334 line = tpl_line % (bp_mark_color + bp_mark, num, line)
331 335
332 336 return line
333 337
334 338 def list_command_pydb(self, arg):
335 339 """List command to use if we have a newer pydb installed"""
336 340 filename, first, last = OldPdb.parse_list_cmd(self, arg)
337 341 if filename is not None:
338 342 self.print_list_lines(filename, first, last)
339 343
340 344 def print_list_lines(self, filename, first, last):
341 345 """The printing (as opposed to the parsing part of a 'list'
342 346 command."""
343 347 try:
344 348 Colors = self.color_scheme_table.active_colors
345 349 ColorsNormal = Colors.Normal
346 350 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
347 351 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
348 352 src = []
349 353 for lineno in range(first, last+1):
350 354 line = linecache.getline(filename, lineno)
351 355 if not line:
352 356 break
353 357
354 358 if lineno == self.curframe.f_lineno:
355 359 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
356 360 else:
357 361 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
358 362
359 363 src.append(line)
360 364 self.lineno = lineno
361 365
362 366 print >>Term.cout, ''.join(src)
363 367
364 368 except KeyboardInterrupt:
365 369 pass
366 370
367 371 def do_list(self, arg):
368 372 self.lastcmd = 'list'
369 373 last = None
370 374 if arg:
371 375 try:
372 376 x = eval(arg, {}, {})
373 377 if type(x) == type(()):
374 378 first, last = x
375 379 first = int(first)
376 380 last = int(last)
377 381 if last < first:
378 382 # Assume it's a count
379 383 last = first + last
380 384 else:
381 385 first = max(1, int(x) - 5)
382 386 except:
383 387 print '*** Error in argument:', `arg`
384 388 return
385 389 elif self.lineno is None:
386 390 first = max(1, self.curframe.f_lineno - 5)
387 391 else:
388 392 first = self.lineno + 1
389 393 if last is None:
390 394 last = first + 10
391 395 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
392 396
393 397 do_l = do_list
394 398
395 399 def do_pdef(self, arg):
396 400 """The debugger interface to magic_pdef"""
397 401 namespaces = [('Locals', self.curframe.f_locals),
398 402 ('Globals', self.curframe.f_globals)]
399 403 __IPYTHON__.magic_pdef(arg, namespaces=namespaces)
400 404
401 405 def do_pdoc(self, arg):
402 406 """The debugger interface to magic_pdoc"""
403 407 namespaces = [('Locals', self.curframe.f_locals),
404 408 ('Globals', self.curframe.f_globals)]
405 409 __IPYTHON__.magic_pdoc(arg, namespaces=namespaces)
406 410
407 411 def do_pinfo(self, arg):
408 412 """The debugger equivalant of ?obj"""
409 413 namespaces = [('Locals', self.curframe.f_locals),
410 414 ('Globals', self.curframe.f_globals)]
411 415 __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces)
General Comments 0
You need to be logged in to leave comments. Login now