##// END OF EJS Templates
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
@@ -281,6 +282,9 b' class Pdb(OldPdb):'
281 self.prompt = prompt
282 self.prompt = prompt
282 self.skip_hidden = True
283 self.skip_hidden = True
283
284
285 # list of predicates we use to skip frames
286 self._predicates = {"tbhide": True, "readonly": True, "ipython_internal": True}
287
284 def set_colors(self, scheme):
288 def set_colors(self, scheme):
285 """Shorthand access to the color table scheme selector method."""
289 """Shorthand access to the color table scheme selector method."""
286 self.color_scheme_table.set_active_scheme(scheme)
290 self.color_scheme_table.set_active_scheme(scheme)
@@ -292,6 +296,26 b' class Pdb(OldPdb):'
292 self.initial_frame = frame
296 self.initial_frame = frame
293 return super().set_trace(frame)
297 return super().set_trace(frame)
294
298
299 def _hidden_predicate(self, frame):
300 """
301 Given a frame return whether it it should be hidden or not by IPython.
302 """
303
304 if self._predicates["readonly"]:
305 fname = frame.f_code.co_filename
306 # we need to check for file existence and interactively define
307 # function would otherwise appear as RO.
308 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
309 return True
310
311 if self._predicates["tbhide"]:
312 if frame in (self.curframe, getattr(self, "initial_frame", None)):
313 return False
314 else:
315 return frame.f_locals.get("__tracebackhide__", False)
316
317 return False
318
295 def hidden_frames(self, stack):
319 def hidden_frames(self, stack):
296 """
320 """
297 Given an index in the stack return whether it should be skipped.
321 Given an index in the stack return whether it should be skipped.
@@ -302,14 +326,9 b' class Pdb(OldPdb):'
302 # locals whenever the .f_locals accessor is called, so we
326 # locals whenever the .f_locals accessor is called, so we
303 # avoid calling it here to preserve self.curframe_locals.
327 # avoid calling it here to preserve self.curframe_locals.
304 # Futhermore, there is no good reason to hide the current frame.
328 # Futhermore, there is no good reason to hide the current frame.
305 ip_hide = [
329 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
306 False
307 if s[0] in (self.curframe, getattr(self, "initial_frame", None))
308 else s[0].f_locals.get("__tracebackhide__", False)
309 for s in stack
310 ]
311 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
330 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
312 if ip_start:
331 if ip_start and self._predicates["ipython_internal"]:
313 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
332 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
314 return ip_hide
333 return ip_hide
315
334
@@ -532,15 +551,62 b' class Pdb(OldPdb):'
532 except KeyboardInterrupt:
551 except KeyboardInterrupt:
533 pass
552 pass
534
553
554 def do_skip_predicates(self, args):
555 """
556 Turn on/off individual predicates as to whether a frame should be hidden/skip.
557
558 The global option to skip (or not) hidden frames is set with skip_hidden
559
560 To change the value of a predicate
561
562 skip_predicates key [true|false]
563
564 Call without arguments to see the current values.
565
566 """
567 if not args.strip():
568 print("current predicates:")
569 for (p, v) in self._predicates.items():
570 print(" ", p, ":", v)
571 return
572 type_value = args.strip().split(" ")
573 if len(type_value) != 2:
574 print("Usage: skip_predicates <type> <value>")
575 return
576
577 type_, value = type_value
578 if type_ not in self._predicates:
579 print(f"{type_!r} not in {set(self._predicates.keys())}")
580 return
581 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
582 print(
583 f"{value} invalid use one of ('true', 'yes', '1', 'no', 'false', '0')"
584 )
585 return
586
587 self._predicates[type_] = value.lower() in ("true", "yes", "1")
588 if not any(self._predicates.values()):
589 print(
590 "Warning, all predicates set to False, skip_hidden may not have any effects."
591 )
592
535 def do_skip_hidden(self, arg):
593 def do_skip_hidden(self, arg):
536 """
594 """
537 Change whether or not we should skip frames with the
595 Change whether or not we should skip frames with the
538 __tracebackhide__ attribute.
596 __tracebackhide__ attribute.
539 """
597 """
540 if arg.strip().lower() in ("true", "yes"):
598 if not arg.strip():
599 print(
600 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
601 )
602 elif arg.strip().lower() in ("true", "yes"):
541 self.skip_hidden = True
603 self.skip_hidden = True
542 elif arg.strip().lower() in ("false", "no"):
604 elif arg.strip().lower() in ("false", "no"):
543 self.skip_hidden = False
605 self.skip_hidden = False
606 if not any(self._predicates.values()):
607 print(
608 "Warning, all predicates set to False, skip_hidden may not have any effects."
609 )
544
610
545 def do_list(self, arg):
611 def do_list(self, arg):
546 """Print lines of code from the current stack frame
612 """Print lines of code from the current stack frame
@@ -704,7 +770,7 b' class Pdb(OldPdb):'
704 def stop_here(self, frame):
770 def stop_here(self, frame):
705 hidden = False
771 hidden = False
706 if self.skip_hidden:
772 if self.skip_hidden:
707 hidden = frame.f_locals.get("__tracebackhide__", False)
773 hidden = self._hidden_predicate(frame)
708 if hidden:
774 if hidden:
709 Colors = self.color_scheme_table.active_colors
775 Colors = self.color_scheme_table.active_colors
710 ColorsNormal = Colors.Normal
776 ColorsNormal = Colors.Normal
@@ -720,7 +786,7 b' class Pdb(OldPdb):'
720 Will skip hidden frames.
786 Will skip hidden frames.
721 """
787 """
722 # modified version of upstream that skips
788 # modified version of upstream that skips
723 # frames with __tracebackide__
789 # frames with __tracebackhide__
724 if self.curindex == 0:
790 if self.curindex == 0:
725 self.error("Oldest frame")
791 self.error("Oldest frame")
726 return
792 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