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