1212import sys
1313import time
1414import warnings
15+
1516from subprocess import PIPE , CalledProcessError , check_output
1617from tempfile import NamedTemporaryFile
1718from textwrap import dedent
@@ -324,15 +325,31 @@ def g():
324325
325326
326327skip_decorators_blocks = (
328+ """
329+ def helpers_helper():
330+ pass # should not stop here except breakpoint
331+ """ ,
327332 """
328333 def helper_1():
329- pass # should not stop here
334+ helpers_helper() # should not stop here
330335 """ ,
331336 """
332337 def helper_2():
333338 pass # should not stop here
334339 """ ,
335340 """
341+ def pdb_skipped_decorator2(function):
342+ def wrapped_fn(*args, **kwargs):
343+ __debuggerskip__ = True
344+ helper_2()
345+ __debuggerskip__ = False
346+ result = function(*args, **kwargs)
347+ __debuggerskip__ = True
348+ helper_2()
349+ return result
350+ return wrapped_fn
351+ """ ,
352+ """
336353 def pdb_skipped_decorator(function):
337354 def wrapped_fn(*args, **kwargs):
338355 __debuggerskip__ = True
@@ -346,6 +363,7 @@ def wrapped_fn(*args, **kwargs):
346363 """ ,
347364 """
348365 @pdb_skipped_decorator
366+ @pdb_skipped_decorator2
349367 def bar(x, y):
350368 return x * y
351369 """ ,
@@ -423,7 +441,7 @@ def test_decorator_skip_disabled():
423441 ("step" , "----> 3 __debuggerskip__" ),
424442 ("step" , "----> 4 helper_1()" ),
425443 ("step" , "---> 1 def helper_1():" ),
426- ("next" , "----> 2 pass " ),
444+ ("next" , "----> 2 helpers_helper() " ),
427445 ("next" , "--Return--" ),
428446 ("next" , "----> 5 __debuggerskip__ = False" ),
429447 ]:
@@ -435,6 +453,62 @@ def test_decorator_skip_disabled():
435453 child .close ()
436454
437455
456+ @skip_win32
457+ def test_decorator_skip_with_breakpoint ():
458+ """test that decorator frame skipping can be disabled"""
459+
460+ import pexpect
461+
462+ env = os .environ .copy ()
463+ env ["IPY_TEST_SIMPLE_PROMPT" ] = "1"
464+
465+ child = pexpect .spawn (
466+ sys .executable , ["-m" , "IPython" , "--colors=nocolor" ], env = env
467+ )
468+ child .timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
469+
470+ child .expect ("IPython" )
471+ child .expect ("\n " )
472+
473+ ### we need a filename, so we need to exec the full block with a filename
474+ with NamedTemporaryFile (suffix = ".py" , dir = "." , delete = True ) as tf :
475+
476+ name = tf .name [:- 3 ].split ("/" )[- 1 ]
477+ tf .write ("\n " .join ([dedent (x ) for x in skip_decorators_blocks [:- 1 ]]).encode ())
478+ tf .flush ()
479+ codeblock = f"from { name } import f"
480+
481+ dedented_blocks = [
482+ codeblock ,
483+ "f()" ,
484+ ]
485+
486+ in_prompt_number = 1
487+ for cblock in dedented_blocks :
488+ child .expect_exact (f"In [{ in_prompt_number } ]:" )
489+ in_prompt_number += 1
490+ for line in cblock .splitlines ():
491+ child .sendline (line )
492+ child .expect_exact (line )
493+ child .sendline ("" )
494+
495+ # as the filename does not exists, we'll rely on the filename prompt
496+ child .expect_exact ("47 bar(3, 4)" )
497+
498+ for input_ , expected in [
499+ (f"b { name } .py:3" , "" ),
500+ ("step" , "1---> 3 pass # should not stop here except" ),
501+ ("step" , "---> 38 @pdb_skipped_decorator" ),
502+ ("continue" , "" ),
503+ ]:
504+ child .expect ("ipdb>" )
505+ child .sendline (input_ )
506+ child .expect_exact (input_ )
507+ child .expect_exact (expected )
508+
509+ child .close ()
510+
511+
438512@skip_win32
439513def test_where_erase_value ():
440514 """Test that `where` does not access f_locals and erase values."""
0 commit comments