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