##// END OF EJS Templates
allow keyboard interrupt to break out of ipdb...
Paul Ivanov -
Show More
@@ -1,590 +1,591 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 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("\nKeyboardInterrupt\n")
281 break
281 282 else:
282 283 break
283 284
284 285 def new_do_up(self, arg):
285 286 OldPdb.do_up(self, arg)
286 287 self.shell.set_completer_frame(self.curframe)
287 288 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
288 289
289 290 def new_do_down(self, arg):
290 291 OldPdb.do_down(self, arg)
291 292 self.shell.set_completer_frame(self.curframe)
292 293
293 294 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
294 295
295 296 def new_do_frame(self, arg):
296 297 OldPdb.do_frame(self, arg)
297 298 self.shell.set_completer_frame(self.curframe)
298 299
299 300 def new_do_quit(self, arg):
300 301
301 302 if hasattr(self, 'old_all_completions'):
302 303 self.shell.Completer.all_completions=self.old_all_completions
303 304
304 305
305 306 return OldPdb.do_quit(self, arg)
306 307
307 308 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
308 309
309 310 def new_do_restart(self, arg):
310 311 """Restart command. In the context of ipython this is exactly the same
311 312 thing as 'quit'."""
312 313 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
313 314 return self.do_quit(arg)
314 315
315 316 def postloop(self):
316 317 self.shell.set_completer_frame(None)
317 318
318 319 def print_stack_trace(self):
319 320 try:
320 321 for frame_lineno in self.stack:
321 322 self.print_stack_entry(frame_lineno, context = 5)
322 323 except KeyboardInterrupt:
323 324 pass
324 325
325 326 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
326 327 context = 3):
327 328 #frame, lineno = frame_lineno
328 329 print(self.format_stack_entry(frame_lineno, '', context), file=io.stdout)
329 330
330 331 # vds: >>
331 332 frame, lineno = frame_lineno
332 333 filename = frame.f_code.co_filename
333 334 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
334 335 # vds: <<
335 336
336 337 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
337 338 try:
338 339 import reprlib # Py 3
339 340 except ImportError:
340 341 import repr as reprlib # Py 2
341 342
342 343 ret = []
343 344
344 345 Colors = self.color_scheme_table.active_colors
345 346 ColorsNormal = Colors.Normal
346 347 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
347 348 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
348 349 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
349 350 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
350 351 ColorsNormal)
351 352
352 353 frame, lineno = frame_lineno
353 354
354 355 return_value = ''
355 356 if '__return__' in frame.f_locals:
356 357 rv = frame.f_locals['__return__']
357 358 #return_value += '->'
358 359 return_value += reprlib.repr(rv) + '\n'
359 360 ret.append(return_value)
360 361
361 362 #s = filename + '(' + `lineno` + ')'
362 363 filename = self.canonic(frame.f_code.co_filename)
363 364 link = tpl_link % py3compat.cast_unicode(filename)
364 365
365 366 if frame.f_code.co_name:
366 367 func = frame.f_code.co_name
367 368 else:
368 369 func = "<lambda>"
369 370
370 371 call = ''
371 372 if func != '?':
372 373 if '__args__' in frame.f_locals:
373 374 args = reprlib.repr(frame.f_locals['__args__'])
374 375 else:
375 376 args = '()'
376 377 call = tpl_call % (func, args)
377 378
378 379 # The level info should be generated in the same format pdb uses, to
379 380 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
380 381 if frame is self.curframe:
381 382 ret.append('> ')
382 383 else:
383 384 ret.append(' ')
384 385 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
385 386
386 387 start = lineno - 1 - context//2
387 388 lines = ulinecache.getlines(filename)
388 389 start = min(start, len(lines) - context)
389 390 start = max(start, 0)
390 391 lines = lines[start : start + context]
391 392
392 393 for i,line in enumerate(lines):
393 394 show_arrow = (start + 1 + i == lineno)
394 395 linetpl = (frame is self.curframe or show_arrow) \
395 396 and tpl_line_em \
396 397 or tpl_line
397 398 ret.append(self.__format_line(linetpl, filename,
398 399 start + 1 + i, line,
399 400 arrow = show_arrow) )
400 401 return ''.join(ret)
401 402
402 403 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
403 404 bp_mark = ""
404 405 bp_mark_color = ""
405 406
406 407 scheme = self.color_scheme_table.active_scheme_name
407 408 new_line, err = self.parser.format2(line, 'str', scheme)
408 409 if not err: line = new_line
409 410
410 411 bp = None
411 412 if lineno in self.get_file_breaks(filename):
412 413 bps = self.get_breaks(filename, lineno)
413 414 bp = bps[-1]
414 415
415 416 if bp:
416 417 Colors = self.color_scheme_table.active_colors
417 418 bp_mark = str(bp.number)
418 419 bp_mark_color = Colors.breakpoint_enabled
419 420 if not bp.enabled:
420 421 bp_mark_color = Colors.breakpoint_disabled
421 422
422 423 numbers_width = 7
423 424 if arrow:
424 425 # This is the line with the error
425 426 pad = numbers_width - len(str(lineno)) - len(bp_mark)
426 427 if pad >= 3:
427 428 marker = '-'*(pad-3) + '-> '
428 429 elif pad == 2:
429 430 marker = '> '
430 431 elif pad == 1:
431 432 marker = '>'
432 433 else:
433 434 marker = ''
434 435 num = '%s%s' % (marker, str(lineno))
435 436 line = tpl_line % (bp_mark_color + bp_mark, num, line)
436 437 else:
437 438 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
438 439 line = tpl_line % (bp_mark_color + bp_mark, num, line)
439 440
440 441 return line
441 442
442 443 def list_command_pydb(self, arg):
443 444 """List command to use if we have a newer pydb installed"""
444 445 filename, first, last = OldPdb.parse_list_cmd(self, arg)
445 446 if filename is not None:
446 447 self.print_list_lines(filename, first, last)
447 448
448 449 def print_list_lines(self, filename, first, last):
449 450 """The printing (as opposed to the parsing part of a 'list'
450 451 command."""
451 452 try:
452 453 Colors = self.color_scheme_table.active_colors
453 454 ColorsNormal = Colors.Normal
454 455 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
455 456 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
456 457 src = []
457 458 if filename == "<string>" and hasattr(self, "_exec_filename"):
458 459 filename = self._exec_filename
459 460
460 461 for lineno in range(first, last+1):
461 462 line = ulinecache.getline(filename, lineno)
462 463 if not line:
463 464 break
464 465
465 466 if lineno == self.curframe.f_lineno:
466 467 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
467 468 else:
468 469 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
469 470
470 471 src.append(line)
471 472 self.lineno = lineno
472 473
473 474 print(''.join(src), file=io.stdout)
474 475
475 476 except KeyboardInterrupt:
476 477 pass
477 478
478 479 def do_list(self, arg):
479 480 self.lastcmd = 'list'
480 481 last = None
481 482 if arg:
482 483 try:
483 484 x = eval(arg, {}, {})
484 485 if type(x) == type(()):
485 486 first, last = x
486 487 first = int(first)
487 488 last = int(last)
488 489 if last < first:
489 490 # Assume it's a count
490 491 last = first + last
491 492 else:
492 493 first = max(1, int(x) - 5)
493 494 except:
494 495 print('*** Error in argument:', repr(arg))
495 496 return
496 497 elif self.lineno is None:
497 498 first = max(1, self.curframe.f_lineno - 5)
498 499 else:
499 500 first = self.lineno + 1
500 501 if last is None:
501 502 last = first + 10
502 503 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
503 504
504 505 # vds: >>
505 506 lineno = first
506 507 filename = self.curframe.f_code.co_filename
507 508 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
508 509 # vds: <<
509 510
510 511 do_l = do_list
511 512
512 513 def do_pdef(self, arg):
513 514 """Print the call signature for any callable object.
514 515
515 516 The debugger interface to %pdef"""
516 517 namespaces = [('Locals', self.curframe.f_locals),
517 518 ('Globals', self.curframe.f_globals)]
518 519 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
519 520
520 521 def do_pdoc(self, arg):
521 522 """Print the docstring for an object.
522 523
523 524 The debugger interface to %pdoc."""
524 525 namespaces = [('Locals', self.curframe.f_locals),
525 526 ('Globals', self.curframe.f_globals)]
526 527 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
527 528
528 529 def do_pfile(self, arg):
529 530 """Print (or run through pager) the file where an object is defined.
530 531
531 532 The debugger interface to %pfile.
532 533 """
533 534 namespaces = [('Locals', self.curframe.f_locals),
534 535 ('Globals', self.curframe.f_globals)]
535 536 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
536 537
537 538 def do_pinfo(self, arg):
538 539 """Provide detailed information about an object.
539 540
540 541 The debugger interface to %pinfo, i.e., obj?."""
541 542 namespaces = [('Locals', self.curframe.f_locals),
542 543 ('Globals', self.curframe.f_globals)]
543 544 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
544 545
545 546 def do_pinfo2(self, arg):
546 547 """Provide extra detailed information about an object.
547 548
548 549 The debugger interface to %pinfo2, i.e., obj??."""
549 550 namespaces = [('Locals', self.curframe.f_locals),
550 551 ('Globals', self.curframe.f_globals)]
551 552 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
552 553
553 554 def do_psource(self, arg):
554 555 """Print (or run through pager) the source code for an object."""
555 556 namespaces = [('Locals', self.curframe.f_locals),
556 557 ('Globals', self.curframe.f_globals)]
557 558 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
558 559
559 560 def checkline(self, filename, lineno):
560 561 """Check whether specified line seems to be executable.
561 562
562 563 Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
563 564 line or EOF). Warning: testing is not comprehensive.
564 565 """
565 566 #######################################################################
566 567 # XXX Hack! Use python-2.5 compatible code for this call, because with
567 568 # all of our changes, we've drifted from the pdb api in 2.6. For now,
568 569 # changing:
569 570 #
570 571 #line = linecache.getline(filename, lineno, self.curframe.f_globals)
571 572 # to:
572 573 #
573 574 line = linecache.getline(filename, lineno)
574 575 #
575 576 # does the trick. But in reality, we need to fix this by reconciling
576 577 # our updates with the new Pdb APIs in Python 2.6.
577 578 #
578 579 # End hack. The rest of this method is copied verbatim from 2.6 pdb.py
579 580 #######################################################################
580 581
581 582 if not line:
582 583 print('End of file', file=self.stdout)
583 584 return 0
584 585 line = line.strip()
585 586 # Don't allow setting breakpoint at a blank line
586 587 if (not line or (line[0] == '#') or
587 588 (line[:3] == '"""') or line[:3] == "'''"):
588 589 print('*** Blank or comment', file=self.stdout)
589 590 return 0
590 591 return lineno
General Comments 0
You need to be logged in to leave comments. Login now