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