##// END OF EJS Templates
Fix #8077 for non-quit pdb exits.
memeplex -
Show More
@@ -1,589 +1,587 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 18 #*****************************************************************************
19 19 #
20 20 # This file is licensed under the PSF license.
21 21 #
22 22 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 24 #
25 25 #
26 26 #*****************************************************************************
27 27 from __future__ import print_function
28 28
29 29 import bdb
30 30 import functools
31 31 import inspect
32 32 import linecache
33 33 import sys
34 34
35 35 from IPython import get_ipython
36 36 from IPython.utils import PyColorize, ulinecache
37 37 from IPython.utils import coloransi, io, py3compat
38 38 from IPython.core.excolors import exception_colors
39 39 from IPython.testing.skipdoctest import skip_doctest
40 40
41 41 # See if we can use pydb.
42 42 has_pydb = False
43 43 prompt = 'ipdb> '
44 44 #We have to check this directly from sys.argv, config struct not yet available
45 45 if '--pydb' in sys.argv:
46 46 try:
47 47 import pydb
48 48 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
49 49 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
50 50 # better protect against it.
51 51 has_pydb = True
52 52 except ImportError:
53 53 print("Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available")
54 54
55 55 if has_pydb:
56 56 from pydb import Pdb as OldPdb
57 57 #print "Using pydb for %run -d and post-mortem" #dbg
58 58 prompt = 'ipydb> '
59 59 else:
60 60 from pdb import Pdb as OldPdb
61 61
62 62 # Allow the set_trace code to operate outside of an ipython instance, even if
63 63 # it does so with some limitations. The rest of this support is implemented in
64 64 # the Tracer constructor.
65 65 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
66 66 """Exception hook which handles `BdbQuit` exceptions.
67 67
68 68 All other exceptions are processed using the `excepthook`
69 69 parameter.
70 70 """
71 71 if et==bdb.BdbQuit:
72 72 print('Exiting Debugger.')
73 73 elif excepthook is not None:
74 74 excepthook(et, ev, tb)
75 75 else:
76 76 # Backwards compatibility. Raise deprecation warning?
77 77 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
78 78
79 79 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
80 80 print('Exiting Debugger.')
81 81
82 82
83 83 class Tracer(object):
84 84 """Class for local debugging, similar to pdb.set_trace.
85 85
86 86 Instances of this class, when called, behave like pdb.set_trace, but
87 87 providing IPython's enhanced capabilities.
88 88
89 89 This is implemented as a class which must be initialized in your own code
90 90 and not as a standalone function because we need to detect at runtime
91 91 whether IPython is already active or not. That detection is done in the
92 92 constructor, ensuring that this code plays nicely with a running IPython,
93 93 while functioning acceptably (though with limitations) if outside of it.
94 94 """
95 95
96 96 @skip_doctest
97 97 def __init__(self,colors=None):
98 98 """Create a local debugger instance.
99 99
100 100 Parameters
101 101 ----------
102 102
103 103 colors : str, optional
104 104 The name of the color scheme to use, it must be one of IPython's
105 105 valid color schemes. If not given, the function will default to
106 106 the current IPython scheme when running inside IPython, and to
107 107 'NoColor' otherwise.
108 108
109 109 Examples
110 110 --------
111 111 ::
112 112
113 113 from IPython.core.debugger import Tracer; debug_here = Tracer()
114 114
115 115 Later in your code::
116 116
117 117 debug_here() # -> will open up the debugger at that point.
118 118
119 119 Once the debugger activates, you can use all of its regular commands to
120 120 step through code, set breakpoints, etc. See the pdb documentation
121 121 from the Python standard library for usage details.
122 122 """
123 123
124 124 ip = get_ipython()
125 125 if ip is None:
126 126 # Outside of ipython, we set our own exception hook manually
127 127 sys.excepthook = functools.partial(BdbQuit_excepthook,
128 128 excepthook=sys.excepthook)
129 129 def_colors = 'NoColor'
130 130 try:
131 131 # Limited tab completion support
132 132 import readline
133 133 readline.parse_and_bind('tab: complete')
134 134 except ImportError:
135 135 pass
136 136 else:
137 137 # In ipython, we use its custom exception handler mechanism
138 138 def_colors = ip.colors
139 139 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
140 140
141 141 if colors is None:
142 142 colors = def_colors
143 143
144 144 # The stdlib debugger internally uses a modified repr from the `repr`
145 145 # module, that limits the length of printed strings to a hardcoded
146 146 # limit of 30 characters. That much trimming is too aggressive, let's
147 147 # at least raise that limit to 80 chars, which should be enough for
148 148 # most interactive uses.
149 149 try:
150 150 try:
151 151 from reprlib import aRepr # Py 3
152 152 except ImportError:
153 153 from repr import aRepr # Py 2
154 154 aRepr.maxstring = 80
155 155 except:
156 156 # This is only a user-facing convenience, so any error we encounter
157 157 # here can be warned about but can be otherwise ignored. These
158 158 # printouts will tell us about problems if this API changes
159 159 import traceback
160 160 traceback.print_exc()
161 161
162 162 self.debugger = Pdb(colors)
163 163
164 164 def __call__(self):
165 165 """Starts an interactive debugger at the point where called.
166 166
167 167 This is similar to the pdb.set_trace() function from the std lib, but
168 168 using IPython's enhanced debugger."""
169 169
170 170 self.debugger.set_trace(sys._getframe().f_back)
171 171
172 172
173 173 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
174 174 """Make new_fn have old_fn's doc string. This is particularly useful
175 175 for the ``do_...`` commands that hook into the help system.
176 176 Adapted from from a comp.lang.python posting
177 177 by Duncan Booth."""
178 178 def wrapper(*args, **kw):
179 179 return new_fn(*args, **kw)
180 180 if old_fn.__doc__:
181 181 wrapper.__doc__ = old_fn.__doc__ + additional_text
182 182 return wrapper
183 183
184 184
185 185 def _file_lines(fname):
186 186 """Return the contents of a named file as a list of lines.
187 187
188 188 This function never raises an IOError exception: if the file can't be
189 189 read, it simply returns an empty list."""
190 190
191 191 try:
192 192 outfile = open(fname)
193 193 except IOError:
194 194 return []
195 195 else:
196 196 out = outfile.readlines()
197 197 outfile.close()
198 198 return out
199 199
200 200
201 201 class Pdb(OldPdb):
202 202 """Modified Pdb class, does not load readline."""
203 203
204 204 def __init__(self,color_scheme='NoColor',completekey=None,
205 205 stdin=None, stdout=None):
206 206
207 207 # Parent constructor:
208 208 if has_pydb and completekey is None:
209 209 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
210 210 else:
211 211 OldPdb.__init__(self,completekey,stdin,stdout)
212 212
213 213 # IPython changes...
214 214 self.is_pydb = has_pydb
215 215
216 216 self.shell = get_ipython()
217 217
218 218 if self.shell is None:
219 219 # No IPython instance running, we must create one
220 220 from IPython.terminal.interactiveshell import \
221 221 TerminalInteractiveShell
222 222 self.shell = TerminalInteractiveShell.instance()
223 223
224 224 if self.is_pydb:
225 225
226 226 # interactiveshell.py's ipalias seems to want pdb's checkline
227 227 # which located in pydb.fn
228 228 import pydb.fns
229 229 self.checkline = lambda filename, lineno: \
230 230 pydb.fns.checkline(self, filename, lineno)
231 231
232 232 self.curframe = None
233 233 self.do_restart = self.new_do_restart
234 234
235 235 self.old_all_completions = self.shell.Completer.all_completions
236 236 self.shell.Completer.all_completions=self.all_completions
237 237
238 238 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
239 239 OldPdb.do_list)
240 240 self.do_l = self.do_list
241 241 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
242 242 OldPdb.do_frame)
243 243
244 244 self.aliases = {}
245 245
246 246 # Create color table: we copy the default one from the traceback
247 247 # module and add a few attributes needed for debugging
248 248 self.color_scheme_table = exception_colors()
249 249
250 250 # shorthands
251 251 C = coloransi.TermColors
252 252 cst = self.color_scheme_table
253 253
254 254 cst['NoColor'].colors.prompt = C.NoColor
255 255 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
256 256 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
257 257
258 258 cst['Linux'].colors.prompt = C.Green
259 259 cst['Linux'].colors.breakpoint_enabled = C.LightRed
260 260 cst['Linux'].colors.breakpoint_disabled = C.Red
261 261
262 262 cst['LightBG'].colors.prompt = C.Blue
263 263 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
264 264 cst['LightBG'].colors.breakpoint_disabled = C.Red
265 265
266 266 self.set_colors(color_scheme)
267 267
268 268 # Add a python parser so we can syntax highlight source while
269 269 # debugging.
270 270 self.parser = PyColorize.Parser()
271 271
272 272 # Set the prompt
273 273 Colors = cst.active_colors
274 274 self.prompt = u'%s%s%s' % (Colors.prompt, prompt, Colors.Normal) # The default prompt is '(Pdb)'
275 275
276 276 def set_colors(self, scheme):
277 277 """Shorthand access to the color table scheme selector method."""
278 278 self.color_scheme_table.set_active_scheme(scheme)
279 279
280 280 def interaction(self, frame, traceback):
281 281 self.shell.set_completer_frame(frame)
282 282 while True:
283 283 try:
284 284 OldPdb.interaction(self, frame, traceback)
285 break
285 286 except KeyboardInterrupt:
286 287 self.shell.write('\n' + self.shell.get_exception_only())
287 288 break
288 else:
289 break
289 finally:
290 # Pdb sets readline delimiters, so set them back to our own
291 self.shell.readline.set_completer_delims(self.shell.readline_delims)
290 292
291 293 def new_do_up(self, arg):
292 294 OldPdb.do_up(self, arg)
293 295 self.shell.set_completer_frame(self.curframe)
294 296 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
295 297
296 298 def new_do_down(self, arg):
297 299 OldPdb.do_down(self, arg)
298 300 self.shell.set_completer_frame(self.curframe)
299 301
300 302 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
301 303
302 304 def new_do_frame(self, arg):
303 305 OldPdb.do_frame(self, arg)
304 306 self.shell.set_completer_frame(self.curframe)
305 307
306 308 def new_do_quit(self, arg):
307 309
308 310 if hasattr(self, 'old_all_completions'):
309 311 self.shell.Completer.all_completions=self.old_all_completions
310 312
311 # Pdb sets readline delimiters, so set them back to our own
312 if self.shell.readline is not None:
313 self.shell.readline.set_completer_delims(self.shell.readline_delims)
314
315 313 return OldPdb.do_quit(self, arg)
316 314
317 315 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
318 316
319 317 def new_do_restart(self, arg):
320 318 """Restart command. In the context of ipython this is exactly the same
321 319 thing as 'quit'."""
322 320 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
323 321 return self.do_quit(arg)
324 322
325 323 def postloop(self):
326 324 self.shell.set_completer_frame(None)
327 325
328 326 def print_stack_trace(self):
329 327 try:
330 328 for frame_lineno in self.stack:
331 329 self.print_stack_entry(frame_lineno, context = 5)
332 330 except KeyboardInterrupt:
333 331 pass
334 332
335 333 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
336 334 context = 3):
337 335 #frame, lineno = frame_lineno
338 336 print(self.format_stack_entry(frame_lineno, '', context), file=io.stdout)
339 337
340 338 # vds: >>
341 339 frame, lineno = frame_lineno
342 340 filename = frame.f_code.co_filename
343 341 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
344 342 # vds: <<
345 343
346 344 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
347 345 try:
348 346 import reprlib # Py 3
349 347 except ImportError:
350 348 import repr as reprlib # Py 2
351 349
352 350 ret = []
353 351
354 352 Colors = self.color_scheme_table.active_colors
355 353 ColorsNormal = Colors.Normal
356 354 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
357 355 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
358 356 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
359 357 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
360 358 ColorsNormal)
361 359
362 360 frame, lineno = frame_lineno
363 361
364 362 return_value = ''
365 363 if '__return__' in frame.f_locals:
366 364 rv = frame.f_locals['__return__']
367 365 #return_value += '->'
368 366 return_value += reprlib.repr(rv) + '\n'
369 367 ret.append(return_value)
370 368
371 369 #s = filename + '(' + `lineno` + ')'
372 370 filename = self.canonic(frame.f_code.co_filename)
373 371 link = tpl_link % py3compat.cast_unicode(filename)
374 372
375 373 if frame.f_code.co_name:
376 374 func = frame.f_code.co_name
377 375 else:
378 376 func = "<lambda>"
379 377
380 378 call = ''
381 379 if func != '?':
382 380 if '__args__' in frame.f_locals:
383 381 args = reprlib.repr(frame.f_locals['__args__'])
384 382 else:
385 383 args = '()'
386 384 call = tpl_call % (func, args)
387 385
388 386 # The level info should be generated in the same format pdb uses, to
389 387 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
390 388 if frame is self.curframe:
391 389 ret.append('> ')
392 390 else:
393 391 ret.append(' ')
394 392 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
395 393
396 394 start = lineno - 1 - context//2
397 395 lines = ulinecache.getlines(filename)
398 396 start = min(start, len(lines) - context)
399 397 start = max(start, 0)
400 398 lines = lines[start : start + context]
401 399
402 400 for i,line in enumerate(lines):
403 401 show_arrow = (start + 1 + i == lineno)
404 402 linetpl = (frame is self.curframe or show_arrow) \
405 403 and tpl_line_em \
406 404 or tpl_line
407 405 ret.append(self.__format_line(linetpl, filename,
408 406 start + 1 + i, line,
409 407 arrow = show_arrow) )
410 408 return ''.join(ret)
411 409
412 410 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
413 411 bp_mark = ""
414 412 bp_mark_color = ""
415 413
416 414 scheme = self.color_scheme_table.active_scheme_name
417 415 new_line, err = self.parser.format2(line, 'str', scheme)
418 416 if not err: line = new_line
419 417
420 418 bp = None
421 419 if lineno in self.get_file_breaks(filename):
422 420 bps = self.get_breaks(filename, lineno)
423 421 bp = bps[-1]
424 422
425 423 if bp:
426 424 Colors = self.color_scheme_table.active_colors
427 425 bp_mark = str(bp.number)
428 426 bp_mark_color = Colors.breakpoint_enabled
429 427 if not bp.enabled:
430 428 bp_mark_color = Colors.breakpoint_disabled
431 429
432 430 numbers_width = 7
433 431 if arrow:
434 432 # This is the line with the error
435 433 pad = numbers_width - len(str(lineno)) - len(bp_mark)
436 434 if pad >= 3:
437 435 marker = '-'*(pad-3) + '-> '
438 436 elif pad == 2:
439 437 marker = '> '
440 438 elif pad == 1:
441 439 marker = '>'
442 440 else:
443 441 marker = ''
444 442 num = '%s%s' % (marker, str(lineno))
445 443 line = tpl_line % (bp_mark_color + bp_mark, num, line)
446 444 else:
447 445 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
448 446 line = tpl_line % (bp_mark_color + bp_mark, num, line)
449 447
450 448 return line
451 449
452 450 def list_command_pydb(self, arg):
453 451 """List command to use if we have a newer pydb installed"""
454 452 filename, first, last = OldPdb.parse_list_cmd(self, arg)
455 453 if filename is not None:
456 454 self.print_list_lines(filename, first, last)
457 455
458 456 def print_list_lines(self, filename, first, last):
459 457 """The printing (as opposed to the parsing part of a 'list'
460 458 command."""
461 459 try:
462 460 Colors = self.color_scheme_table.active_colors
463 461 ColorsNormal = Colors.Normal
464 462 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
465 463 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
466 464 src = []
467 465 if filename == "<string>" and hasattr(self, "_exec_filename"):
468 466 filename = self._exec_filename
469 467
470 468 for lineno in range(first, last+1):
471 469 line = ulinecache.getline(filename, lineno)
472 470 if not line:
473 471 break
474 472
475 473 if lineno == self.curframe.f_lineno:
476 474 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
477 475 else:
478 476 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
479 477
480 478 src.append(line)
481 479 self.lineno = lineno
482 480
483 481 print(''.join(src), file=io.stdout)
484 482
485 483 except KeyboardInterrupt:
486 484 pass
487 485
488 486 def do_list(self, arg):
489 487 self.lastcmd = 'list'
490 488 last = None
491 489 if arg:
492 490 try:
493 491 x = eval(arg, {}, {})
494 492 if type(x) == type(()):
495 493 first, last = x
496 494 first = int(first)
497 495 last = int(last)
498 496 if last < first:
499 497 # Assume it's a count
500 498 last = first + last
501 499 else:
502 500 first = max(1, int(x) - 5)
503 501 except:
504 502 print('*** Error in argument:', repr(arg))
505 503 return
506 504 elif self.lineno is None:
507 505 first = max(1, self.curframe.f_lineno - 5)
508 506 else:
509 507 first = self.lineno + 1
510 508 if last is None:
511 509 last = first + 10
512 510 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
513 511
514 512 # vds: >>
515 513 lineno = first
516 514 filename = self.curframe.f_code.co_filename
517 515 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
518 516 # vds: <<
519 517
520 518 do_l = do_list
521 519
522 520 def getsourcelines(self, obj):
523 521 lines, lineno = inspect.findsource(obj)
524 522 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
525 523 # must be a module frame: do not try to cut a block out of it
526 524 return lines, 1
527 525 elif inspect.ismodule(obj):
528 526 return lines, 1
529 527 return inspect.getblock(lines[lineno:]), lineno+1
530 528
531 529 def do_longlist(self, arg):
532 530 self.lastcmd = 'longlist'
533 531 filename = self.curframe.f_code.co_filename
534 532 breaklist = self.get_file_breaks(filename)
535 533 try:
536 534 lines, lineno = self.getsourcelines(self.curframe)
537 535 except OSError as err:
538 536 self.error(err)
539 537 return
540 538 last = lineno + len(lines)
541 539 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
542 540 do_ll = do_longlist
543 541
544 542 def do_pdef(self, arg):
545 543 """Print the call signature for any callable object.
546 544
547 545 The debugger interface to %pdef"""
548 546 namespaces = [('Locals', self.curframe.f_locals),
549 547 ('Globals', self.curframe.f_globals)]
550 548 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
551 549
552 550 def do_pdoc(self, arg):
553 551 """Print the docstring for an object.
554 552
555 553 The debugger interface to %pdoc."""
556 554 namespaces = [('Locals', self.curframe.f_locals),
557 555 ('Globals', self.curframe.f_globals)]
558 556 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
559 557
560 558 def do_pfile(self, arg):
561 559 """Print (or run through pager) the file where an object is defined.
562 560
563 561 The debugger interface to %pfile.
564 562 """
565 563 namespaces = [('Locals', self.curframe.f_locals),
566 564 ('Globals', self.curframe.f_globals)]
567 565 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
568 566
569 567 def do_pinfo(self, arg):
570 568 """Provide detailed information about an object.
571 569
572 570 The debugger interface to %pinfo, i.e., obj?."""
573 571 namespaces = [('Locals', self.curframe.f_locals),
574 572 ('Globals', self.curframe.f_globals)]
575 573 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
576 574
577 575 def do_pinfo2(self, arg):
578 576 """Provide extra detailed information about an object.
579 577
580 578 The debugger interface to %pinfo2, i.e., obj??."""
581 579 namespaces = [('Locals', self.curframe.f_locals),
582 580 ('Globals', self.curframe.f_globals)]
583 581 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
584 582
585 583 def do_psource(self, arg):
586 584 """Print (or run through pager) the source code for an object."""
587 585 namespaces = [('Locals', self.curframe.f_locals),
588 586 ('Globals', self.curframe.f_globals)]
589 587 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
General Comments 0
You need to be logged in to leave comments. Login now