##// END OF EJS Templates
Backport PR #12969: Implement more predicates to skip frames in debugger.
Matthias Bussonnier -
Show More
@@ -33,6 +33,7 b' import linecache'
33 33 import sys
34 34 import warnings
35 35 import re
36 import os
36 37
37 38 from IPython import get_ipython
38 39 from IPython.utils import PyColorize
@@ -282,6 +283,9 b' class Pdb(OldPdb):'
282 283 self.prompt = prompt
283 284 self.skip_hidden = True
284 285
286 # list of predicates we use to skip frames
287 self._predicates = {"tbhide": True, "readonly": True, "ipython_internal": True}
288
285 289 def set_colors(self, scheme):
286 290 """Shorthand access to the color table scheme selector method."""
287 291 self.color_scheme_table.set_active_scheme(scheme)
@@ -293,6 +297,26 b' class Pdb(OldPdb):'
293 297 self.initial_frame = frame
294 298 return super().set_trace(frame)
295 299
300 def _hidden_predicate(self, frame):
301 """
302 Given a frame return whether it it should be hidden or not by IPython.
303 """
304
305 if self._predicates["readonly"]:
306 fname = frame.f_code.co_filename
307 # we need to check for file existence and interactively define
308 # function would otherwise appear as RO.
309 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
310 return True
311
312 if self._predicates["tbhide"]:
313 if frame in (self.curframe, getattr(self, "initial_frame", None)):
314 return False
315 else:
316 return frame.f_locals.get("__tracebackhide__", False)
317
318 return False
319
296 320 def hidden_frames(self, stack):
297 321 """
298 322 Given an index in the stack return wether it should be skipped.
@@ -303,14 +327,9 b' class Pdb(OldPdb):'
303 327 # locals whenever the .f_locals accessor is called, so we
304 328 # avoid calling it here to preserve self.curframe_locals.
305 329 # Futhermore, there is no good reason to hide the current frame.
306 ip_hide = [
307 False
308 if s[0] in (self.curframe, getattr(self, "initial_frame", None))
309 else s[0].f_locals.get("__tracebackhide__", False)
310 for s in stack
311 ]
330 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
312 331 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
313 if ip_start:
332 if ip_start and self._predicates["ipython_internal"]:
314 333 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
315 334 return ip_hide
316 335
@@ -521,15 +540,64 b' class Pdb(OldPdb):'
521 540 except KeyboardInterrupt:
522 541 pass
523 542
543 def do_skip_predicates(self, args):
544 """
545 Turn on/off individual predicates as to whether a frame should be hidden/skip.
546
547 The global option to skip (or not) hidden frames is set with skip_hidden
548
549 To change the value of a predicate
550
551 skip_predicates key [true|false]
552
553 Call without arguments to see the current values.
554
555 """
556 if not args.strip():
557 print("current predicates:")
558 for (p, v) in self._predicates.items():
559 print(" ", p, ":", v)
560 return
561 type_value = args.strip().split(" ")
562 if len(type_value) != 2:
563 print(
564 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
565 )
566 return
567
568 type_, value = type_value
569 if type_ not in self._predicates:
570 print(f"{type_!r} not in {set(self._predicates.keys())}")
571 return
572 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
573 print(
574 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
575 )
576 return
577
578 self._predicates[type_] = value.lower() in ("true", "yes", "1")
579 if not any(self._predicates.values()):
580 print(
581 "Warning, all predicates set to False, skip_hidden may not have any effects."
582 )
583
524 584 def do_skip_hidden(self, arg):
525 585 """
526 586 Change whether or not we should skip frames with the
527 587 __tracebackhide__ attribute.
528 588 """
529 if arg.strip().lower() in ("true", "yes"):
589 if not arg.strip():
590 print(
591 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
592 )
593 elif arg.strip().lower() in ("true", "yes"):
530 594 self.skip_hidden = True
531 595 elif arg.strip().lower() in ("false", "no"):
532 596 self.skip_hidden = False
597 if not any(self._predicates.values()):
598 print(
599 "Warning, all predicates set to False, skip_hidden may not have any effects."
600 )
533 601
534 602 def do_list(self, arg):
535 603 """Print lines of code from the current stack frame
@@ -694,9 +762,12 b' class Pdb(OldPdb):'
694 762 """Check if pdb should stop here"""
695 763 if not super().stop_here(frame):
696 764 return False
697 if self.skip_hidden and frame.f_locals.get("__tracebackhide__", False):
765 hidden = False
766 if self.skip_hidden:
767 hidden = self._hidden_predicate(frame)
698 768 if self._wait_for_mainpyfile:
699 769 return False
770 if hidden:
700 771 Colors = self.color_scheme_table.active_colors
701 772 ColorsNormal = Colors.Normal
702 773 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
@@ -710,8 +781,8 b' class Pdb(OldPdb):'
710 781
711 782 Will skip hidden frames.
712 783 """
713 ## modified version of upstream that skips
714 # frames with __tracebackide__
784 # modified version of upstream that skips
785 # frames with __tracebackhide__
715 786 if self.curindex == 0:
716 787 self.error("Oldest frame")
717 788 return
@@ -2,6 +2,50 b''
2 2 7.x Series
3 3 ============
4 4
5
6 The debugger (and ``%debug`` magic) have been improved to skip and hide frames
7 originating from files that are not writable to the user, as these are less
8 likely to be the source of errors, or be part of system files.
9
10 In addition to the global ``skip_hidden True|False`` command, the debugger has
11 gained finer grained control of predicates as to whether to a frame should be
12 considered hidden. So far 3 predicates are available and activated by default:
13
14 - ``tbhide``: frames containing the local variable ``__tracebackhide__`` set to
15 True.
16 - ``readonly``: frames originating from readonly files.
17 - ``ipython_internal``: frames that are likely to be from IPython internal code.
18
19 You can toggle individual predicates during a session with
20
21 .. code-block::
22
23 ipdb> skip_predicates readonly False
24
25 Read-only files will not be considered hidden frames.
26
27
28 You can call ``skip_predicates`` without arguments to see the states of current
29 predicates:
30
31 .. code-block::
32
33 ipdb> skip_predicates
34 current predicates:
35 tbhide : True
36 readonly : True
37 ipython_internal : True
38
39 If all predicates are set to ``False``, ``skip_hidden`` will practically have
40 no effect. We attempt to warn you when all predicates are False.
41
42 Note that the ``readonly`` predicate may increase disk access as we check for
43 file access permission for all frames on many command invocation, but is usually
44 cached by operating system. Let us know if you encounter any issues.
45
46
47
48
5 49 .. _version 7.23:
6 50
7 51 IPython 7.23
General Comments 0
You need to be logged in to leave comments. Login now