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 |
|
|
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