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