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 | |
@@ -892,12 +897,16 b' class Pdb(OldPdb):' | |||||
892 | stop at any point inside the function |
|
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 | if self._predicates["debuggerskip"]: |
|
904 | if self._predicates["debuggerskip"]: | |
896 | if DEBUGGERSKIP in frame.f_code.co_varnames: |
|
905 | if DEBUGGERSKIP in frame.f_code.co_varnames: | |
897 | return True |
|
906 | return True | |
898 | if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP): |
|
907 | if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP): | |
899 | return True |
|
908 | return True | |
900 | return super().break_anywhere(frame) |
|
909 | return False | |
901 |
|
910 | |||
902 | @skip_doctest |
|
911 | @skip_doctest | |
903 | def _is_in_decorator_internal_and_should_skip(self, frame): |
|
912 | def _is_in_decorator_internal_and_should_skip(self, frame): | |
@@ -916,9 +925,13 b' class Pdb(OldPdb):' | |||||
916 | if DEBUGGERSKIP in frame.f_code.co_varnames: |
|
925 | if DEBUGGERSKIP in frame.f_code.co_varnames: | |
917 | return True |
|
926 | return True | |
918 |
|
927 | |||
919 | # if parent frame value set to True skip as well. |
|
928 | # if one of the parent frame value set to True skip as well. | |
920 | if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP): |
|
929 | ||
921 | return True |
|
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 | return False |
|
936 | return False | |
924 |
|
937 |
@@ -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 | |
@@ -328,14 +329,30 b' def test_xmode_skip():' | |||||
328 |
|
329 | |||
329 | skip_decorators_blocks = ( |
|
330 | skip_decorators_blocks = ( | |
330 | """ |
|
331 | """ | |
|
332 | def helpers_helper(): | |||
|
333 | pass # should not stop here except breakpoint | |||
|
334 | """, | |||
|
335 | """ | |||
331 | def helper_1(): |
|
336 | def helper_1(): | |
332 |
|
|
337 | helpers_helper() # should not stop here | |
333 | """, |
|
338 | """, | |
334 | """ |
|
339 | """ | |
335 | def helper_2(): |
|
340 | def helper_2(): | |
336 | pass # should not stop here |
|
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 | def pdb_skipped_decorator(function): |
|
356 | def pdb_skipped_decorator(function): | |
340 | def wrapped_fn(*args, **kwargs): |
|
357 | def wrapped_fn(*args, **kwargs): | |
341 | __debuggerskip__ = True |
|
358 | __debuggerskip__ = True | |
@@ -349,6 +366,7 b' skip_decorators_blocks = (' | |||||
349 | """, |
|
366 | """, | |
350 | """ |
|
367 | """ | |
351 | @pdb_skipped_decorator |
|
368 | @pdb_skipped_decorator | |
|
369 | @pdb_skipped_decorator2 | |||
352 | def bar(x, y): |
|
370 | def bar(x, y): | |
353 | return x * y |
|
371 | return x * y | |
354 | """, |
|
372 | """, | |
@@ -426,7 +444,7 b' def test_decorator_skip_disabled():' | |||||
426 | ("step", "----> 3 __debuggerskip__"), |
|
444 | ("step", "----> 3 __debuggerskip__"), | |
427 | ("step", "----> 4 helper_1()"), |
|
445 | ("step", "----> 4 helper_1()"), | |
428 | ("step", "---> 1 def helper_1():"), |
|
446 | ("step", "---> 1 def helper_1():"), | |
429 |
("next", "----> 2 |
|
447 | ("next", "----> 2 helpers_helper()"), | |
430 | ("next", "--Return--"), |
|
448 | ("next", "--Return--"), | |
431 | ("next", "----> 5 __debuggerskip__ = False"), |
|
449 | ("next", "----> 5 __debuggerskip__ = False"), | |
432 | ]: |
|
450 | ]: | |
@@ -439,6 +457,62 b' def test_decorator_skip_disabled():' | |||||
439 |
|
457 | |||
440 |
|
458 | |||
441 | @skip_win32 |
|
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 | def test_where_erase_value(): |
|
516 | def test_where_erase_value(): | |
443 | """Test that `where` does not access f_locals and erase values.""" |
|
517 | """Test that `where` does not access f_locals and erase values.""" | |
444 | import pexpect |
|
518 | import pexpect |
General Comments 0
You need to be logged in to leave comments.
Login now