##// END OF EJS Templates
Merge pull request #12393 from Carreau/auto-backport-of-pr-12359-on-7.x...
Matthias Bussonnier -
r25846:3d900d38 merge
parent child Browse files
Show More
@@ -0,0 +1,11 b''
1 The default tracebackmode will now skip frames that are marked with
2 ``__tracebackhide__ = True`` and show how many traceback frames have been
3 skipped. This can be toggled by using :magic:`xmode` with the ``--show`` or
4 ``--hide`` attribute. It will have no effect on non verbose traceback modes.
5
6 The ipython debugger also now understand ``__tracebackhide__`` as well and will
7 skip hidden frames when displaying. Movement up and down the stack will skip the
8 hidden frames and will show how many frames were hidden. Internal IPython frames
9 are also now hidden by default. The behavior can be changed with the
10 ``skip_hidden`` command and accepts "yes", "no", "true" and "false" case
11 insensitive parameters.
@@ -280,26 +280,31 b' class Pdb(OldPdb):'
280 280
281 281 # Set the prompt - the default prompt is '(Pdb)'
282 282 self.prompt = prompt
283 self.skip_hidden = True
283 284
284 285 def set_colors(self, scheme):
285 286 """Shorthand access to the color table scheme selector method."""
286 287 self.color_scheme_table.set_active_scheme(scheme)
287 288 self.parser.style = scheme
288 289
290
291 def hidden_frames(self, stack):
292 """
293 Given an index in the stack return wether it should be skipped.
294
295 This is used in up/down and where to skip frames.
296 """
297 ip_hide = [s[0].f_locals.get("__tracebackhide__", False) for s in stack]
298 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
299 if ip_start:
300 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
301 return ip_hide
302
289 303 def interaction(self, frame, traceback):
290 304 try:
291 305 OldPdb.interaction(self, frame, traceback)
292 306 except KeyboardInterrupt:
293 self.stdout.write('\n' + self.shell.get_exception_only())
294
295 def new_do_up(self, arg):
296 OldPdb.do_up(self, arg)
297 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
298
299 def new_do_down(self, arg):
300 OldPdb.do_down(self, arg)
301
302 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
307 self.stdout.write("\n" + self.shell.get_exception_only())
303 308
304 309 def new_do_frame(self, arg):
305 310 OldPdb.do_frame(self, arg)
@@ -320,6 +325,8 b' class Pdb(OldPdb):'
320 325 return self.do_quit(arg)
321 326
322 327 def print_stack_trace(self, context=None):
328 Colors = self.color_scheme_table.active_colors
329 ColorsNormal = Colors.Normal
323 330 if context is None:
324 331 context = self.context
325 332 try:
@@ -329,8 +336,21 b' class Pdb(OldPdb):'
329 336 except (TypeError, ValueError):
330 337 raise ValueError("Context must be a positive integer")
331 338 try:
332 for frame_lineno in self.stack:
339 skipped = 0
340 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
341 if hidden and self.skip_hidden:
342 skipped += 1
343 continue
344 if skipped:
345 print(
346 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
347 )
348 skipped = 0
333 349 self.print_stack_entry(frame_lineno, context=context)
350 if skipped:
351 print(
352 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
353 )
334 354 except KeyboardInterrupt:
335 355 pass
336 356
@@ -487,6 +507,16 b' class Pdb(OldPdb):'
487 507 except KeyboardInterrupt:
488 508 pass
489 509
510 def do_skip_hidden(self, arg):
511 """
512 Change whether or not we should skip frames with the
513 __tracebackhide__ attribute.
514 """
515 if arg.strip().lower() in ("true", "yes"):
516 self.skip_hidden = True
517 elif arg.strip().lower() in ("false", "no"):
518 self.skip_hidden = False
519
490 520 def do_list(self, arg):
491 521 """Print lines of code from the current stack frame
492 522 """
@@ -633,6 +663,109 b' class Pdb(OldPdb):'
633 663
634 664 do_w = do_where
635 665
666 def stop_here(self, frame):
667 hidden = False
668 if self.skip_hidden:
669 hidden = frame.f_locals.get("__tracebackhide__", False)
670 if hidden:
671 Colors = self.color_scheme_table.active_colors
672 ColorsNormal = Colors.Normal
673 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
674
675 return super().stop_here(frame)
676
677 def do_up(self, arg):
678 """u(p) [count]
679 Move the current frame count (default one) levels up in the
680 stack trace (to an older frame).
681
682 Will skip hidden frames.
683 """
684 ## modified version of upstream that skips
685 # frames with __tracebackide__
686 if self.curindex == 0:
687 self.error("Oldest frame")
688 return
689 try:
690 count = int(arg or 1)
691 except ValueError:
692 self.error("Invalid frame count (%s)" % arg)
693 return
694 skipped = 0
695 if count < 0:
696 _newframe = 0
697 else:
698 _newindex = self.curindex
699 counter = 0
700 hidden_frames = self.hidden_frames(self.stack)
701 for i in range(self.curindex - 1, -1, -1):
702 frame = self.stack[i][0]
703 if hidden_frames[i] and self.skip_hidden:
704 skipped += 1
705 continue
706 counter += 1
707 if counter >= count:
708 break
709 else:
710 # if no break occured.
711 self.error("all frames above hidden")
712 return
713
714 Colors = self.color_scheme_table.active_colors
715 ColorsNormal = Colors.Normal
716 _newframe = i
717 self._select_frame(_newframe)
718 if skipped:
719 print(
720 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
721 )
722
723 def do_down(self, arg):
724 """d(own) [count]
725 Move the current frame count (default one) levels down in the
726 stack trace (to a newer frame).
727
728 Will skip hidden frames.
729 """
730 if self.curindex + 1 == len(self.stack):
731 self.error("Newest frame")
732 return
733 try:
734 count = int(arg or 1)
735 except ValueError:
736 self.error("Invalid frame count (%s)" % arg)
737 return
738 if count < 0:
739 _newframe = len(self.stack) - 1
740 else:
741 _newindex = self.curindex
742 counter = 0
743 skipped = 0
744 hidden_frames = self.hidden_frames(self.stack)
745 for i in range(self.curindex + 1, len(self.stack)):
746 frame = self.stack[i][0]
747 if hidden_frames[i] and self.skip_hidden:
748 skipped += 1
749 continue
750 counter += 1
751 if counter >= count:
752 break
753 else:
754 self.error("all frames bellow hidden")
755 return
756
757 Colors = self.color_scheme_table.active_colors
758 ColorsNormal = Colors.Normal
759 if skipped:
760 print(
761 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
762 )
763 _newframe = i
764
765 self._select_frame(_newframe)
766
767 do_d = do_down
768 do_u = do_up
636 769
637 770 class InterruptiblePdb(Pdb):
638 771 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
@@ -2211,11 +2211,20 b' class InteractiveShell(SingletonConfigurable):'
2211 2211 with self.builtin_trap:
2212 2212 return self.Completer.complete(text, line, cursor_pos)
2213 2213
2214 def set_custom_completer(self, completer, pos=0):
2214 def set_custom_completer(self, completer, pos=0) -> None:
2215 2215 """Adds a new custom completer function.
2216 2216
2217 2217 The position argument (defaults to 0) is the index in the completers
2218 list where you want the completer to be inserted."""
2218 list where you want the completer to be inserted.
2219
2220 `completer` should have the following signature::
2221
2222 def completion(self: Completer, text: string) -> List[str]:
2223 raise NotImplementedError
2224
2225 It will be bound to the current Completer instance and pass some text
2226 and return a list with current completions to suggest to the user.
2227 """
2219 2228
2220 2229 newcomp = types.MethodType(completer, self.Completer)
2221 2230 self.Completer.custom_matchers.insert(pos,newcomp)
@@ -3310,6 +3319,9 b' class InteractiveShell(SingletonConfigurable):'
3310 3319 False : successful execution.
3311 3320 True : an error occurred.
3312 3321 """
3322 # special value to say that anything above is IPython and should be
3323 # hidden.
3324 __tracebackhide__ = "__ipython_bottom__"
3313 3325 # Set our own excepthook in case the user code tries to call it
3314 3326 # directly, so that the IPython crash handler doesn't get triggered
3315 3327 old_excepthook, sys.excepthook = sys.excepthook, self.excepthook
@@ -364,13 +364,25 b' Currently the magic system has the following functions:""",'
364 364
365 365 Valid modes: Plain, Context, Verbose, and Minimal.
366 366
367 If called without arguments, acts as a toggle."""
367 If called without arguments, acts as a toggle.
368
369 When in verbose mode the value --show (and --hide)
370 will respectively show (or hide) frames with ``__tracebackhide__ =
371 True`` value set.
372 """
368 373
369 374 def xmode_switch_err(name):
370 375 warn('Error changing %s exception modes.\n%s' %
371 376 (name,sys.exc_info()[1]))
372 377
373 378 shell = self.shell
379 if parameter_s.strip() == "--show":
380 shell.InteractiveTB.skip_hidden = False
381 return
382 if parameter_s.strip() == "--hide":
383 shell.InteractiveTB.skip_hidden = True
384 return
385
374 386 new_mode = parameter_s.strip().capitalize()
375 387 try:
376 388 shell.InteractiveTB.set_mode(mode=new_mode)
@@ -4,20 +4,24 b''
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 import bdb
8 import builtins
9 import os
7 10 import signal
11 import subprocess
8 12 import sys
9 13 import time
10 14 import warnings
15 from subprocess import PIPE, CalledProcessError, check_output
11 16 from tempfile import NamedTemporaryFile
12 from subprocess import check_output, CalledProcessError, PIPE
13 import subprocess
17 from textwrap import dedent
14 18 from unittest.mock import patch
15 import builtins
16 import bdb
17 19
18 20 import nose.tools as nt
19 21
20 22 from IPython.core import debugger
23 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
24 from IPython.testing.decorators import skip_win32
21 25
22 26 #-----------------------------------------------------------------------------
23 27 # Helper classes, from CPython's Pdb test suite
@@ -254,3 +258,69 b' def test_interruptible_core_debugger():'
254 258 # implementation would involve a subprocess, but that adds issues with
255 259 # interrupting subprocesses that are rather complex, so it's simpler
256 260 # just to do it this way.
261
262 @skip_win32
263 def test_xmode_skip():
264 """that xmode skip frames
265
266 Not as a doctest as pytest does not run doctests.
267 """
268 import pexpect
269 env = os.environ.copy()
270 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
271
272 child = pexpect.spawn(
273 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
274 )
275 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
276
277 child.expect("IPython")
278 child.expect("\n")
279 child.expect_exact("In [1]")
280
281 block = dedent(
282 """
283 def f():
284 __tracebackhide__ = True
285 g()
286
287 def g():
288 raise ValueError
289
290 f()
291 """
292 )
293
294 for line in block.splitlines():
295 child.sendline(line)
296 child.expect_exact(line)
297 child.expect_exact("skipping")
298
299 block = dedent(
300 """
301 def f():
302 __tracebackhide__ = True
303 g()
304
305 def g():
306 from IPython.core.debugger import set_trace
307 set_trace()
308
309 f()
310 """
311 )
312
313 for line in block.splitlines():
314 child.sendline(line)
315 child.expect_exact(line)
316
317 child.expect("ipdb>")
318 child.sendline("w")
319 child.expect("hidden")
320 child.expect("ipdb>")
321 child.sendline("skip_hidden false")
322 child.sendline("w")
323 child.expect("__traceba")
324 child.expect("ipdb>")
325
326 child.close()
@@ -879,13 +879,36 b' class VerboseTB(TBTools):'
879 879 self.check_cache = check_cache
880 880
881 881 self.debugger_cls = debugger_cls or debugger.Pdb
882 self.skip_hidden = True
882 883
883 884 def format_records(self, records, last_unique, recursion_repeat):
884 885 """Format the stack frames of the traceback"""
885 886 frames = []
887
888 skipped = 0
886 889 for r in records[:last_unique+recursion_repeat+1]:
887 #print '*** record:',file,lnum,func,lines,index # dbg
890 if self.skip_hidden:
891 if r[0].f_locals.get("__tracebackhide__", 0):
892 skipped += 1
893 continue
894 if skipped:
895 Colors = self.Colors # just a shorthand + quicker name lookup
896 ColorsNormal = Colors.Normal # used a lot
897 frames.append(
898 " %s[... skipping hidden %s frame]%s\n"
899 % (Colors.excName, skipped, ColorsNormal)
900 )
901 skipped = 0
902
888 903 frames.append(self.format_record(*r))
904
905 if skipped:
906 Colors = self.Colors # just a shorthand + quicker name lookup
907 ColorsNormal = Colors.Normal # used a lot
908 frames.append(
909 " %s[... skipping hidden %s frame]%s\n"
910 % (Colors.excName, skipped, ColorsNormal)
911 )
889 912
890 913 if recursion_repeat:
891 914 frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat)
@@ -1123,8 +1146,6 b' class VerboseTB(TBTools):'
1123 1146 head = self.prepare_header(etype, self.long_header)
1124 1147 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
1125 1148
1126 if records is None:
1127 return ""
1128 1149
1129 1150 last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records)
1130 1151
@@ -41,6 +41,16 b' class TerminalPdb(Pdb):'
41 41 global_namespace={},
42 42 parent=self.shell,
43 43 )
44 # add a completer for all the do_ methods
45 methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
46
47 def gen_comp(self, text):
48 return [m for m in methods_names if m.startswith(text)]
49 import types
50 newcomp = types.MethodType(gen_comp, compl)
51 compl.custom_matchers.insert(0, newcomp)
52 # end add completer.
53
44 54 self._ptcomp = IPythonPTCompleter(compl)
45 55
46 56 options = dict(
@@ -4,13 +4,6 b' matrix:'
4 4
5 5 environment:
6 6 matrix:
7 - PYTHON: "C:\\Python36"
8 PYTHON_VERSION: "3.6.x"
9 PYTHON_ARCH: "32"
10
11 - PYTHON: "C:\\Python36-x64"
12 PYTHON_VERSION: "3.6.x"
13 PYTHON_ARCH: "64"
14 7
15 8 - PYTHON: "C:\\Python37-x64"
16 9 PYTHON_VERSION: "3.7.x"
General Comments 0
You need to be logged in to leave comments. Login now