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