Show More
@@ -24,8 +24,12 b' By default, frames from readonly files will be hidden, frames containing' | |||||
24 | Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent |
|
24 | Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent | |
25 | frames value of ``__debuggerskip__`` is ``True`` will be skipped. |
|
25 | frames value of ``__debuggerskip__`` is ``True`` will be skipped. | |
26 |
|
26 | |||
27 |
>>> def helper |
|
27 | >>> def helpers_helper(): | |
|
28 | ... pass | |||
|
29 | ... | |||
|
30 | ... def helper_1(): | |||
28 | ... print("don't step in me") |
|
31 | ... print("don't step in me") | |
|
32 | ... helpers_helpers() # will be stepped over unless breakpoint set. | |||
29 | ... |
|
33 | ... | |
30 | ... |
|
34 | ... | |
31 | ... def helper_2(): |
|
35 | ... def helper_2(): | |
@@ -44,6 +48,7 b' One can define a decorator that wraps a function between the two helpers:' | |||||
44 | ... result = function(*args, **kwargs) |
|
48 | ... result = function(*args, **kwargs) | |
45 | ... __debuggerskip__ = True |
|
49 | ... __debuggerskip__ = True | |
46 | ... helper_2() |
|
50 | ... helper_2() | |
|
51 | ... # setting __debuggerskip__ to False again is not necessary | |||
47 | ... return result |
|
52 | ... return result | |
48 | ... |
|
53 | ... | |
49 | ... return wrapped_fn |
|
54 | ... return wrapped_fn | |
@@ -902,12 +907,16 b' class Pdb(OldPdb):' | |||||
902 | stop at any point inside the function |
|
907 | stop at any point inside the function | |
903 |
|
908 | |||
904 | """ |
|
909 | """ | |
|
910 | ||||
|
911 | sup = super().break_anywhere(frame) | |||
|
912 | if sup: | |||
|
913 | return sup | |||
905 | if self._predicates["debuggerskip"]: |
|
914 | if self._predicates["debuggerskip"]: | |
906 | if DEBUGGERSKIP in frame.f_code.co_varnames: |
|
915 | if DEBUGGERSKIP in frame.f_code.co_varnames: | |
907 | return True |
|
916 | return True | |
908 | if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP): |
|
917 | if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP): | |
909 | return True |
|
918 | return True | |
910 | return super().break_anywhere(frame) |
|
919 | return False | |
911 |
|
920 | |||
912 | @skip_doctest |
|
921 | @skip_doctest | |
913 | def _is_in_decorator_internal_and_should_skip(self, frame): |
|
922 | def _is_in_decorator_internal_and_should_skip(self, frame): | |
@@ -926,8 +935,12 b' class Pdb(OldPdb):' | |||||
926 | if DEBUGGERSKIP in frame.f_code.co_varnames: |
|
935 | if DEBUGGERSKIP in frame.f_code.co_varnames: | |
927 | return True |
|
936 | return True | |
928 |
|
937 | |||
929 | # if parent frame value set to True skip as well. |
|
938 | # if one of the parent frame value set to True skip as well. | |
930 | if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP): |
|
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): | |||
931 | return True |
|
944 | return True | |
932 |
|
945 | |||
933 | return False |
|
946 | return False |
@@ -12,6 +12,7 b' import subprocess' | |||||
12 | import sys |
|
12 | import sys | |
13 | import time |
|
13 | import time | |
14 | import warnings |
|
14 | import warnings | |
|
15 | ||||
15 | from subprocess import PIPE, CalledProcessError, check_output |
|
16 | from subprocess import PIPE, CalledProcessError, check_output | |
16 | from tempfile import NamedTemporaryFile |
|
17 | from tempfile import NamedTemporaryFile | |
17 | from textwrap import dedent |
|
18 | from textwrap import dedent | |
@@ -325,14 +326,30 b' def test_xmode_skip():' | |||||
325 |
|
326 | |||
326 | skip_decorators_blocks = ( |
|
327 | skip_decorators_blocks = ( | |
327 | """ |
|
328 | """ | |
|
329 | def helpers_helper(): | |||
|
330 | pass # should not stop here except breakpoint | |||
|
331 | """, | |||
|
332 | """ | |||
328 | def helper_1(): |
|
333 | def helper_1(): | |
329 |
|
|
334 | helpers_helper() # should not stop here | |
330 | """, |
|
335 | """, | |
331 | """ |
|
336 | """ | |
332 | def helper_2(): |
|
337 | def helper_2(): | |
333 | pass # should not stop here |
|
338 | pass # should not stop here | |
334 | """, |
|
339 | """, | |
335 | """ |
|
340 | """ | |
|
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 | """ | |||
336 | def pdb_skipped_decorator(function): |
|
353 | def pdb_skipped_decorator(function): | |
337 | def wrapped_fn(*args, **kwargs): |
|
354 | def wrapped_fn(*args, **kwargs): | |
338 | __debuggerskip__ = True |
|
355 | __debuggerskip__ = True | |
@@ -346,6 +363,7 b' skip_decorators_blocks = (' | |||||
346 | """, |
|
363 | """, | |
347 | """ |
|
364 | """ | |
348 | @pdb_skipped_decorator |
|
365 | @pdb_skipped_decorator | |
|
366 | @pdb_skipped_decorator2 | |||
349 | def bar(x, y): |
|
367 | def bar(x, y): | |
350 | return x * y |
|
368 | return x * y | |
351 | """, |
|
369 | """, | |
@@ -423,7 +441,7 b' def test_decorator_skip_disabled():' | |||||
423 | ("step", "----> 3 __debuggerskip__"), |
|
441 | ("step", "----> 3 __debuggerskip__"), | |
424 | ("step", "----> 4 helper_1()"), |
|
442 | ("step", "----> 4 helper_1()"), | |
425 | ("step", "---> 1 def helper_1():"), |
|
443 | ("step", "---> 1 def helper_1():"), | |
426 |
("next", "----> 2 |
|
444 | ("next", "----> 2 helpers_helper()"), | |
427 | ("next", "--Return--"), |
|
445 | ("next", "--Return--"), | |
428 | ("next", "----> 5 __debuggerskip__ = False"), |
|
446 | ("next", "----> 5 __debuggerskip__ = False"), | |
429 | ]: |
|
447 | ]: | |
@@ -436,6 +454,62 b' def test_decorator_skip_disabled():' | |||||
436 |
|
454 | |||
437 |
|
455 | |||
438 | @skip_win32 |
|
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 | ||||
|
512 | @skip_win32 | |||
439 | def test_where_erase_value(): |
|
513 | def test_where_erase_value(): | |
440 | """Test that `where` does not access f_locals and erase values.""" |
|
514 | """Test that `where` does not access f_locals and erase values.""" | |
441 | import pexpect |
|
515 | import pexpect |
General Comments 0
You need to be logged in to leave comments.
Login now