##// END OF EJS Templates
Backport PR #13264: f_locals might not have get method
Matthias Bussonnier -
Show More
@@ -1,1098 +1,1099 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Pdb debugger class.
4 4
5 5
6 6 This is an extension to PDB which adds a number of new features.
7 7 Note that there is also the `IPython.terminal.debugger` class which provides UI
8 8 improvements.
9 9
10 10 We also strongly recommend to use this via the `ipdb` package, which provides
11 11 extra configuration options.
12 12
13 13 Among other things, this subclass of PDB:
14 14 - supports many IPython magics like pdef/psource
15 15 - hide frames in tracebacks based on `__tracebackhide__`
16 16 - allows to skip frames based on `__debuggerskip__`
17 17
18 18 The skipping and hiding frames are configurable via the `skip_predicates`
19 19 command.
20 20
21 21 By default, frames from readonly files will be hidden, frames containing
22 22 ``__tracebackhide__=True`` will be hidden.
23 23
24 24 Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
25 25 frames value of ``__debuggerskip__`` is ``True`` will be skipped.
26 26
27 27 >>> def helpers_helper():
28 28 ... pass
29 29 ...
30 30 ... def helper_1():
31 31 ... print("don't step in me")
32 32 ... helpers_helpers() # will be stepped over unless breakpoint set.
33 33 ...
34 34 ...
35 35 ... def helper_2():
36 36 ... print("in me neither")
37 37 ...
38 38
39 39 One can define a decorator that wraps a function between the two helpers:
40 40
41 41 >>> def pdb_skipped_decorator(function):
42 42 ...
43 43 ...
44 44 ... def wrapped_fn(*args, **kwargs):
45 45 ... __debuggerskip__ = True
46 46 ... helper_1()
47 47 ... __debuggerskip__ = False
48 48 ... result = function(*args, **kwargs)
49 49 ... __debuggerskip__ = True
50 50 ... helper_2()
51 51 ... # setting __debuggerskip__ to False again is not necessary
52 52 ... return result
53 53 ...
54 54 ... return wrapped_fn
55 55
56 56 When decorating a function, ipdb will directly step into ``bar()`` by
57 57 default:
58 58
59 59 >>> @foo_decorator
60 60 ... def bar(x, y):
61 61 ... return x * y
62 62
63 63
64 64 You can toggle the behavior with
65 65
66 66 ipdb> skip_predicates debuggerskip false
67 67
68 68 or configure it in your ``.pdbrc``
69 69
70 70
71 71
72 72 Licencse
73 73 --------
74 74
75 75 Modified from the standard pdb.Pdb class to avoid including readline, so that
76 76 the command line completion of other programs which include this isn't
77 77 damaged.
78 78
79 79 In the future, this class will be expanded with improvements over the standard
80 80 pdb.
81 81
82 82 The original code in this file is mainly lifted out of cmd.py in Python 2.2,
83 83 with minor changes. Licensing should therefore be under the standard Python
84 84 terms. For details on the PSF (Python Software Foundation) standard license,
85 85 see:
86 86
87 87 https://docs.python.org/2/license.html
88 88
89 89
90 90 All the changes since then are under the same license as IPython.
91 91
92 92 """
93 93
94 94 #*****************************************************************************
95 95 #
96 96 # This file is licensed under the PSF license.
97 97 #
98 98 # Copyright (C) 2001 Python Software Foundation, www.python.org
99 99 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
100 100 #
101 101 #
102 102 #*****************************************************************************
103 103
104 104 import bdb
105 105 import functools
106 106 import inspect
107 107 import linecache
108 108 import sys
109 109 import warnings
110 110 import re
111 111 import os
112 112
113 113 from IPython import get_ipython
114 114 from IPython.utils import PyColorize
115 115 from IPython.utils import coloransi, py3compat
116 116 from IPython.core.excolors import exception_colors
117 117 from IPython.testing.skipdoctest import skip_doctest
118 118
119 119
120 120 prompt = 'ipdb> '
121 121
122 122 #We have to check this directly from sys.argv, config struct not yet available
123 123 from pdb import Pdb as OldPdb
124 124
125 125 # Allow the set_trace code to operate outside of an ipython instance, even if
126 126 # it does so with some limitations. The rest of this support is implemented in
127 127 # the Tracer constructor.
128 128
129 129 DEBUGGERSKIP = "__debuggerskip__"
130 130
131 131
132 132 def make_arrow(pad):
133 133 """generate the leading arrow in front of traceback or debugger"""
134 134 if pad >= 2:
135 135 return '-'*(pad-2) + '> '
136 136 elif pad == 1:
137 137 return '>'
138 138 return ''
139 139
140 140
141 141 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
142 142 """Exception hook which handles `BdbQuit` exceptions.
143 143
144 144 All other exceptions are processed using the `excepthook`
145 145 parameter.
146 146 """
147 147 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
148 148 DeprecationWarning, stacklevel=2)
149 149 if et==bdb.BdbQuit:
150 150 print('Exiting Debugger.')
151 151 elif excepthook is not None:
152 152 excepthook(et, ev, tb)
153 153 else:
154 154 # Backwards compatibility. Raise deprecation warning?
155 155 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
156 156
157 157
158 158 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
159 159 warnings.warn(
160 160 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
161 161 DeprecationWarning, stacklevel=2)
162 162 print('Exiting Debugger.')
163 163
164 164
165 165 class Tracer(object):
166 166 """
167 167 DEPRECATED
168 168
169 169 Class for local debugging, similar to pdb.set_trace.
170 170
171 171 Instances of this class, when called, behave like pdb.set_trace, but
172 172 providing IPython's enhanced capabilities.
173 173
174 174 This is implemented as a class which must be initialized in your own code
175 175 and not as a standalone function because we need to detect at runtime
176 176 whether IPython is already active or not. That detection is done in the
177 177 constructor, ensuring that this code plays nicely with a running IPython,
178 178 while functioning acceptably (though with limitations) if outside of it.
179 179 """
180 180
181 181 @skip_doctest
182 182 def __init__(self, colors=None):
183 183 """
184 184 DEPRECATED
185 185
186 186 Create a local debugger instance.
187 187
188 188 Parameters
189 189 ----------
190 190
191 191 colors : str, optional
192 192 The name of the color scheme to use, it must be one of IPython's
193 193 valid color schemes. If not given, the function will default to
194 194 the current IPython scheme when running inside IPython, and to
195 195 'NoColor' otherwise.
196 196
197 197 Examples
198 198 --------
199 199 ::
200 200
201 201 from IPython.core.debugger import Tracer; debug_here = Tracer()
202 202
203 203 Later in your code::
204 204
205 205 debug_here() # -> will open up the debugger at that point.
206 206
207 207 Once the debugger activates, you can use all of its regular commands to
208 208 step through code, set breakpoints, etc. See the pdb documentation
209 209 from the Python standard library for usage details.
210 210 """
211 211 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
212 212 "`IPython.core.debugger.Pdb.set_trace()`",
213 213 DeprecationWarning, stacklevel=2)
214 214
215 215 ip = get_ipython()
216 216 if ip is None:
217 217 # Outside of ipython, we set our own exception hook manually
218 218 sys.excepthook = functools.partial(BdbQuit_excepthook,
219 219 excepthook=sys.excepthook)
220 220 def_colors = 'NoColor'
221 221 else:
222 222 # In ipython, we use its custom exception handler mechanism
223 223 def_colors = ip.colors
224 224 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
225 225
226 226 if colors is None:
227 227 colors = def_colors
228 228
229 229 # The stdlib debugger internally uses a modified repr from the `repr`
230 230 # module, that limits the length of printed strings to a hardcoded
231 231 # limit of 30 characters. That much trimming is too aggressive, let's
232 232 # at least raise that limit to 80 chars, which should be enough for
233 233 # most interactive uses.
234 234 try:
235 235 from reprlib import aRepr
236 236 aRepr.maxstring = 80
237 237 except:
238 238 # This is only a user-facing convenience, so any error we encounter
239 239 # here can be warned about but can be otherwise ignored. These
240 240 # printouts will tell us about problems if this API changes
241 241 import traceback
242 242 traceback.print_exc()
243 243
244 244 self.debugger = Pdb(colors)
245 245
246 246 def __call__(self):
247 247 """Starts an interactive debugger at the point where called.
248 248
249 249 This is similar to the pdb.set_trace() function from the std lib, but
250 250 using IPython's enhanced debugger."""
251 251
252 252 self.debugger.set_trace(sys._getframe().f_back)
253 253
254 254
255 255 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
256 256
257 257
258 258 def strip_indentation(multiline_string):
259 259 return RGX_EXTRA_INDENT.sub('', multiline_string)
260 260
261 261
262 262 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
263 263 """Make new_fn have old_fn's doc string. This is particularly useful
264 264 for the ``do_...`` commands that hook into the help system.
265 265 Adapted from from a comp.lang.python posting
266 266 by Duncan Booth."""
267 267 def wrapper(*args, **kw):
268 268 return new_fn(*args, **kw)
269 269 if old_fn.__doc__:
270 270 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
271 271 return wrapper
272 272
273 273
274 274 class Pdb(OldPdb):
275 275 """Modified Pdb class, does not load readline.
276 276
277 277 for a standalone version that uses prompt_toolkit, see
278 278 `IPython.terminal.debugger.TerminalPdb` and
279 279 `IPython.terminal.debugger.set_trace()`
280 280
281 281
282 282 This debugger can hide and skip frames that are tagged according to some predicates.
283 283 See the `skip_predicates` commands.
284 284
285 285 """
286 286
287 287 default_predicates = {
288 288 "tbhide": True,
289 289 "readonly": False,
290 290 "ipython_internal": True,
291 291 "debuggerskip": True,
292 292 }
293 293
294 294 def __init__(self, color_scheme=None, completekey=None,
295 295 stdin=None, stdout=None, context=5, **kwargs):
296 296 """Create a new IPython debugger.
297 297
298 298 Parameters
299 299 ----------
300 300 color_scheme : default None
301 301 Deprecated, do not use.
302 302 completekey : default None
303 303 Passed to pdb.Pdb.
304 304 stdin : default None
305 305 Passed to pdb.Pdb.
306 306 stdout : default None
307 307 Passed to pdb.Pdb.
308 308 context : int
309 309 Number of lines of source code context to show when
310 310 displaying stacktrace information.
311 311 **kwargs
312 312 Passed to pdb.Pdb.
313 313
314 314 Notes
315 315 -----
316 316 The possibilities are python version dependent, see the python
317 317 docs for more info.
318 318 """
319 319
320 320 # Parent constructor:
321 321 try:
322 322 self.context = int(context)
323 323 if self.context <= 0:
324 324 raise ValueError("Context must be a positive integer")
325 325 except (TypeError, ValueError):
326 326 raise ValueError("Context must be a positive integer")
327 327
328 328 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
329 329 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
330 330
331 331 # IPython changes...
332 332 self.shell = get_ipython()
333 333
334 334 if self.shell is None:
335 335 save_main = sys.modules['__main__']
336 336 # No IPython instance running, we must create one
337 337 from IPython.terminal.interactiveshell import \
338 338 TerminalInteractiveShell
339 339 self.shell = TerminalInteractiveShell.instance()
340 340 # needed by any code which calls __import__("__main__") after
341 341 # the debugger was entered. See also #9941.
342 342 sys.modules['__main__'] = save_main
343 343
344 344 if color_scheme is not None:
345 345 warnings.warn(
346 346 "The `color_scheme` argument is deprecated since version 5.1",
347 347 DeprecationWarning, stacklevel=2)
348 348 else:
349 349 color_scheme = self.shell.colors
350 350
351 351 self.aliases = {}
352 352
353 353 # Create color table: we copy the default one from the traceback
354 354 # module and add a few attributes needed for debugging
355 355 self.color_scheme_table = exception_colors()
356 356
357 357 # shorthands
358 358 C = coloransi.TermColors
359 359 cst = self.color_scheme_table
360 360
361 361 cst['NoColor'].colors.prompt = C.NoColor
362 362 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
363 363 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
364 364
365 365 cst['Linux'].colors.prompt = C.Green
366 366 cst['Linux'].colors.breakpoint_enabled = C.LightRed
367 367 cst['Linux'].colors.breakpoint_disabled = C.Red
368 368
369 369 cst['LightBG'].colors.prompt = C.Blue
370 370 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
371 371 cst['LightBG'].colors.breakpoint_disabled = C.Red
372 372
373 373 cst['Neutral'].colors.prompt = C.Blue
374 374 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
375 375 cst['Neutral'].colors.breakpoint_disabled = C.Red
376 376
377 377
378 378 # Add a python parser so we can syntax highlight source while
379 379 # debugging.
380 380 self.parser = PyColorize.Parser(style=color_scheme)
381 381 self.set_colors(color_scheme)
382 382
383 383 # Set the prompt - the default prompt is '(Pdb)'
384 384 self.prompt = prompt
385 385 self.skip_hidden = True
386 386 self.report_skipped = True
387 387
388 388 # list of predicates we use to skip frames
389 389 self._predicates = self.default_predicates
390 390
391 391 #
392 392 def set_colors(self, scheme):
393 393 """Shorthand access to the color table scheme selector method."""
394 394 self.color_scheme_table.set_active_scheme(scheme)
395 395 self.parser.style = scheme
396 396
397 397 def set_trace(self, frame=None):
398 398 if frame is None:
399 399 frame = sys._getframe().f_back
400 400 self.initial_frame = frame
401 401 return super().set_trace(frame)
402 402
403 403 def _hidden_predicate(self, frame):
404 404 """
405 405 Given a frame return whether it it should be hidden or not by IPython.
406 406 """
407 407
408 408 if self._predicates["readonly"]:
409 409 fname = frame.f_code.co_filename
410 410 # we need to check for file existence and interactively define
411 411 # function would otherwise appear as RO.
412 412 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
413 413 return True
414 414
415 415 if self._predicates["tbhide"]:
416 416 if frame in (self.curframe, getattr(self, "initial_frame", None)):
417 417 return False
418 else:
419 return self._get_frame_locals(frame).get("__tracebackhide__", False)
420
418 frame_locals = self._get_frame_locals(frame)
419 if "__tracebackhide__" not in frame_locals:
420 return False
421 return frame_locals["__tracebackhide__"]
421 422 return False
422 423
423 424 def hidden_frames(self, stack):
424 425 """
425 426 Given an index in the stack return wether it should be skipped.
426 427
427 428 This is used in up/down and where to skip frames.
428 429 """
429 430 # The f_locals dictionary is updated from the actual frame
430 431 # locals whenever the .f_locals accessor is called, so we
431 432 # avoid calling it here to preserve self.curframe_locals.
432 433 # Futhermore, there is no good reason to hide the current frame.
433 434 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
434 435 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
435 436 if ip_start and self._predicates["ipython_internal"]:
436 437 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
437 438 return ip_hide
438 439
439 440 def interaction(self, frame, traceback):
440 441 try:
441 442 OldPdb.interaction(self, frame, traceback)
442 443 except KeyboardInterrupt:
443 444 self.stdout.write("\n" + self.shell.get_exception_only())
444 445
445 446 def new_do_frame(self, arg):
446 447 OldPdb.do_frame(self, arg)
447 448
448 449 def new_do_quit(self, arg):
449 450
450 451 if hasattr(self, 'old_all_completions'):
451 452 self.shell.Completer.all_completions=self.old_all_completions
452 453
453 454 return OldPdb.do_quit(self, arg)
454 455
455 456 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
456 457
457 458 def new_do_restart(self, arg):
458 459 """Restart command. In the context of ipython this is exactly the same
459 460 thing as 'quit'."""
460 461 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
461 462 return self.do_quit(arg)
462 463
463 464 def print_stack_trace(self, context=None):
464 465 Colors = self.color_scheme_table.active_colors
465 466 ColorsNormal = Colors.Normal
466 467 if context is None:
467 468 context = self.context
468 469 try:
469 470 context=int(context)
470 471 if context <= 0:
471 472 raise ValueError("Context must be a positive integer")
472 473 except (TypeError, ValueError):
473 474 raise ValueError("Context must be a positive integer")
474 475 try:
475 476 skipped = 0
476 477 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
477 478 if hidden and self.skip_hidden:
478 479 skipped += 1
479 480 continue
480 481 if skipped:
481 482 print(
482 483 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
483 484 )
484 485 skipped = 0
485 486 self.print_stack_entry(frame_lineno, context=context)
486 487 if skipped:
487 488 print(
488 489 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
489 490 )
490 491 except KeyboardInterrupt:
491 492 pass
492 493
493 494 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
494 495 context=None):
495 496 if context is None:
496 497 context = self.context
497 498 try:
498 499 context=int(context)
499 500 if context <= 0:
500 501 raise ValueError("Context must be a positive integer")
501 502 except (TypeError, ValueError):
502 503 raise ValueError("Context must be a positive integer")
503 504 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
504 505
505 506 # vds: >>
506 507 frame, lineno = frame_lineno
507 508 filename = frame.f_code.co_filename
508 509 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
509 510 # vds: <<
510 511
511 512 def _get_frame_locals(self, frame):
512 513 """ "
513 514 Acessing f_local of current frame reset the namespace, so we want to avoid
514 515 that or the following can happend
515 516
516 517 ipdb> foo
517 518 "old"
518 519 ipdb> foo = "new"
519 520 ipdb> foo
520 521 "new"
521 522 ipdb> where
522 523 ipdb> foo
523 524 "old"
524 525
525 526 So if frame is self.current_frame we instead return self.curframe_locals
526 527
527 528 """
528 529 if frame is self.curframe:
529 530 return self.curframe_locals
530 531 else:
531 532 return frame.f_locals
532 533
533 534 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
534 535 if context is None:
535 536 context = self.context
536 537 try:
537 538 context=int(context)
538 539 if context <= 0:
539 540 print("Context must be a positive integer", file=self.stdout)
540 541 except (TypeError, ValueError):
541 542 print("Context must be a positive integer", file=self.stdout)
542 543 try:
543 544 import reprlib # Py 3
544 545 except ImportError:
545 546 import repr as reprlib # Py 2
546 547
547 548 ret = []
548 549
549 550 Colors = self.color_scheme_table.active_colors
550 551 ColorsNormal = Colors.Normal
551 552 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
552 553 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
553 554 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
554 555 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
555 556 ColorsNormal)
556 557
557 558 frame, lineno = frame_lineno
558 559
559 560 return_value = ''
560 561 loc_frame = self._get_frame_locals(frame)
561 562 if "__return__" in loc_frame:
562 563 rv = loc_frame["__return__"]
563 564 # return_value += '->'
564 565 return_value += reprlib.repr(rv) + "\n"
565 566 ret.append(return_value)
566 567
567 568 #s = filename + '(' + `lineno` + ')'
568 569 filename = self.canonic(frame.f_code.co_filename)
569 570 link = tpl_link % py3compat.cast_unicode(filename)
570 571
571 572 if frame.f_code.co_name:
572 573 func = frame.f_code.co_name
573 574 else:
574 575 func = "<lambda>"
575 576
576 577 call = ""
577 578 if func != "?":
578 579 if "__args__" in loc_frame:
579 580 args = reprlib.repr(loc_frame["__args__"])
580 581 else:
581 582 args = '()'
582 583 call = tpl_call % (func, args)
583 584
584 585 # The level info should be generated in the same format pdb uses, to
585 586 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
586 587 if frame is self.curframe:
587 588 ret.append('> ')
588 589 else:
589 590 ret.append(' ')
590 591 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
591 592
592 593 start = lineno - 1 - context//2
593 594 lines = linecache.getlines(filename)
594 595 start = min(start, len(lines) - context)
595 596 start = max(start, 0)
596 597 lines = lines[start : start + context]
597 598
598 599 for i,line in enumerate(lines):
599 600 show_arrow = (start + 1 + i == lineno)
600 601 linetpl = (frame is self.curframe or show_arrow) \
601 602 and tpl_line_em \
602 603 or tpl_line
603 604 ret.append(self.__format_line(linetpl, filename,
604 605 start + 1 + i, line,
605 606 arrow = show_arrow) )
606 607 return ''.join(ret)
607 608
608 609 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
609 610 bp_mark = ""
610 611 bp_mark_color = ""
611 612
612 613 new_line, err = self.parser.format2(line, 'str')
613 614 if not err:
614 615 line = new_line
615 616
616 617 bp = None
617 618 if lineno in self.get_file_breaks(filename):
618 619 bps = self.get_breaks(filename, lineno)
619 620 bp = bps[-1]
620 621
621 622 if bp:
622 623 Colors = self.color_scheme_table.active_colors
623 624 bp_mark = str(bp.number)
624 625 bp_mark_color = Colors.breakpoint_enabled
625 626 if not bp.enabled:
626 627 bp_mark_color = Colors.breakpoint_disabled
627 628
628 629 numbers_width = 7
629 630 if arrow:
630 631 # This is the line with the error
631 632 pad = numbers_width - len(str(lineno)) - len(bp_mark)
632 633 num = '%s%s' % (make_arrow(pad), str(lineno))
633 634 else:
634 635 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
635 636
636 637 return tpl_line % (bp_mark_color + bp_mark, num, line)
637 638
638 639
639 640 def print_list_lines(self, filename, first, last):
640 641 """The printing (as opposed to the parsing part of a 'list'
641 642 command."""
642 643 try:
643 644 Colors = self.color_scheme_table.active_colors
644 645 ColorsNormal = Colors.Normal
645 646 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
646 647 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
647 648 src = []
648 649 if filename == "<string>" and hasattr(self, "_exec_filename"):
649 650 filename = self._exec_filename
650 651
651 652 for lineno in range(first, last+1):
652 653 line = linecache.getline(filename, lineno)
653 654 if not line:
654 655 break
655 656
656 657 if lineno == self.curframe.f_lineno:
657 658 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
658 659 else:
659 660 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
660 661
661 662 src.append(line)
662 663 self.lineno = lineno
663 664
664 665 print(''.join(src), file=self.stdout)
665 666
666 667 except KeyboardInterrupt:
667 668 pass
668 669
669 670 def do_skip_predicates(self, args):
670 671 """
671 672 Turn on/off individual predicates as to whether a frame should be hidden/skip.
672 673
673 674 The global option to skip (or not) hidden frames is set with skip_hidden
674 675
675 676 To change the value of a predicate
676 677
677 678 skip_predicates key [true|false]
678 679
679 680 Call without arguments to see the current values.
680 681
681 682 To permanently change the value of an option add the corresponding
682 683 command to your ``~/.pdbrc`` file. If you are programmatically using the
683 684 Pdb instance you can also change the ``default_predicates`` class
684 685 attribute.
685 686 """
686 687 if not args.strip():
687 688 print("current predicates:")
688 689 for (p, v) in self._predicates.items():
689 690 print(" ", p, ":", v)
690 691 return
691 692 type_value = args.strip().split(" ")
692 693 if len(type_value) != 2:
693 694 print(
694 695 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
695 696 )
696 697 return
697 698
698 699 type_, value = type_value
699 700 if type_ not in self._predicates:
700 701 print(f"{type_!r} not in {set(self._predicates.keys())}")
701 702 return
702 703 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
703 704 print(
704 705 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
705 706 )
706 707 return
707 708
708 709 self._predicates[type_] = value.lower() in ("true", "yes", "1")
709 710 if not any(self._predicates.values()):
710 711 print(
711 712 "Warning, all predicates set to False, skip_hidden may not have any effects."
712 713 )
713 714
714 715 def do_skip_hidden(self, arg):
715 716 """
716 717 Change whether or not we should skip frames with the
717 718 __tracebackhide__ attribute.
718 719 """
719 720 if not arg.strip():
720 721 print(
721 722 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
722 723 )
723 724 elif arg.strip().lower() in ("true", "yes"):
724 725 self.skip_hidden = True
725 726 elif arg.strip().lower() in ("false", "no"):
726 727 self.skip_hidden = False
727 728 if not any(self._predicates.values()):
728 729 print(
729 730 "Warning, all predicates set to False, skip_hidden may not have any effects."
730 731 )
731 732
732 733 def do_list(self, arg):
733 734 """Print lines of code from the current stack frame
734 735 """
735 736 self.lastcmd = 'list'
736 737 last = None
737 738 if arg:
738 739 try:
739 740 x = eval(arg, {}, {})
740 741 if type(x) == type(()):
741 742 first, last = x
742 743 first = int(first)
743 744 last = int(last)
744 745 if last < first:
745 746 # Assume it's a count
746 747 last = first + last
747 748 else:
748 749 first = max(1, int(x) - 5)
749 750 except:
750 751 print('*** Error in argument:', repr(arg), file=self.stdout)
751 752 return
752 753 elif self.lineno is None:
753 754 first = max(1, self.curframe.f_lineno - 5)
754 755 else:
755 756 first = self.lineno + 1
756 757 if last is None:
757 758 last = first + 10
758 759 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
759 760
760 761 # vds: >>
761 762 lineno = first
762 763 filename = self.curframe.f_code.co_filename
763 764 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
764 765 # vds: <<
765 766
766 767 do_l = do_list
767 768
768 769 def getsourcelines(self, obj):
769 770 lines, lineno = inspect.findsource(obj)
770 771 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
771 772 # must be a module frame: do not try to cut a block out of it
772 773 return lines, 1
773 774 elif inspect.ismodule(obj):
774 775 return lines, 1
775 776 return inspect.getblock(lines[lineno:]), lineno+1
776 777
777 778 def do_longlist(self, arg):
778 779 """Print lines of code from the current stack frame.
779 780
780 781 Shows more lines than 'list' does.
781 782 """
782 783 self.lastcmd = 'longlist'
783 784 try:
784 785 lines, lineno = self.getsourcelines(self.curframe)
785 786 except OSError as err:
786 787 self.error(err)
787 788 return
788 789 last = lineno + len(lines)
789 790 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
790 791 do_ll = do_longlist
791 792
792 793 def do_debug(self, arg):
793 794 """debug code
794 795 Enter a recursive debugger that steps through the code
795 796 argument (which is an arbitrary expression or statement to be
796 797 executed in the current environment).
797 798 """
798 799 trace_function = sys.gettrace()
799 800 sys.settrace(None)
800 801 globals = self.curframe.f_globals
801 802 locals = self.curframe_locals
802 803 p = self.__class__(completekey=self.completekey,
803 804 stdin=self.stdin, stdout=self.stdout)
804 805 p.use_rawinput = self.use_rawinput
805 806 p.prompt = "(%s) " % self.prompt.strip()
806 807 self.message("ENTERING RECURSIVE DEBUGGER")
807 808 sys.call_tracing(p.run, (arg, globals, locals))
808 809 self.message("LEAVING RECURSIVE DEBUGGER")
809 810 sys.settrace(trace_function)
810 811 self.lastcmd = p.lastcmd
811 812
812 813 def do_pdef(self, arg):
813 814 """Print the call signature for any callable object.
814 815
815 816 The debugger interface to %pdef"""
816 817 namespaces = [
817 818 ("Locals", self.curframe_locals),
818 819 ("Globals", self.curframe.f_globals),
819 820 ]
820 821 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
821 822
822 823 def do_pdoc(self, arg):
823 824 """Print the docstring for an object.
824 825
825 826 The debugger interface to %pdoc."""
826 827 namespaces = [
827 828 ("Locals", self.curframe_locals),
828 829 ("Globals", self.curframe.f_globals),
829 830 ]
830 831 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
831 832
832 833 def do_pfile(self, arg):
833 834 """Print (or run through pager) the file where an object is defined.
834 835
835 836 The debugger interface to %pfile.
836 837 """
837 838 namespaces = [
838 839 ("Locals", self.curframe_locals),
839 840 ("Globals", self.curframe.f_globals),
840 841 ]
841 842 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
842 843
843 844 def do_pinfo(self, arg):
844 845 """Provide detailed information about an object.
845 846
846 847 The debugger interface to %pinfo, i.e., obj?."""
847 848 namespaces = [
848 849 ("Locals", self.curframe_locals),
849 850 ("Globals", self.curframe.f_globals),
850 851 ]
851 852 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
852 853
853 854 def do_pinfo2(self, arg):
854 855 """Provide extra detailed information about an object.
855 856
856 857 The debugger interface to %pinfo2, i.e., obj??."""
857 858 namespaces = [
858 859 ("Locals", self.curframe_locals),
859 860 ("Globals", self.curframe.f_globals),
860 861 ]
861 862 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
862 863
863 864 def do_psource(self, arg):
864 865 """Print (or run through pager) the source code for an object."""
865 866 namespaces = [
866 867 ("Locals", self.curframe_locals),
867 868 ("Globals", self.curframe.f_globals),
868 869 ]
869 870 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
870 871
871 872 def do_where(self, arg):
872 873 """w(here)
873 874 Print a stack trace, with the most recent frame at the bottom.
874 875 An arrow indicates the "current frame", which determines the
875 876 context of most commands. 'bt' is an alias for this command.
876 877
877 878 Take a number as argument as an (optional) number of context line to
878 879 print"""
879 880 if arg:
880 881 try:
881 882 context = int(arg)
882 883 except ValueError as err:
883 884 self.error(err)
884 885 return
885 886 self.print_stack_trace(context)
886 887 else:
887 888 self.print_stack_trace()
888 889
889 890 do_w = do_where
890 891
891 892 def break_anywhere(self, frame):
892 893 """
893 894
894 895 _stop_in_decorator_internals is overly restrictive, as we may still want
895 896 to trace function calls, so we need to also update break_anywhere so
896 897 that is we don't `stop_here`, because of debugger skip, we may still
897 898 stop at any point inside the function
898 899
899 900 """
900 901
901 902 sup = super().break_anywhere(frame)
902 903 if sup:
903 904 return sup
904 905 if self._predicates["debuggerskip"]:
905 906 if DEBUGGERSKIP in frame.f_code.co_varnames:
906 907 return True
907 908 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
908 909 return True
909 910 return False
910 911
911 912 @skip_doctest
912 913 def _is_in_decorator_internal_and_should_skip(self, frame):
913 914 """
914 915 Utility to tell us whether we are in a decorator internal and should stop.
915 916
916 917
917 918
918 919 """
919 920
920 921 # if we are disabled don't skip
921 922 if not self._predicates["debuggerskip"]:
922 923 return False
923 924
924 925 # if frame is tagged, skip by default.
925 926 if DEBUGGERSKIP in frame.f_code.co_varnames:
926 927 return True
927 928
928 929 # if one of the parent frame value set to True skip as well.
929 930
930 931 cframe = frame
931 932 while getattr(cframe, "f_back", None):
932 933 cframe = cframe.f_back
933 934 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
934 935 return True
935 936
936 937 return False
937 938
938 939 def stop_here(self, frame):
939 940 """Check if pdb should stop here"""
940 941 if not super().stop_here(frame):
941 942 return False
942 943
943 944 if self._is_in_decorator_internal_and_should_skip(frame) is True:
944 945 return False
945 946
946 947 hidden = False
947 948 if self.skip_hidden:
948 949 hidden = self._hidden_predicate(frame)
949 950 if hidden:
950 951 if self.report_skipped:
951 952 Colors = self.color_scheme_table.active_colors
952 953 ColorsNormal = Colors.Normal
953 954 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
954 955 return False
955 956 return True
956 957
957 958 def do_up(self, arg):
958 959 """u(p) [count]
959 960 Move the current frame count (default one) levels up in the
960 961 stack trace (to an older frame).
961 962
962 963 Will skip hidden frames.
963 964 """
964 965 # modified version of upstream that skips
965 966 # frames with __tracebackhide__
966 967 if self.curindex == 0:
967 968 self.error("Oldest frame")
968 969 return
969 970 try:
970 971 count = int(arg or 1)
971 972 except ValueError:
972 973 self.error("Invalid frame count (%s)" % arg)
973 974 return
974 975 skipped = 0
975 976 if count < 0:
976 977 _newframe = 0
977 978 else:
978 979 _newindex = self.curindex
979 980 counter = 0
980 981 hidden_frames = self.hidden_frames(self.stack)
981 982 for i in range(self.curindex - 1, -1, -1):
982 983 frame = self.stack[i][0]
983 984 if hidden_frames[i] and self.skip_hidden:
984 985 skipped += 1
985 986 continue
986 987 counter += 1
987 988 if counter >= count:
988 989 break
989 990 else:
990 991 # if no break occured.
991 992 self.error("all frames above hidden")
992 993 return
993 994
994 995 Colors = self.color_scheme_table.active_colors
995 996 ColorsNormal = Colors.Normal
996 997 _newframe = i
997 998 self._select_frame(_newframe)
998 999 if skipped:
999 1000 print(
1000 1001 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1001 1002 )
1002 1003
1003 1004 def do_down(self, arg):
1004 1005 """d(own) [count]
1005 1006 Move the current frame count (default one) levels down in the
1006 1007 stack trace (to a newer frame).
1007 1008
1008 1009 Will skip hidden frames.
1009 1010 """
1010 1011 if self.curindex + 1 == len(self.stack):
1011 1012 self.error("Newest frame")
1012 1013 return
1013 1014 try:
1014 1015 count = int(arg or 1)
1015 1016 except ValueError:
1016 1017 self.error("Invalid frame count (%s)" % arg)
1017 1018 return
1018 1019 if count < 0:
1019 1020 _newframe = len(self.stack) - 1
1020 1021 else:
1021 1022 _newindex = self.curindex
1022 1023 counter = 0
1023 1024 skipped = 0
1024 1025 hidden_frames = self.hidden_frames(self.stack)
1025 1026 for i in range(self.curindex + 1, len(self.stack)):
1026 1027 frame = self.stack[i][0]
1027 1028 if hidden_frames[i] and self.skip_hidden:
1028 1029 skipped += 1
1029 1030 continue
1030 1031 counter += 1
1031 1032 if counter >= count:
1032 1033 break
1033 1034 else:
1034 1035 self.error("all frames bellow hidden")
1035 1036 return
1036 1037
1037 1038 Colors = self.color_scheme_table.active_colors
1038 1039 ColorsNormal = Colors.Normal
1039 1040 if skipped:
1040 1041 print(
1041 1042 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1042 1043 )
1043 1044 _newframe = i
1044 1045
1045 1046 self._select_frame(_newframe)
1046 1047
1047 1048 do_d = do_down
1048 1049 do_u = do_up
1049 1050
1050 1051 def do_context(self, context):
1051 1052 """context number_of_lines
1052 1053 Set the number of lines of source code to show when displaying
1053 1054 stacktrace information.
1054 1055 """
1055 1056 try:
1056 1057 new_context = int(context)
1057 1058 if new_context <= 0:
1058 1059 raise ValueError()
1059 1060 self.context = new_context
1060 1061 except ValueError:
1061 1062 self.error("The 'context' command requires a positive integer argument.")
1062 1063
1063 1064
1064 1065 class InterruptiblePdb(Pdb):
1065 1066 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
1066 1067
1067 1068 def cmdloop(self, intro=None):
1068 1069 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
1069 1070 try:
1070 1071 return OldPdb.cmdloop(self, intro=intro)
1071 1072 except KeyboardInterrupt:
1072 1073 self.stop_here = lambda frame: False
1073 1074 self.do_quit("")
1074 1075 sys.settrace(None)
1075 1076 self.quitting = False
1076 1077 raise
1077 1078
1078 1079 def _cmdloop(self):
1079 1080 while True:
1080 1081 try:
1081 1082 # keyboard interrupts allow for an easy way to cancel
1082 1083 # the current command, so allow them during interactive input
1083 1084 self.allow_kbdint = True
1084 1085 self.cmdloop()
1085 1086 self.allow_kbdint = False
1086 1087 break
1087 1088 except KeyboardInterrupt:
1088 1089 self.message('--KeyboardInterrupt--')
1089 1090 raise
1090 1091
1091 1092
1092 1093 def set_trace(frame=None):
1093 1094 """
1094 1095 Start debugging from `frame`.
1095 1096
1096 1097 If frame is not specified, debugging starts from caller's frame.
1097 1098 """
1098 1099 Pdb().set_trace(frame or sys._getframe().f_back)
General Comments 0
You need to be logged in to leave comments. Login now