Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 65c34c4

Browse files
committed
Expand and Fix PDB skip.
This expand and fix the logic arround PBD skip. 1) it should support nested decorators, as long as they are all marked. 2) it will stop into breakpoints if those are set even in debuggerskip. 3) Documentation mentioned those facts.
1 parent d50de43 commit 65c34c4

2 files changed

Lines changed: 94 additions & 7 deletions

File tree

IPython/core/debugger.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@
2424
Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
2525
frames value of ``__debuggerskip__`` is ``True`` will be skipped.
2626
27-
>>> def helper_1():
27+
>>> def helpers_helper():
28+
... pass
29+
...
30+
... def helper_1():
2831
... print("don't step in me")
32+
... helpers_helpers() # will be stepped over unless breakpoint set.
2933
...
3034
...
3135
... def helper_2():
@@ -44,6 +48,7 @@
4448
... result = function(*args, **kwargs)
4549
... __debuggerskip__ = True
4650
... helper_2()
51+
... # setting __debuggerskip__ to False again is not necessary
4752
... return result
4853
...
4954
... return wrapped_fn
@@ -902,12 +907,16 @@ def break_anywhere(self, frame):
902907
stop at any point inside the function
903908
904909
"""
910+
911+
sup = super().break_anywhere(frame)
912+
if sup:
913+
return sup
905914
if self._predicates["debuggerskip"]:
906915
if DEBUGGERSKIP in frame.f_code.co_varnames:
907916
return True
908917
if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
909918
return True
910-
return super().break_anywhere(frame)
919+
return False
911920

912921
@skip_doctest
913922
def _is_in_decorator_internal_and_should_skip(self, frame):
@@ -926,9 +935,13 @@ def _is_in_decorator_internal_and_should_skip(self, frame):
926935
if DEBUGGERSKIP in frame.f_code.co_varnames:
927936
return True
928937

929-
# if parent frame value set to True skip as well.
930-
if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
931-
return True
938+
# if one of the parent frame value set to True skip as well.
939+
940+
cframe = frame
941+
while getattr(cframe, "f_back", None):
942+
cframe = cframe.f_back
943+
if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
944+
return True
932945

933946
return False
934947

IPython/core/tests/test_debugger.py

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import sys
1313
import time
1414
import warnings
15+
1516
from subprocess import PIPE, CalledProcessError, check_output
1617
from tempfile import NamedTemporaryFile
1718
from textwrap import dedent
@@ -324,15 +325,31 @@ def g():
324325

325326

326327
skip_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
439513
def test_where_erase_value():
440514
"""Test that `where` does not access f_locals and erase values."""

0 commit comments

Comments
 (0)