##// 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 import sys
33 import sys
34 import warnings
34 import warnings
35 import re
35 import re
36 import os
36
37
37 from IPython import get_ipython
38 from IPython import get_ipython
38 from IPython.utils import PyColorize
39 from IPython.utils import PyColorize
@@ -282,6 +283,9 b' class Pdb(OldPdb):'
282 self.prompt = prompt
283 self.prompt = prompt
283 self.skip_hidden = True
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 def set_colors(self, scheme):
289 def set_colors(self, scheme):
286 """Shorthand access to the color table scheme selector method."""
290 """Shorthand access to the color table scheme selector method."""
287 self.color_scheme_table.set_active_scheme(scheme)
291 self.color_scheme_table.set_active_scheme(scheme)
@@ -293,6 +297,26 b' class Pdb(OldPdb):'
293 self.initial_frame = frame
297 self.initial_frame = frame
294 return super().set_trace(frame)
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 def hidden_frames(self, stack):
320 def hidden_frames(self, stack):
297 """
321 """
298 Given an index in the stack return wether it should be skipped.
322 Given an index in the stack return wether it should be skipped.
@@ -303,14 +327,9 b' class Pdb(OldPdb):'
303 # locals whenever the .f_locals accessor is called, so we
327 # locals whenever the .f_locals accessor is called, so we
304 # avoid calling it here to preserve self.curframe_locals.
328 # avoid calling it here to preserve self.curframe_locals.
305 # Futhermore, there is no good reason to hide the current frame.
329 # Futhermore, there is no good reason to hide the current frame.
306 ip_hide = [
330 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
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 ]
312 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
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 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
333 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
315 return ip_hide
334 return ip_hide
316
335
@@ -521,15 +540,64 b' class Pdb(OldPdb):'
521 except KeyboardInterrupt:
540 except KeyboardInterrupt:
522 pass
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 def do_skip_hidden(self, arg):
584 def do_skip_hidden(self, arg):
525 """
585 """
526 Change whether or not we should skip frames with the
586 Change whether or not we should skip frames with the
527 __tracebackhide__ attribute.
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 self.skip_hidden = True
594 self.skip_hidden = True
531 elif arg.strip().lower() in ("false", "no"):
595 elif arg.strip().lower() in ("false", "no"):
532 self.skip_hidden = False
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 def do_list(self, arg):
602 def do_list(self, arg):
535 """Print lines of code from the current stack frame
603 """Print lines of code from the current stack frame
@@ -694,9 +762,12 b' class Pdb(OldPdb):'
694 """Check if pdb should stop here"""
762 """Check if pdb should stop here"""
695 if not super().stop_here(frame):
763 if not super().stop_here(frame):
696 return False
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 if self._wait_for_mainpyfile:
768 if self._wait_for_mainpyfile:
699 return False
769 return False
770 if hidden:
700 Colors = self.color_scheme_table.active_colors
771 Colors = self.color_scheme_table.active_colors
701 ColorsNormal = Colors.Normal
772 ColorsNormal = Colors.Normal
702 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
773 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
@@ -710,8 +781,8 b' class Pdb(OldPdb):'
710
781
711 Will skip hidden frames.
782 Will skip hidden frames.
712 """
783 """
713 ## modified version of upstream that skips
784 # modified version of upstream that skips
714 # frames with __tracebackide__
785 # frames with __tracebackhide__
715 if self.curindex == 0:
786 if self.curindex == 0:
716 self.error("Oldest frame")
787 self.error("Oldest frame")
717 return
788 return
@@ -2,6 +2,50 b''
2 7.x Series
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 .. _version 7.23:
49 .. _version 7.23:
6
50
7 IPython 7.23
51 IPython 7.23
General Comments 0
You need to be logged in to leave comments. Login now