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