1# Testing the line trace facility. 2 3from test import support 4import unittest 5import sys 6import difflib 7import gc 8from functools import wraps 9import asyncio 10 11 12class tracecontext: 13 """Context manager that traces its enter and exit.""" 14 def __init__(self, output, value): 15 self.output = output 16 self.value = value 17 18 def __enter__(self): 19 self.output.append(self.value) 20 21 def __exit__(self, *exc_info): 22 self.output.append(-self.value) 23 24class asynctracecontext: 25 """Asynchronous context manager that traces its aenter and aexit.""" 26 def __init__(self, output, value): 27 self.output = output 28 self.value = value 29 30 async def __aenter__(self): 31 self.output.append(self.value) 32 33 async def __aexit__(self, *exc_info): 34 self.output.append(-self.value) 35 36async def asynciter(iterable): 37 """Convert an iterable to an asynchronous iterator.""" 38 for x in iterable: 39 yield x 40 41 42# A very basic example. If this fails, we're in deep trouble. 43def basic(): 44 return 1 45 46basic.events = [(0, 'call'), 47 (1, 'line'), 48 (1, 'return')] 49 50# Many of the tests below are tricky because they involve pass statements. 51# If there is implicit control flow around a pass statement (in an except 52# clause or else clause) under what conditions do you set a line number 53# following that clause? 54 55 56# Some constructs like "while 0:", "if 0:" or "if 1:...else:..." are optimized 57# away. No code # exists for them, so the line numbers skip directly from 58# "del x" to "x = 1". 59def arigo_example0(): 60 x = 1 61 del x 62 while 0: 63 pass 64 x = 1 65 66arigo_example0.events = [(0, 'call'), 67 (1, 'line'), 68 (2, 'line'), 69 (5, 'line'), 70 (5, 'return')] 71 72def arigo_example1(): 73 x = 1 74 del x 75 if 0: 76 pass 77 x = 1 78 79arigo_example1.events = [(0, 'call'), 80 (1, 'line'), 81 (2, 'line'), 82 (5, 'line'), 83 (5, 'return')] 84 85def arigo_example2(): 86 x = 1 87 del x 88 if 1: 89 x = 1 90 else: 91 pass 92 return None 93 94arigo_example2.events = [(0, 'call'), 95 (1, 'line'), 96 (2, 'line'), 97 (4, 'line'), 98 (7, 'line'), 99 (7, 'return')] 100 101 102# check that lines consisting of just one instruction get traced: 103def one_instr_line(): 104 x = 1 105 del x 106 x = 1 107 108one_instr_line.events = [(0, 'call'), 109 (1, 'line'), 110 (2, 'line'), 111 (3, 'line'), 112 (3, 'return')] 113 114def no_pop_tops(): # 0 115 x = 1 # 1 116 for a in range(2): # 2 117 if a: # 3 118 x = 1 # 4 119 else: # 5 120 x = 1 # 6 121 122no_pop_tops.events = [(0, 'call'), 123 (1, 'line'), 124 (2, 'line'), 125 (3, 'line'), 126 (6, 'line'), 127 (2, 'line'), 128 (3, 'line'), 129 (4, 'line'), 130 (2, 'line'), 131 (2, 'return')] 132 133def no_pop_blocks(): 134 y = 1 135 while not y: 136 bla 137 x = 1 138 139no_pop_blocks.events = [(0, 'call'), 140 (1, 'line'), 141 (2, 'line'), 142 (4, 'line'), 143 (4, 'return')] 144 145def called(): # line -3 146 x = 1 147 148def call(): # line 0 149 called() 150 151call.events = [(0, 'call'), 152 (1, 'line'), 153 (-3, 'call'), 154 (-2, 'line'), 155 (-2, 'return'), 156 (1, 'return')] 157 158def raises(): 159 raise Exception 160 161def test_raise(): 162 try: 163 raises() 164 except Exception: 165 pass 166 167test_raise.events = [(0, 'call'), 168 (1, 'line'), 169 (2, 'line'), 170 (-3, 'call'), 171 (-2, 'line'), 172 (-2, 'exception'), 173 (-2, 'return'), 174 (2, 'exception'), 175 (3, 'line'), 176 (4, 'line'), 177 (4, 'return')] 178 179def _settrace_and_return(tracefunc): 180 sys.settrace(tracefunc) 181 sys._getframe().f_back.f_trace = tracefunc 182def settrace_and_return(tracefunc): 183 _settrace_and_return(tracefunc) 184 185settrace_and_return.events = [(1, 'return')] 186 187def _settrace_and_raise(tracefunc): 188 sys.settrace(tracefunc) 189 sys._getframe().f_back.f_trace = tracefunc 190 raise RuntimeError 191def settrace_and_raise(tracefunc): 192 try: 193 _settrace_and_raise(tracefunc) 194 except RuntimeError: 195 pass 196 197settrace_and_raise.events = [(2, 'exception'), 198 (3, 'line'), 199 (4, 'line'), 200 (4, 'return')] 201 202# implicit return example 203# This test is interesting because of the else: pass 204# part of the code. The code generate for the true 205# part of the if contains a jump past the else branch. 206# The compiler then generates an implicit "return None" 207# Internally, the compiler visits the pass statement 208# and stores its line number for use on the next instruction. 209# The next instruction is the implicit return None. 210def ireturn_example(): 211 a = 5 212 b = 5 213 if a == b: 214 b = a+1 215 else: 216 pass 217 218ireturn_example.events = [(0, 'call'), 219 (1, 'line'), 220 (2, 'line'), 221 (3, 'line'), 222 (4, 'line'), 223 (6, 'line'), 224 (6, 'return')] 225 226# Tight loop with while(1) example (SF #765624) 227def tightloop_example(): 228 items = range(0, 3) 229 try: 230 i = 0 231 while 1: 232 b = items[i]; i+=1 233 except IndexError: 234 pass 235 236tightloop_example.events = [(0, 'call'), 237 (1, 'line'), 238 (2, 'line'), 239 (3, 'line'), 240 (5, 'line'), 241 (5, 'line'), 242 (5, 'line'), 243 (5, 'line'), 244 (5, 'exception'), 245 (6, 'line'), 246 (7, 'line'), 247 (7, 'return')] 248 249def tighterloop_example(): 250 items = range(1, 4) 251 try: 252 i = 0 253 while 1: i = items[i] 254 except IndexError: 255 pass 256 257tighterloop_example.events = [(0, 'call'), 258 (1, 'line'), 259 (2, 'line'), 260 (3, 'line'), 261 (4, 'line'), 262 (4, 'line'), 263 (4, 'line'), 264 (4, 'line'), 265 (4, 'exception'), 266 (5, 'line'), 267 (6, 'line'), 268 (6, 'return')] 269 270def generator_function(): 271 try: 272 yield True 273 "continued" 274 finally: 275 "finally" 276def generator_example(): 277 # any() will leave the generator before its end 278 x = any(generator_function()) 279 280 # the following lines were not traced 281 for x in range(10): 282 y = x 283 284generator_example.events = ([(0, 'call'), 285 (2, 'line'), 286 (-6, 'call'), 287 (-5, 'line'), 288 (-4, 'line'), 289 (-4, 'return'), 290 (-4, 'call'), 291 (-4, 'exception'), 292 (-1, 'line'), 293 (-1, 'return')] + 294 [(5, 'line'), (6, 'line')] * 10 + 295 [(5, 'line'), (5, 'return')]) 296 297 298class Tracer: 299 def __init__(self, trace_line_events=None, trace_opcode_events=None): 300 self.trace_line_events = trace_line_events 301 self.trace_opcode_events = trace_opcode_events 302 self.events = [] 303 304 def _reconfigure_frame(self, frame): 305 if self.trace_line_events is not None: 306 frame.f_trace_lines = self.trace_line_events 307 if self.trace_opcode_events is not None: 308 frame.f_trace_opcodes = self.trace_opcode_events 309 310 def trace(self, frame, event, arg): 311 self._reconfigure_frame(frame) 312 self.events.append((frame.f_lineno, event)) 313 return self.trace 314 315 def traceWithGenexp(self, frame, event, arg): 316 self._reconfigure_frame(frame) 317 (o for o in [1]) 318 self.events.append((frame.f_lineno, event)) 319 return self.trace 320 321 322class TraceTestCase(unittest.TestCase): 323 324 # Disable gc collection when tracing, otherwise the 325 # deallocators may be traced as well. 326 def setUp(self): 327 self.using_gc = gc.isenabled() 328 gc.disable() 329 self.addCleanup(sys.settrace, sys.gettrace()) 330 331 def tearDown(self): 332 if self.using_gc: 333 gc.enable() 334 335 @staticmethod 336 def make_tracer(): 337 """Helper to allow test subclasses to configure tracers differently""" 338 return Tracer() 339 340 def compare_events(self, line_offset, events, expected_events): 341 events = [(l - line_offset, e) for (l, e) in events] 342 if events != expected_events: 343 self.fail( 344 "events did not match expectation:\n" + 345 "\n".join(difflib.ndiff([str(x) for x in expected_events], 346 [str(x) for x in events]))) 347 348 def run_and_compare(self, func, events): 349 tracer = self.make_tracer() 350 sys.settrace(tracer.trace) 351 func() 352 sys.settrace(None) 353 self.compare_events(func.__code__.co_firstlineno, 354 tracer.events, events) 355 356 def run_test(self, func): 357 self.run_and_compare(func, func.events) 358 359 def run_test2(self, func): 360 tracer = self.make_tracer() 361 func(tracer.trace) 362 sys.settrace(None) 363 self.compare_events(func.__code__.co_firstlineno, 364 tracer.events, func.events) 365 366 def test_set_and_retrieve_none(self): 367 sys.settrace(None) 368 assert sys.gettrace() is None 369 370 def test_set_and_retrieve_func(self): 371 def fn(*args): 372 pass 373 374 sys.settrace(fn) 375 try: 376 assert sys.gettrace() is fn 377 finally: 378 sys.settrace(None) 379 380 def test_01_basic(self): 381 self.run_test(basic) 382 def test_02_arigo0(self): 383 self.run_test(arigo_example0) 384 def test_02_arigo1(self): 385 self.run_test(arigo_example1) 386 def test_02_arigo2(self): 387 self.run_test(arigo_example2) 388 def test_03_one_instr(self): 389 self.run_test(one_instr_line) 390 def test_04_no_pop_blocks(self): 391 self.run_test(no_pop_blocks) 392 def test_05_no_pop_tops(self): 393 self.run_test(no_pop_tops) 394 def test_06_call(self): 395 self.run_test(call) 396 def test_07_raise(self): 397 self.run_test(test_raise) 398 399 def test_08_settrace_and_return(self): 400 self.run_test2(settrace_and_return) 401 def test_09_settrace_and_raise(self): 402 self.run_test2(settrace_and_raise) 403 def test_10_ireturn(self): 404 self.run_test(ireturn_example) 405 def test_11_tightloop(self): 406 self.run_test(tightloop_example) 407 def test_12_tighterloop(self): 408 self.run_test(tighterloop_example) 409 410 def test_13_genexp(self): 411 self.run_test(generator_example) 412 # issue1265: if the trace function contains a generator, 413 # and if the traced function contains another generator 414 # that is not completely exhausted, the trace stopped. 415 # Worse: the 'finally' clause was not invoked. 416 tracer = self.make_tracer() 417 sys.settrace(tracer.traceWithGenexp) 418 generator_example() 419 sys.settrace(None) 420 self.compare_events(generator_example.__code__.co_firstlineno, 421 tracer.events, generator_example.events) 422 423 def test_14_onliner_if(self): 424 def onliners(): 425 if True: x=False 426 else: x=True 427 return 0 428 self.run_and_compare( 429 onliners, 430 [(0, 'call'), 431 (1, 'line'), 432 (3, 'line'), 433 (3, 'return')]) 434 435 def test_15_loops(self): 436 # issue1750076: "while" expression is skipped by debugger 437 def for_example(): 438 for x in range(2): 439 pass 440 self.run_and_compare( 441 for_example, 442 [(0, 'call'), 443 (1, 'line'), 444 (2, 'line'), 445 (1, 'line'), 446 (2, 'line'), 447 (1, 'line'), 448 (1, 'return')]) 449 450 def while_example(): 451 # While expression should be traced on every loop 452 x = 2 453 while x > 0: 454 x -= 1 455 self.run_and_compare( 456 while_example, 457 [(0, 'call'), 458 (2, 'line'), 459 (3, 'line'), 460 (4, 'line'), 461 (3, 'line'), 462 (4, 'line'), 463 (3, 'line'), 464 (3, 'return')]) 465 466 def test_16_blank_lines(self): 467 namespace = {} 468 exec("def f():\n" + "\n" * 256 + " pass", namespace) 469 self.run_and_compare( 470 namespace["f"], 471 [(0, 'call'), 472 (257, 'line'), 473 (257, 'return')]) 474 475 def test_17_none_f_trace(self): 476 # Issue 20041: fix TypeError when f_trace is set to None. 477 def func(): 478 sys._getframe().f_trace = None 479 lineno = 2 480 self.run_and_compare(func, 481 [(0, 'call'), 482 (1, 'line')]) 483 484 def test_18_except_with_name(self): 485 def func(): 486 try: 487 try: 488 raise Exception 489 except Exception as e: 490 raise 491 x = "Something" 492 y = "Something" 493 except Exception: 494 pass 495 496 self.run_and_compare(func, 497 [(0, 'call'), 498 (1, 'line'), 499 (2, 'line'), 500 (3, 'line'), 501 (3, 'exception'), 502 (4, 'line'), 503 (5, 'line'), 504 (8, 'line'), 505 (9, 'line'), 506 (9, 'return')]) 507 508 def test_19_except_with_finally(self): 509 def func(): 510 try: 511 try: 512 raise Exception 513 finally: 514 y = "Something" 515 except Exception: 516 b = 23 517 518 self.run_and_compare(func, 519 [(0, 'call'), 520 (1, 'line'), 521 (2, 'line'), 522 (3, 'line'), 523 (3, 'exception'), 524 (5, 'line'), 525 (6, 'line'), 526 (7, 'line'), 527 (7, 'return')]) 528 529 def test_20_async_for_loop(self): 530 class AsyncIteratorWrapper: 531 def __init__(self, obj): 532 self._it = iter(obj) 533 534 def __aiter__(self): 535 return self 536 537 async def __anext__(self): 538 try: 539 return next(self._it) 540 except StopIteration: 541 raise StopAsyncIteration 542 543 async def doit_async(): 544 async for letter in AsyncIteratorWrapper("abc"): 545 x = letter 546 y = 42 547 548 def run(tracer): 549 x = doit_async() 550 try: 551 sys.settrace(tracer) 552 x.send(None) 553 finally: 554 sys.settrace(None) 555 556 tracer = self.make_tracer() 557 events = [ 558 (0, 'call'), 559 (1, 'line'), 560 (-12, 'call'), 561 (-11, 'line'), 562 (-11, 'return'), 563 (-9, 'call'), 564 (-8, 'line'), 565 (-8, 'return'), 566 (-6, 'call'), 567 (-5, 'line'), 568 (-4, 'line'), 569 (-4, 'return'), 570 (1, 'exception'), 571 (2, 'line'), 572 (1, 'line'), 573 (-6, 'call'), 574 (-5, 'line'), 575 (-4, 'line'), 576 (-4, 'return'), 577 (1, 'exception'), 578 (2, 'line'), 579 (1, 'line'), 580 (-6, 'call'), 581 (-5, 'line'), 582 (-4, 'line'), 583 (-4, 'return'), 584 (1, 'exception'), 585 (2, 'line'), 586 (1, 'line'), 587 (-6, 'call'), 588 (-5, 'line'), 589 (-4, 'line'), 590 (-4, 'exception'), 591 (-3, 'line'), 592 (-2, 'line'), 593 (-2, 'exception'), 594 (-2, 'return'), 595 (1, 'exception'), 596 (3, 'line'), 597 (3, 'return')] 598 try: 599 run(tracer.trace) 600 except Exception: 601 pass 602 self.compare_events(doit_async.__code__.co_firstlineno, 603 tracer.events, events) 604 605 606class SkipLineEventsTraceTestCase(TraceTestCase): 607 """Repeat the trace tests, but with per-line events skipped""" 608 609 def compare_events(self, line_offset, events, expected_events): 610 skip_line_events = [e for e in expected_events if e[1] != 'line'] 611 super().compare_events(line_offset, events, skip_line_events) 612 613 @staticmethod 614 def make_tracer(): 615 return Tracer(trace_line_events=False) 616 617 618@support.cpython_only 619class TraceOpcodesTestCase(TraceTestCase): 620 """Repeat the trace tests, but with per-opcodes events enabled""" 621 622 def compare_events(self, line_offset, events, expected_events): 623 skip_opcode_events = [e for e in events if e[1] != 'opcode'] 624 if len(events) > 1: 625 self.assertLess(len(skip_opcode_events), len(events), 626 msg="No 'opcode' events received by the tracer") 627 super().compare_events(line_offset, skip_opcode_events, expected_events) 628 629 @staticmethod 630 def make_tracer(): 631 return Tracer(trace_opcode_events=True) 632 633 634class RaisingTraceFuncTestCase(unittest.TestCase): 635 def setUp(self): 636 self.addCleanup(sys.settrace, sys.gettrace()) 637 638 def trace(self, frame, event, arg): 639 """A trace function that raises an exception in response to a 640 specific trace event.""" 641 if event == self.raiseOnEvent: 642 raise ValueError # just something that isn't RuntimeError 643 else: 644 return self.trace 645 646 def f(self): 647 """The function to trace; raises an exception if that's the case 648 we're testing, so that the 'exception' trace event fires.""" 649 if self.raiseOnEvent == 'exception': 650 x = 0 651 y = 1/x 652 else: 653 return 1 654 655 def run_test_for_event(self, event): 656 """Tests that an exception raised in response to the given event is 657 handled OK.""" 658 self.raiseOnEvent = event 659 try: 660 for i in range(sys.getrecursionlimit() + 1): 661 sys.settrace(self.trace) 662 try: 663 self.f() 664 except ValueError: 665 pass 666 else: 667 self.fail("exception not raised!") 668 except RuntimeError: 669 self.fail("recursion counter not reset") 670 671 # Test the handling of exceptions raised by each kind of trace event. 672 def test_call(self): 673 self.run_test_for_event('call') 674 def test_line(self): 675 self.run_test_for_event('line') 676 def test_return(self): 677 self.run_test_for_event('return') 678 def test_exception(self): 679 self.run_test_for_event('exception') 680 681 def test_trash_stack(self): 682 def f(): 683 for i in range(5): 684 print(i) # line tracing will raise an exception at this line 685 686 def g(frame, why, extra): 687 if (why == 'line' and 688 frame.f_lineno == f.__code__.co_firstlineno + 2): 689 raise RuntimeError("i am crashing") 690 return g 691 692 sys.settrace(g) 693 try: 694 f() 695 except RuntimeError: 696 # the test is really that this doesn't segfault: 697 import gc 698 gc.collect() 699 else: 700 self.fail("exception not propagated") 701 702 703 def test_exception_arguments(self): 704 def f(): 705 x = 0 706 # this should raise an error 707 x.no_such_attr 708 def g(frame, event, arg): 709 if (event == 'exception'): 710 type, exception, trace = arg 711 self.assertIsInstance(exception, Exception) 712 return g 713 714 existing = sys.gettrace() 715 try: 716 sys.settrace(g) 717 try: 718 f() 719 except AttributeError: 720 # this is expected 721 pass 722 finally: 723 sys.settrace(existing) 724 725 726# 'Jump' tests: assigning to frame.f_lineno within a trace function 727# moves the execution position - it's how debuggers implement a Jump 728# command (aka. "Set next statement"). 729 730class JumpTracer: 731 """Defines a trace function that jumps from one place to another.""" 732 733 def __init__(self, function, jumpFrom, jumpTo, event='line', 734 decorated=False): 735 self.code = function.__code__ 736 self.jumpFrom = jumpFrom 737 self.jumpTo = jumpTo 738 self.event = event 739 self.firstLine = None if decorated else self.code.co_firstlineno 740 self.done = False 741 742 def trace(self, frame, event, arg): 743 if self.done: 744 return 745 # frame.f_code.co_firstlineno is the first line of the decorator when 746 # 'function' is decorated and the decorator may be written using 747 # multiple physical lines when it is too long. Use the first line 748 # trace event in 'function' to find the first line of 'function'. 749 if (self.firstLine is None and frame.f_code == self.code and 750 event == 'line'): 751 self.firstLine = frame.f_lineno - 1 752 if (event == self.event and self.firstLine and 753 frame.f_lineno == self.firstLine + self.jumpFrom): 754 f = frame 755 while f is not None and f.f_code != self.code: 756 f = f.f_back 757 if f is not None: 758 # Cope with non-integer self.jumpTo (because of 759 # no_jump_to_non_integers below). 760 try: 761 frame.f_lineno = self.firstLine + self.jumpTo 762 except TypeError: 763 frame.f_lineno = self.jumpTo 764 self.done = True 765 return self.trace 766 767# This verifies the line-numbers-must-be-integers rule. 768def no_jump_to_non_integers(output): 769 try: 770 output.append(2) 771 except ValueError as e: 772 output.append('integer' in str(e)) 773 774# This verifies that you can't set f_lineno via _getframe or similar 775# trickery. 776def no_jump_without_trace_function(): 777 try: 778 previous_frame = sys._getframe().f_back 779 previous_frame.f_lineno = previous_frame.f_lineno 780 except ValueError as e: 781 # This is the exception we wanted; make sure the error message 782 # talks about trace functions. 783 if 'trace' not in str(e): 784 raise 785 else: 786 # Something's wrong - the expected exception wasn't raised. 787 raise AssertionError("Trace-function-less jump failed to fail") 788 789 790class JumpTestCase(unittest.TestCase): 791 def setUp(self): 792 self.addCleanup(sys.settrace, sys.gettrace()) 793 sys.settrace(None) 794 795 def compare_jump_output(self, expected, received): 796 if received != expected: 797 self.fail( "Outputs don't match:\n" + 798 "Expected: " + repr(expected) + "\n" + 799 "Received: " + repr(received)) 800 801 def run_test(self, func, jumpFrom, jumpTo, expected, error=None, 802 event='line', decorated=False): 803 tracer = JumpTracer(func, jumpFrom, jumpTo, event, decorated) 804 sys.settrace(tracer.trace) 805 output = [] 806 if error is None: 807 func(output) 808 else: 809 with self.assertRaisesRegex(*error): 810 func(output) 811 sys.settrace(None) 812 self.compare_jump_output(expected, output) 813 814 def run_async_test(self, func, jumpFrom, jumpTo, expected, error=None, 815 event='line', decorated=False): 816 tracer = JumpTracer(func, jumpFrom, jumpTo, event, decorated) 817 sys.settrace(tracer.trace) 818 output = [] 819 if error is None: 820 asyncio.run(func(output)) 821 else: 822 with self.assertRaisesRegex(*error): 823 asyncio.run(func(output)) 824 sys.settrace(None) 825 asyncio.set_event_loop_policy(None) 826 self.compare_jump_output(expected, output) 827 828 def jump_test(jumpFrom, jumpTo, expected, error=None, event='line'): 829 """Decorator that creates a test that makes a jump 830 from one place to another in the following code. 831 """ 832 def decorator(func): 833 @wraps(func) 834 def test(self): 835 self.run_test(func, jumpFrom, jumpTo, expected, 836 error=error, event=event, decorated=True) 837 return test 838 return decorator 839 840 def async_jump_test(jumpFrom, jumpTo, expected, error=None, event='line'): 841 """Decorator that creates a test that makes a jump 842 from one place to another in the following asynchronous code. 843 """ 844 def decorator(func): 845 @wraps(func) 846 def test(self): 847 self.run_async_test(func, jumpFrom, jumpTo, expected, 848 error=error, event=event, decorated=True) 849 return test 850 return decorator 851 852 ## The first set of 'jump' tests are for things that are allowed: 853 854 @jump_test(1, 3, [3]) 855 def test_jump_simple_forwards(output): 856 output.append(1) 857 output.append(2) 858 output.append(3) 859 860 @jump_test(2, 1, [1, 1, 2]) 861 def test_jump_simple_backwards(output): 862 output.append(1) 863 output.append(2) 864 865 @jump_test(3, 5, [2, 5]) 866 def test_jump_out_of_block_forwards(output): 867 for i in 1, 2: 868 output.append(2) 869 for j in [3]: # Also tests jumping over a block 870 output.append(4) 871 output.append(5) 872 873 @jump_test(6, 1, [1, 3, 5, 1, 3, 5, 6, 7]) 874 def test_jump_out_of_block_backwards(output): 875 output.append(1) 876 for i in [1]: 877 output.append(3) 878 for j in [2]: # Also tests jumping over a block 879 output.append(5) 880 output.append(6) 881 output.append(7) 882 883 @async_jump_test(4, 5, [3, 5]) 884 async def test_jump_out_of_async_for_block_forwards(output): 885 for i in [1]: 886 async for i in asynciter([1, 2]): 887 output.append(3) 888 output.append(4) 889 output.append(5) 890 891 @async_jump_test(5, 2, [2, 4, 2, 4, 5, 6]) 892 async def test_jump_out_of_async_for_block_backwards(output): 893 for i in [1]: 894 output.append(2) 895 async for i in asynciter([1]): 896 output.append(4) 897 output.append(5) 898 output.append(6) 899 900 @jump_test(1, 2, [3]) 901 def test_jump_to_codeless_line(output): 902 output.append(1) 903 # Jumping to this line should skip to the next one. 904 output.append(3) 905 906 @jump_test(2, 2, [1, 2, 3]) 907 def test_jump_to_same_line(output): 908 output.append(1) 909 output.append(2) 910 output.append(3) 911 912 # Tests jumping within a finally block, and over one. 913 @jump_test(4, 9, [2, 9]) 914 def test_jump_in_nested_finally(output): 915 try: 916 output.append(2) 917 finally: 918 output.append(4) 919 try: 920 output.append(6) 921 finally: 922 output.append(8) 923 output.append(9) 924 925 @jump_test(6, 7, [2, 7], (ZeroDivisionError, '')) 926 def test_jump_in_nested_finally_2(output): 927 try: 928 output.append(2) 929 1/0 930 return 931 finally: 932 output.append(6) 933 output.append(7) 934 output.append(8) 935 936 @jump_test(6, 11, [2, 11], (ZeroDivisionError, '')) 937 def test_jump_in_nested_finally_3(output): 938 try: 939 output.append(2) 940 1/0 941 return 942 finally: 943 output.append(6) 944 try: 945 output.append(8) 946 finally: 947 output.append(10) 948 output.append(11) 949 output.append(12) 950 951 @jump_test(5, 11, [2, 4], (ValueError, 'unreachable')) 952 def test_no_jump_over_return_try_finally_in_finally_block(output): 953 try: 954 output.append(2) 955 finally: 956 output.append(4) 957 output.append(5) 958 return 959 try: 960 output.append(8) 961 finally: 962 output.append(10) 963 pass 964 output.append(12) 965 966 @jump_test(3, 4, [1], (ValueError, 'unreachable')) 967 def test_no_jump_infinite_while_loop(output): 968 output.append(1) 969 while True: 970 output.append(3) 971 output.append(4) 972 973 @jump_test(2, 4, [4, 4]) 974 def test_jump_forwards_into_while_block(output): 975 i = 1 976 output.append(2) 977 while i <= 2: 978 output.append(4) 979 i += 1 980 981 @jump_test(5, 3, [3, 3, 3, 5]) 982 def test_jump_backwards_into_while_block(output): 983 i = 1 984 while i <= 2: 985 output.append(3) 986 i += 1 987 output.append(5) 988 989 @jump_test(2, 3, [1, 3]) 990 def test_jump_forwards_out_of_with_block(output): 991 with tracecontext(output, 1): 992 output.append(2) 993 output.append(3) 994 995 @async_jump_test(2, 3, [1, 3]) 996 async def test_jump_forwards_out_of_async_with_block(output): 997 async with asynctracecontext(output, 1): 998 output.append(2) 999 output.append(3) 1000 1001 @jump_test(3, 1, [1, 2, 1, 2, 3, -2]) 1002 def test_jump_backwards_out_of_with_block(output): 1003 output.append(1) 1004 with tracecontext(output, 2): 1005 output.append(3) 1006 1007 @async_jump_test(3, 1, [1, 2, 1, 2, 3, -2]) 1008 async def test_jump_backwards_out_of_async_with_block(output): 1009 output.append(1) 1010 async with asynctracecontext(output, 2): 1011 output.append(3) 1012 1013 @jump_test(2, 5, [5]) 1014 def test_jump_forwards_out_of_try_finally_block(output): 1015 try: 1016 output.append(2) 1017 finally: 1018 output.append(4) 1019 output.append(5) 1020 1021 @jump_test(3, 1, [1, 1, 3, 5]) 1022 def test_jump_backwards_out_of_try_finally_block(output): 1023 output.append(1) 1024 try: 1025 output.append(3) 1026 finally: 1027 output.append(5) 1028 1029 @jump_test(2, 6, [6]) 1030 def test_jump_forwards_out_of_try_except_block(output): 1031 try: 1032 output.append(2) 1033 except: 1034 output.append(4) 1035 raise 1036 output.append(6) 1037 1038 @jump_test(3, 1, [1, 1, 3]) 1039 def test_jump_backwards_out_of_try_except_block(output): 1040 output.append(1) 1041 try: 1042 output.append(3) 1043 except: 1044 output.append(5) 1045 raise 1046 1047 @jump_test(5, 7, [4, 7, 8]) 1048 def test_jump_between_except_blocks(output): 1049 try: 1050 1/0 1051 except ZeroDivisionError: 1052 output.append(4) 1053 output.append(5) 1054 except FloatingPointError: 1055 output.append(7) 1056 output.append(8) 1057 1058 @jump_test(5, 6, [4, 6, 7]) 1059 def test_jump_within_except_block(output): 1060 try: 1061 1/0 1062 except: 1063 output.append(4) 1064 output.append(5) 1065 output.append(6) 1066 output.append(7) 1067 1068 @jump_test(2, 4, [1, 4, 5, -4]) 1069 def test_jump_across_with(output): 1070 output.append(1) 1071 with tracecontext(output, 2): 1072 output.append(3) 1073 with tracecontext(output, 4): 1074 output.append(5) 1075 1076 @async_jump_test(2, 4, [1, 4, 5, -4]) 1077 async def test_jump_across_async_with(output): 1078 output.append(1) 1079 async with asynctracecontext(output, 2): 1080 output.append(3) 1081 async with asynctracecontext(output, 4): 1082 output.append(5) 1083 1084 @jump_test(4, 5, [1, 3, 5, 6]) 1085 def test_jump_out_of_with_block_within_for_block(output): 1086 output.append(1) 1087 for i in [1]: 1088 with tracecontext(output, 3): 1089 output.append(4) 1090 output.append(5) 1091 output.append(6) 1092 1093 @async_jump_test(4, 5, [1, 3, 5, 6]) 1094 async def test_jump_out_of_async_with_block_within_for_block(output): 1095 output.append(1) 1096 for i in [1]: 1097 async with asynctracecontext(output, 3): 1098 output.append(4) 1099 output.append(5) 1100 output.append(6) 1101 1102 @jump_test(4, 5, [1, 2, 3, 5, -2, 6]) 1103 def test_jump_out_of_with_block_within_with_block(output): 1104 output.append(1) 1105 with tracecontext(output, 2): 1106 with tracecontext(output, 3): 1107 output.append(4) 1108 output.append(5) 1109 output.append(6) 1110 1111 @async_jump_test(4, 5, [1, 2, 3, 5, -2, 6]) 1112 async def test_jump_out_of_async_with_block_within_with_block(output): 1113 output.append(1) 1114 with tracecontext(output, 2): 1115 async with asynctracecontext(output, 3): 1116 output.append(4) 1117 output.append(5) 1118 output.append(6) 1119 1120 @jump_test(5, 6, [2, 4, 6, 7]) 1121 def test_jump_out_of_with_block_within_finally_block(output): 1122 try: 1123 output.append(2) 1124 finally: 1125 with tracecontext(output, 4): 1126 output.append(5) 1127 output.append(6) 1128 output.append(7) 1129 1130 @async_jump_test(5, 6, [2, 4, 6, 7]) 1131 async def test_jump_out_of_async_with_block_within_finally_block(output): 1132 try: 1133 output.append(2) 1134 finally: 1135 async with asynctracecontext(output, 4): 1136 output.append(5) 1137 output.append(6) 1138 output.append(7) 1139 1140 @jump_test(8, 11, [1, 3, 5, 11, 12]) 1141 def test_jump_out_of_complex_nested_blocks(output): 1142 output.append(1) 1143 for i in [1]: 1144 output.append(3) 1145 for j in [1, 2]: 1146 output.append(5) 1147 try: 1148 for k in [1, 2]: 1149 output.append(8) 1150 finally: 1151 output.append(10) 1152 output.append(11) 1153 output.append(12) 1154 1155 @jump_test(3, 5, [1, 2, 5]) 1156 def test_jump_out_of_with_assignment(output): 1157 output.append(1) 1158 with tracecontext(output, 2) \ 1159 as x: 1160 output.append(4) 1161 output.append(5) 1162 1163 @async_jump_test(3, 5, [1, 2, 5]) 1164 async def test_jump_out_of_async_with_assignment(output): 1165 output.append(1) 1166 async with asynctracecontext(output, 2) \ 1167 as x: 1168 output.append(4) 1169 output.append(5) 1170 1171 @jump_test(3, 6, [1, 6, 8, 9]) 1172 def test_jump_over_return_in_try_finally_block(output): 1173 output.append(1) 1174 try: 1175 output.append(3) 1176 if not output: # always false 1177 return 1178 output.append(6) 1179 finally: 1180 output.append(8) 1181 output.append(9) 1182 1183 @jump_test(5, 8, [1, 3, 8, 10, 11, 13]) 1184 def test_jump_over_break_in_try_finally_block(output): 1185 output.append(1) 1186 while True: 1187 output.append(3) 1188 try: 1189 output.append(5) 1190 if not output: # always false 1191 break 1192 output.append(8) 1193 finally: 1194 output.append(10) 1195 output.append(11) 1196 break 1197 output.append(13) 1198 1199 @jump_test(1, 7, [7, 8]) 1200 def test_jump_over_for_block_before_else(output): 1201 output.append(1) 1202 if not output: # always false 1203 for i in [3]: 1204 output.append(4) 1205 else: 1206 output.append(6) 1207 output.append(7) 1208 output.append(8) 1209 1210 @async_jump_test(1, 7, [7, 8]) 1211 async def test_jump_over_async_for_block_before_else(output): 1212 output.append(1) 1213 if not output: # always false 1214 async for i in asynciter([3]): 1215 output.append(4) 1216 else: 1217 output.append(6) 1218 output.append(7) 1219 output.append(8) 1220 1221 # The second set of 'jump' tests are for things that are not allowed: 1222 1223 @jump_test(2, 3, [1], (ValueError, 'after')) 1224 def test_no_jump_too_far_forwards(output): 1225 output.append(1) 1226 output.append(2) 1227 1228 @jump_test(2, -2, [1], (ValueError, 'before')) 1229 def test_no_jump_too_far_backwards(output): 1230 output.append(1) 1231 output.append(2) 1232 1233 # Test each kind of 'except' line. 1234 @jump_test(2, 3, [4], (ValueError, 'except')) 1235 def test_no_jump_to_except_1(output): 1236 try: 1237 output.append(2) 1238 except: 1239 output.append(4) 1240 raise 1241 1242 @jump_test(2, 3, [4], (ValueError, 'except')) 1243 def test_no_jump_to_except_2(output): 1244 try: 1245 output.append(2) 1246 except ValueError: 1247 output.append(4) 1248 raise 1249 1250 @jump_test(2, 3, [4], (ValueError, 'except')) 1251 def test_no_jump_to_except_3(output): 1252 try: 1253 output.append(2) 1254 except ValueError as e: 1255 output.append(4) 1256 raise e 1257 1258 @jump_test(2, 3, [4], (ValueError, 'except')) 1259 def test_no_jump_to_except_4(output): 1260 try: 1261 output.append(2) 1262 except (ValueError, RuntimeError) as e: 1263 output.append(4) 1264 raise e 1265 1266 @jump_test(1, 3, [], (ValueError, 'into')) 1267 def test_no_jump_forwards_into_for_block(output): 1268 output.append(1) 1269 for i in 1, 2: 1270 output.append(3) 1271 1272 @async_jump_test(1, 3, [], (ValueError, 'into')) 1273 async def test_no_jump_forwards_into_async_for_block(output): 1274 output.append(1) 1275 async for i in asynciter([1, 2]): 1276 output.append(3) 1277 1278 @jump_test(3, 2, [2, 2], (ValueError, 'into')) 1279 def test_no_jump_backwards_into_for_block(output): 1280 for i in 1, 2: 1281 output.append(2) 1282 output.append(3) 1283 1284 @async_jump_test(3, 2, [2, 2], (ValueError, 'into')) 1285 async def test_no_jump_backwards_into_async_for_block(output): 1286 async for i in asynciter([1, 2]): 1287 output.append(2) 1288 output.append(3) 1289 1290 @jump_test(1, 3, [], (ValueError, 'into')) 1291 def test_no_jump_forwards_into_with_block(output): 1292 output.append(1) 1293 with tracecontext(output, 2): 1294 output.append(3) 1295 1296 @async_jump_test(1, 3, [], (ValueError, 'into')) 1297 async def test_no_jump_forwards_into_async_with_block(output): 1298 output.append(1) 1299 async with asynctracecontext(output, 2): 1300 output.append(3) 1301 1302 @jump_test(3, 2, [1, 2, -1], (ValueError, 'into')) 1303 def test_no_jump_backwards_into_with_block(output): 1304 with tracecontext(output, 1): 1305 output.append(2) 1306 output.append(3) 1307 1308 @async_jump_test(3, 2, [1, 2, -1], (ValueError, 'into')) 1309 async def test_no_jump_backwards_into_async_with_block(output): 1310 async with asynctracecontext(output, 1): 1311 output.append(2) 1312 output.append(3) 1313 1314 @jump_test(1, 3, [], (ValueError, 'into')) 1315 def test_no_jump_forwards_into_try_finally_block(output): 1316 output.append(1) 1317 try: 1318 output.append(3) 1319 finally: 1320 output.append(5) 1321 1322 @jump_test(5, 2, [2, 4], (ValueError, 'into')) 1323 def test_no_jump_backwards_into_try_finally_block(output): 1324 try: 1325 output.append(2) 1326 finally: 1327 output.append(4) 1328 output.append(5) 1329 1330 @jump_test(1, 3, [], (ValueError, 'into')) 1331 def test_no_jump_forwards_into_try_except_block(output): 1332 output.append(1) 1333 try: 1334 output.append(3) 1335 except: 1336 output.append(5) 1337 raise 1338 1339 @jump_test(6, 2, [2], (ValueError, 'into')) 1340 def test_no_jump_backwards_into_try_except_block(output): 1341 try: 1342 output.append(2) 1343 except: 1344 output.append(4) 1345 raise 1346 output.append(6) 1347 1348 # 'except' with a variable creates an implicit finally block 1349 @jump_test(5, 7, [4], (ValueError, 'into')) 1350 def test_no_jump_between_except_blocks_2(output): 1351 try: 1352 1/0 1353 except ZeroDivisionError: 1354 output.append(4) 1355 output.append(5) 1356 except FloatingPointError as e: 1357 output.append(7) 1358 output.append(8) 1359 1360 @jump_test(1, 5, [5]) 1361 def test_jump_into_finally_block(output): 1362 output.append(1) 1363 try: 1364 output.append(3) 1365 finally: 1366 output.append(5) 1367 1368 @jump_test(3, 6, [2, 6, 7]) 1369 def test_jump_into_finally_block_from_try_block(output): 1370 try: 1371 output.append(2) 1372 output.append(3) 1373 finally: # still executed if the jump is failed 1374 output.append(5) 1375 output.append(6) 1376 output.append(7) 1377 1378 @jump_test(5, 1, [1, 3, 1, 3, 5]) 1379 def test_jump_out_of_finally_block(output): 1380 output.append(1) 1381 try: 1382 output.append(3) 1383 finally: 1384 output.append(5) 1385 1386 @jump_test(1, 5, [], (ValueError, "into an 'except'")) 1387 def test_no_jump_into_bare_except_block(output): 1388 output.append(1) 1389 try: 1390 output.append(3) 1391 except: 1392 output.append(5) 1393 1394 @jump_test(1, 5, [], (ValueError, "into an 'except'")) 1395 def test_no_jump_into_qualified_except_block(output): 1396 output.append(1) 1397 try: 1398 output.append(3) 1399 except Exception: 1400 output.append(5) 1401 1402 @jump_test(3, 6, [2, 5, 6], (ValueError, "into an 'except'")) 1403 def test_no_jump_into_bare_except_block_from_try_block(output): 1404 try: 1405 output.append(2) 1406 output.append(3) 1407 except: # executed if the jump is failed 1408 output.append(5) 1409 output.append(6) 1410 raise 1411 output.append(8) 1412 1413 @jump_test(3, 6, [2], (ValueError, "into an 'except'")) 1414 def test_no_jump_into_qualified_except_block_from_try_block(output): 1415 try: 1416 output.append(2) 1417 output.append(3) 1418 except ZeroDivisionError: 1419 output.append(5) 1420 output.append(6) 1421 raise 1422 output.append(8) 1423 1424 @jump_test(7, 1, [1, 3, 6], (ValueError, "out of an 'except'")) 1425 def test_no_jump_out_of_bare_except_block(output): 1426 output.append(1) 1427 try: 1428 output.append(3) 1429 1/0 1430 except: 1431 output.append(6) 1432 output.append(7) 1433 1434 @jump_test(7, 1, [1, 3, 6], (ValueError, "out of an 'except'")) 1435 def test_no_jump_out_of_qualified_except_block(output): 1436 output.append(1) 1437 try: 1438 output.append(3) 1439 1/0 1440 except Exception: 1441 output.append(6) 1442 output.append(7) 1443 1444 @jump_test(3, 5, [1, 2, 5, -2]) 1445 def test_jump_between_with_blocks(output): 1446 output.append(1) 1447 with tracecontext(output, 2): 1448 output.append(3) 1449 with tracecontext(output, 4): 1450 output.append(5) 1451 1452 @async_jump_test(3, 5, [1, 2, 5, -2]) 1453 async def test_jump_between_async_with_blocks(output): 1454 output.append(1) 1455 async with asynctracecontext(output, 2): 1456 output.append(3) 1457 async with asynctracecontext(output, 4): 1458 output.append(5) 1459 1460 @jump_test(5, 7, [2, 4], (ValueError, "unreachable")) 1461 def test_no_jump_over_return_out_of_finally_block(output): 1462 try: 1463 output.append(2) 1464 finally: 1465 output.append(4) 1466 output.append(5) 1467 return 1468 output.append(7) 1469 1470 @jump_test(7, 4, [1, 6], (ValueError, 'into')) 1471 def test_no_jump_into_for_block_before_else(output): 1472 output.append(1) 1473 if not output: # always false 1474 for i in [3]: 1475 output.append(4) 1476 else: 1477 output.append(6) 1478 output.append(7) 1479 output.append(8) 1480 1481 @async_jump_test(7, 4, [1, 6], (ValueError, 'into')) 1482 async def test_no_jump_into_async_for_block_before_else(output): 1483 output.append(1) 1484 if not output: # always false 1485 async for i in asynciter([3]): 1486 output.append(4) 1487 else: 1488 output.append(6) 1489 output.append(7) 1490 output.append(8) 1491 1492 def test_no_jump_to_non_integers(self): 1493 self.run_test(no_jump_to_non_integers, 2, "Spam", [True]) 1494 1495 def test_no_jump_without_trace_function(self): 1496 # Must set sys.settrace(None) in setUp(), else condition is not 1497 # triggered. 1498 no_jump_without_trace_function() 1499 1500 def test_large_function(self): 1501 d = {} 1502 exec("""def f(output): # line 0 1503 x = 0 # line 1 1504 y = 1 # line 2 1505 ''' # line 3 1506 %s # lines 4-1004 1507 ''' # line 1005 1508 x += 1 # line 1006 1509 output.append(x) # line 1007 1510 return""" % ('\n' * 1000,), d) 1511 f = d['f'] 1512 self.run_test(f, 2, 1007, [0]) 1513 1514 def test_jump_to_firstlineno(self): 1515 # This tests that PDB can jump back to the first line in a 1516 # file. See issue #1689458. It can only be triggered in a 1517 # function call if the function is defined on a single line. 1518 code = compile(""" 1519# Comments don't count. 1520output.append(2) # firstlineno is here. 1521output.append(3) 1522output.append(4) 1523""", "<fake module>", "exec") 1524 class fake_function: 1525 __code__ = code 1526 tracer = JumpTracer(fake_function, 2, 0) 1527 sys.settrace(tracer.trace) 1528 namespace = {"output": []} 1529 exec(code, namespace) 1530 sys.settrace(None) 1531 self.compare_jump_output([2, 3, 2, 3, 4], namespace["output"]) 1532 1533 @jump_test(2, 3, [1], event='call', error=(ValueError, "can't jump from" 1534 " the 'call' trace event of a new frame")) 1535 def test_no_jump_from_call(output): 1536 output.append(1) 1537 def nested(): 1538 output.append(3) 1539 nested() 1540 output.append(5) 1541 1542 @jump_test(2, 1, [1], event='return', error=(ValueError, 1543 "can only jump from a 'line' trace event")) 1544 def test_no_jump_from_return_event(output): 1545 output.append(1) 1546 return 1547 1548 @jump_test(2, 1, [1], event='exception', error=(ValueError, 1549 "can only jump from a 'line' trace event")) 1550 def test_no_jump_from_exception_event(output): 1551 output.append(1) 1552 1 / 0 1553 1554 @jump_test(3, 2, [2, 5], event='return') 1555 def test_jump_from_yield(output): 1556 def gen(): 1557 output.append(2) 1558 yield 3 1559 next(gen()) 1560 output.append(5) 1561 1562 1563if __name__ == "__main__": 1564 unittest.main() 1565