##// END OF EJS Templates
Backport PR #13175: Expand and Fix PDB skip.
Matthias Bussonnier -
Show More
@@ -24,8 +24,12 b' By default, frames from readonly files will be hidden, frames containing'
24 24 Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
25 25 frames value of ``__debuggerskip__`` is ``True`` will be skipped.
26 26
27 >>> def helper_1():
27 >>> def helpers_helper():
28 ... pass
29 ...
30 ... def helper_1():
28 31 ... print("don't step in me")
32 ... helpers_helpers() # will be stepped over unless breakpoint set.
29 33 ...
30 34 ...
31 35 ... def helper_2():
@@ -44,6 +48,7 b' One can define a decorator that wraps a function between the two helpers:'
44 48 ... result = function(*args, **kwargs)
45 49 ... __debuggerskip__ = True
46 50 ... helper_2()
51 ... # setting __debuggerskip__ to False again is not necessary
47 52 ... return result
48 53 ...
49 54 ... return wrapped_fn
@@ -892,12 +897,16 b' class Pdb(OldPdb):'
892 897 stop at any point inside the function
893 898
894 899 """
900
901 sup = super().break_anywhere(frame)
902 if sup:
903 return sup
895 904 if self._predicates["debuggerskip"]:
896 905 if DEBUGGERSKIP in frame.f_code.co_varnames:
897 906 return True
898 907 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
899 908 return True
900 return super().break_anywhere(frame)
909 return False
901 910
902 911 @skip_doctest
903 912 def _is_in_decorator_internal_and_should_skip(self, frame):
@@ -916,9 +925,13 b' class Pdb(OldPdb):'
916 925 if DEBUGGERSKIP in frame.f_code.co_varnames:
917 926 return True
918 927
919 # if parent frame value set to True skip as well.
920 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
921 return True
928 # if one of the parent frame value set to True skip as well.
929
930 cframe = frame
931 while getattr(cframe, "f_back", None):
932 cframe = cframe.f_back
933 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
934 return True
922 935
923 936 return False
924 937
@@ -12,6 +12,7 b' import subprocess'
12 12 import sys
13 13 import time
14 14 import warnings
15
15 16 from subprocess import PIPE, CalledProcessError, check_output
16 17 from tempfile import NamedTemporaryFile
17 18 from textwrap import dedent
@@ -328,14 +329,30 b' def test_xmode_skip():'
328 329
329 330 skip_decorators_blocks = (
330 331 """
332 def helpers_helper():
333 pass # should not stop here except breakpoint
334 """,
335 """
331 336 def helper_1():
332 pass # should not stop here
337 helpers_helper() # should not stop here
333 338 """,
334 339 """
335 340 def helper_2():
336 341 pass # should not stop here
337 342 """,
338 343 """
344 def pdb_skipped_decorator2(function):
345 def wrapped_fn(*args, **kwargs):
346 __debuggerskip__ = True
347 helper_2()
348 __debuggerskip__ = False
349 result = function(*args, **kwargs)
350 __debuggerskip__ = True
351 helper_2()
352 return result
353 return wrapped_fn
354 """,
355 """
339 356 def pdb_skipped_decorator(function):
340 357 def wrapped_fn(*args, **kwargs):
341 358 __debuggerskip__ = True
@@ -349,6 +366,7 b' skip_decorators_blocks = ('
349 366 """,
350 367 """
351 368 @pdb_skipped_decorator
369 @pdb_skipped_decorator2
352 370 def bar(x, y):
353 371 return x * y
354 372 """,
@@ -426,7 +444,7 b' def test_decorator_skip_disabled():'
426 444 ("step", "----> 3 __debuggerskip__"),
427 445 ("step", "----> 4 helper_1()"),
428 446 ("step", "---> 1 def helper_1():"),
429 ("next", "----> 2 pass"),
447 ("next", "----> 2 helpers_helper()"),
430 448 ("next", "--Return--"),
431 449 ("next", "----> 5 __debuggerskip__ = False"),
432 450 ]:
@@ -439,6 +457,62 b' def test_decorator_skip_disabled():'
439 457
440 458
441 459 @skip_win32
460 def test_decorator_skip_with_breakpoint():
461 """test that decorator frame skipping can be disabled"""
462
463 import pexpect
464
465 env = os.environ.copy()
466 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
467
468 child = pexpect.spawn(
469 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
470 )
471 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
472
473 child.expect("IPython")
474 child.expect("\n")
475
476 ### we need a filename, so we need to exec the full block with a filename
477 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
478
479 name = tf.name[:-3].split("/")[-1]
480 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
481 tf.flush()
482 codeblock = f"from {name} import f"
483
484 dedented_blocks = [
485 codeblock,
486 "f()",
487 ]
488
489 in_prompt_number = 1
490 for cblock in dedented_blocks:
491 child.expect_exact(f"In [{in_prompt_number}]:")
492 in_prompt_number += 1
493 for line in cblock.splitlines():
494 child.sendline(line)
495 child.expect_exact(line)
496 child.sendline("")
497
498 # as the filename does not exists, we'll rely on the filename prompt
499 child.expect_exact("47 bar(3, 4)")
500
501 for input_, expected in [
502 (f"b {name}.py:3", ""),
503 ("step", "1---> 3 pass # should not stop here except"),
504 ("step", "---> 38 @pdb_skipped_decorator"),
505 ("continue", ""),
506 ]:
507 child.expect("ipdb>")
508 child.sendline(input_)
509 child.expect_exact(input_)
510 child.expect_exact(expected)
511
512 child.close()
513
514
515 @skip_win32
442 516 def test_where_erase_value():
443 517 """Test that `where` does not access f_locals and erase values."""
444 518 import pexpect
General Comments 0
You need to be logged in to leave comments. Login now