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