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