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 as exc: 165 x = 1 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 as exc: 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 485class SkipLineEventsTraceTestCase(TraceTestCase): 486 """Repeat the trace tests, but with per-line events skipped""" 487 488 def compare_events(self, line_offset, events, expected_events): 489 skip_line_events = [e for e in expected_events if e[1] != 'line'] 490 super().compare_events(line_offset, events, skip_line_events) 491 492 @staticmethod 493 def make_tracer(): 494 return Tracer(trace_line_events=False) 495 496 497@support.cpython_only 498class TraceOpcodesTestCase(TraceTestCase): 499 """Repeat the trace tests, but with per-opcodes events enabled""" 500 501 def compare_events(self, line_offset, events, expected_events): 502 skip_opcode_events = [e for e in events if e[1] != 'opcode'] 503 if len(events) > 1: 504 self.assertLess(len(skip_opcode_events), len(events), 505 msg="No 'opcode' events received by the tracer") 506 super().compare_events(line_offset, skip_opcode_events, expected_events) 507 508 @staticmethod 509 def make_tracer(): 510 return Tracer(trace_opcode_events=True) 511 512 513class RaisingTraceFuncTestCase(unittest.TestCase): 514 def setUp(self): 515 self.addCleanup(sys.settrace, sys.gettrace()) 516 517 def trace(self, frame, event, arg): 518 """A trace function that raises an exception in response to a 519 specific trace event.""" 520 if event == self.raiseOnEvent: 521 raise ValueError # just something that isn't RuntimeError 522 else: 523 return self.trace 524 525 def f(self): 526 """The function to trace; raises an exception if that's the case 527 we're testing, so that the 'exception' trace event fires.""" 528 if self.raiseOnEvent == 'exception': 529 x = 0 530 y = 1/x 531 else: 532 return 1 533 534 def run_test_for_event(self, event): 535 """Tests that an exception raised in response to the given event is 536 handled OK.""" 537 self.raiseOnEvent = event 538 try: 539 for i in range(sys.getrecursionlimit() + 1): 540 sys.settrace(self.trace) 541 try: 542 self.f() 543 except ValueError: 544 pass 545 else: 546 self.fail("exception not raised!") 547 except RuntimeError: 548 self.fail("recursion counter not reset") 549 550 # Test the handling of exceptions raised by each kind of trace event. 551 def test_call(self): 552 self.run_test_for_event('call') 553 def test_line(self): 554 self.run_test_for_event('line') 555 def test_return(self): 556 self.run_test_for_event('return') 557 def test_exception(self): 558 self.run_test_for_event('exception') 559 560 def test_trash_stack(self): 561 def f(): 562 for i in range(5): 563 print(i) # line tracing will raise an exception at this line 564 565 def g(frame, why, extra): 566 if (why == 'line' and 567 frame.f_lineno == f.__code__.co_firstlineno + 2): 568 raise RuntimeError("i am crashing") 569 return g 570 571 sys.settrace(g) 572 try: 573 f() 574 except RuntimeError: 575 # the test is really that this doesn't segfault: 576 import gc 577 gc.collect() 578 else: 579 self.fail("exception not propagated") 580 581 582 def test_exception_arguments(self): 583 def f(): 584 x = 0 585 # this should raise an error 586 x.no_such_attr 587 def g(frame, event, arg): 588 if (event == 'exception'): 589 type, exception, trace = arg 590 self.assertIsInstance(exception, Exception) 591 return g 592 593 existing = sys.gettrace() 594 try: 595 sys.settrace(g) 596 try: 597 f() 598 except AttributeError: 599 # this is expected 600 pass 601 finally: 602 sys.settrace(existing) 603 604 605# 'Jump' tests: assigning to frame.f_lineno within a trace function 606# moves the execution position - it's how debuggers implement a Jump 607# command (aka. "Set next statement"). 608 609class JumpTracer: 610 """Defines a trace function that jumps from one place to another.""" 611 612 def __init__(self, function, jumpFrom, jumpTo, event='line', 613 decorated=False): 614 self.code = function.__code__ 615 self.jumpFrom = jumpFrom 616 self.jumpTo = jumpTo 617 self.event = event 618 self.firstLine = None if decorated else self.code.co_firstlineno 619 self.done = False 620 621 def trace(self, frame, event, arg): 622 if self.done: 623 return 624 # frame.f_code.co_firstlineno is the first line of the decorator when 625 # 'function' is decorated and the decorator may be written using 626 # multiple physical lines when it is too long. Use the first line 627 # trace event in 'function' to find the first line of 'function'. 628 if (self.firstLine is None and frame.f_code == self.code and 629 event == 'line'): 630 self.firstLine = frame.f_lineno - 1 631 if (event == self.event and self.firstLine and 632 frame.f_lineno == self.firstLine + self.jumpFrom): 633 f = frame 634 while f is not None and f.f_code != self.code: 635 f = f.f_back 636 if f is not None: 637 # Cope with non-integer self.jumpTo (because of 638 # no_jump_to_non_integers below). 639 try: 640 frame.f_lineno = self.firstLine + self.jumpTo 641 except TypeError: 642 frame.f_lineno = self.jumpTo 643 self.done = True 644 return self.trace 645 646# This verifies the line-numbers-must-be-integers rule. 647def no_jump_to_non_integers(output): 648 try: 649 output.append(2) 650 except ValueError as e: 651 output.append('integer' in str(e)) 652 653# This verifies that you can't set f_lineno via _getframe or similar 654# trickery. 655def no_jump_without_trace_function(): 656 try: 657 previous_frame = sys._getframe().f_back 658 previous_frame.f_lineno = previous_frame.f_lineno 659 except ValueError as e: 660 # This is the exception we wanted; make sure the error message 661 # talks about trace functions. 662 if 'trace' not in str(e): 663 raise 664 else: 665 # Something's wrong - the expected exception wasn't raised. 666 raise AssertionError("Trace-function-less jump failed to fail") 667 668 669class JumpTestCase(unittest.TestCase): 670 def setUp(self): 671 self.addCleanup(sys.settrace, sys.gettrace()) 672 sys.settrace(None) 673 674 def compare_jump_output(self, expected, received): 675 if received != expected: 676 self.fail( "Outputs don't match:\n" + 677 "Expected: " + repr(expected) + "\n" + 678 "Received: " + repr(received)) 679 680 def run_test(self, func, jumpFrom, jumpTo, expected, error=None, 681 event='line', decorated=False): 682 tracer = JumpTracer(func, jumpFrom, jumpTo, event, decorated) 683 sys.settrace(tracer.trace) 684 output = [] 685 if error is None: 686 func(output) 687 else: 688 with self.assertRaisesRegex(*error): 689 func(output) 690 sys.settrace(None) 691 self.compare_jump_output(expected, output) 692 693 def run_async_test(self, func, jumpFrom, jumpTo, expected, error=None, 694 event='line', decorated=False): 695 tracer = JumpTracer(func, jumpFrom, jumpTo, event, decorated) 696 sys.settrace(tracer.trace) 697 output = [] 698 if error is None: 699 asyncio.run(func(output)) 700 else: 701 with self.assertRaisesRegex(*error): 702 asyncio.run(func(output)) 703 sys.settrace(None) 704 asyncio.set_event_loop_policy(None) 705 self.compare_jump_output(expected, output) 706 707 def jump_test(jumpFrom, jumpTo, expected, error=None, event='line'): 708 """Decorator that creates a test that makes a jump 709 from one place to another in the following code. 710 """ 711 def decorator(func): 712 @wraps(func) 713 def test(self): 714 self.run_test(func, jumpFrom, jumpTo, expected, 715 error=error, event=event, decorated=True) 716 return test 717 return decorator 718 719 def async_jump_test(jumpFrom, jumpTo, expected, error=None, event='line'): 720 """Decorator that creates a test that makes a jump 721 from one place to another in the following asynchronous code. 722 """ 723 def decorator(func): 724 @wraps(func) 725 def test(self): 726 self.run_async_test(func, jumpFrom, jumpTo, expected, 727 error=error, event=event, decorated=True) 728 return test 729 return decorator 730 731 ## The first set of 'jump' tests are for things that are allowed: 732 733 @jump_test(1, 3, [3]) 734 def test_jump_simple_forwards(output): 735 output.append(1) 736 output.append(2) 737 output.append(3) 738 739 @jump_test(2, 1, [1, 1, 2]) 740 def test_jump_simple_backwards(output): 741 output.append(1) 742 output.append(2) 743 744 @jump_test(3, 5, [2, 5]) 745 def test_jump_out_of_block_forwards(output): 746 for i in 1, 2: 747 output.append(2) 748 for j in [3]: # Also tests jumping over a block 749 output.append(4) 750 output.append(5) 751 752 @jump_test(6, 1, [1, 3, 5, 1, 3, 5, 6, 7]) 753 def test_jump_out_of_block_backwards(output): 754 output.append(1) 755 for i in [1]: 756 output.append(3) 757 for j in [2]: # Also tests jumping over a block 758 output.append(5) 759 output.append(6) 760 output.append(7) 761 762 @async_jump_test(4, 5, [3, 5]) 763 async def test_jump_out_of_async_for_block_forwards(output): 764 for i in [1]: 765 async for i in asynciter([1, 2]): 766 output.append(3) 767 output.append(4) 768 output.append(5) 769 770 @async_jump_test(5, 2, [2, 4, 2, 4, 5, 6]) 771 async def test_jump_out_of_async_for_block_backwards(output): 772 for i in [1]: 773 output.append(2) 774 async for i in asynciter([1]): 775 output.append(4) 776 output.append(5) 777 output.append(6) 778 779 @jump_test(1, 2, [3]) 780 def test_jump_to_codeless_line(output): 781 output.append(1) 782 # Jumping to this line should skip to the next one. 783 output.append(3) 784 785 @jump_test(2, 2, [1, 2, 3]) 786 def test_jump_to_same_line(output): 787 output.append(1) 788 output.append(2) 789 output.append(3) 790 791 # Tests jumping within a finally block, and over one. 792 @jump_test(4, 9, [2, 9]) 793 def test_jump_in_nested_finally(output): 794 try: 795 output.append(2) 796 finally: 797 output.append(4) 798 try: 799 output.append(6) 800 finally: 801 output.append(8) 802 output.append(9) 803 804 @jump_test(6, 7, [2, 7], (ZeroDivisionError, '')) 805 def test_jump_in_nested_finally_2(output): 806 try: 807 output.append(2) 808 1/0 809 return 810 finally: 811 output.append(6) 812 output.append(7) 813 output.append(8) 814 815 @jump_test(6, 11, [2, 11], (ZeroDivisionError, '')) 816 def test_jump_in_nested_finally_3(output): 817 try: 818 output.append(2) 819 1/0 820 return 821 finally: 822 output.append(6) 823 try: 824 output.append(8) 825 finally: 826 output.append(10) 827 output.append(11) 828 output.append(12) 829 830 @jump_test(5, 11, [2, 4, 12]) 831 def test_jump_over_return_try_finally_in_finally_block(output): 832 try: 833 output.append(2) 834 finally: 835 output.append(4) 836 output.append(5) 837 return 838 try: 839 output.append(8) 840 finally: 841 output.append(10) 842 pass 843 output.append(12) 844 845 @jump_test(3, 4, [1, 4]) 846 def test_jump_infinite_while_loop(output): 847 output.append(1) 848 while True: 849 output.append(3) 850 output.append(4) 851 852 @jump_test(2, 4, [4, 4]) 853 def test_jump_forwards_into_while_block(output): 854 i = 1 855 output.append(2) 856 while i <= 2: 857 output.append(4) 858 i += 1 859 860 @jump_test(5, 3, [3, 3, 3, 5]) 861 def test_jump_backwards_into_while_block(output): 862 i = 1 863 while i <= 2: 864 output.append(3) 865 i += 1 866 output.append(5) 867 868 @jump_test(2, 3, [1, 3]) 869 def test_jump_forwards_out_of_with_block(output): 870 with tracecontext(output, 1): 871 output.append(2) 872 output.append(3) 873 874 @async_jump_test(2, 3, [1, 3]) 875 async def test_jump_forwards_out_of_async_with_block(output): 876 async with asynctracecontext(output, 1): 877 output.append(2) 878 output.append(3) 879 880 @jump_test(3, 1, [1, 2, 1, 2, 3, -2]) 881 def test_jump_backwards_out_of_with_block(output): 882 output.append(1) 883 with tracecontext(output, 2): 884 output.append(3) 885 886 @async_jump_test(3, 1, [1, 2, 1, 2, 3, -2]) 887 async def test_jump_backwards_out_of_async_with_block(output): 888 output.append(1) 889 async with asynctracecontext(output, 2): 890 output.append(3) 891 892 @jump_test(2, 5, [5]) 893 def test_jump_forwards_out_of_try_finally_block(output): 894 try: 895 output.append(2) 896 finally: 897 output.append(4) 898 output.append(5) 899 900 @jump_test(3, 1, [1, 1, 3, 5]) 901 def test_jump_backwards_out_of_try_finally_block(output): 902 output.append(1) 903 try: 904 output.append(3) 905 finally: 906 output.append(5) 907 908 @jump_test(2, 6, [6]) 909 def test_jump_forwards_out_of_try_except_block(output): 910 try: 911 output.append(2) 912 except: 913 output.append(4) 914 raise 915 output.append(6) 916 917 @jump_test(3, 1, [1, 1, 3]) 918 def test_jump_backwards_out_of_try_except_block(output): 919 output.append(1) 920 try: 921 output.append(3) 922 except: 923 output.append(5) 924 raise 925 926 @jump_test(5, 7, [4, 7, 8]) 927 def test_jump_between_except_blocks(output): 928 try: 929 1/0 930 except ZeroDivisionError: 931 output.append(4) 932 output.append(5) 933 except FloatingPointError: 934 output.append(7) 935 output.append(8) 936 937 @jump_test(5, 6, [4, 6, 7]) 938 def test_jump_within_except_block(output): 939 try: 940 1/0 941 except: 942 output.append(4) 943 output.append(5) 944 output.append(6) 945 output.append(7) 946 947 @jump_test(2, 4, [1, 4, 5, -4]) 948 def test_jump_across_with(output): 949 output.append(1) 950 with tracecontext(output, 2): 951 output.append(3) 952 with tracecontext(output, 4): 953 output.append(5) 954 955 @async_jump_test(2, 4, [1, 4, 5, -4]) 956 async def test_jump_across_async_with(output): 957 output.append(1) 958 async with asynctracecontext(output, 2): 959 output.append(3) 960 async with asynctracecontext(output, 4): 961 output.append(5) 962 963 @jump_test(4, 5, [1, 3, 5, 6]) 964 def test_jump_out_of_with_block_within_for_block(output): 965 output.append(1) 966 for i in [1]: 967 with tracecontext(output, 3): 968 output.append(4) 969 output.append(5) 970 output.append(6) 971 972 @async_jump_test(4, 5, [1, 3, 5, 6]) 973 async def test_jump_out_of_async_with_block_within_for_block(output): 974 output.append(1) 975 for i in [1]: 976 async with asynctracecontext(output, 3): 977 output.append(4) 978 output.append(5) 979 output.append(6) 980 981 @jump_test(4, 5, [1, 2, 3, 5, -2, 6]) 982 def test_jump_out_of_with_block_within_with_block(output): 983 output.append(1) 984 with tracecontext(output, 2): 985 with tracecontext(output, 3): 986 output.append(4) 987 output.append(5) 988 output.append(6) 989 990 @async_jump_test(4, 5, [1, 2, 3, 5, -2, 6]) 991 async def test_jump_out_of_async_with_block_within_with_block(output): 992 output.append(1) 993 with tracecontext(output, 2): 994 async with asynctracecontext(output, 3): 995 output.append(4) 996 output.append(5) 997 output.append(6) 998 999 @jump_test(5, 6, [2, 4, 6, 7]) 1000 def test_jump_out_of_with_block_within_finally_block(output): 1001 try: 1002 output.append(2) 1003 finally: 1004 with tracecontext(output, 4): 1005 output.append(5) 1006 output.append(6) 1007 output.append(7) 1008 1009 @async_jump_test(5, 6, [2, 4, 6, 7]) 1010 async def test_jump_out_of_async_with_block_within_finally_block(output): 1011 try: 1012 output.append(2) 1013 finally: 1014 async with asynctracecontext(output, 4): 1015 output.append(5) 1016 output.append(6) 1017 output.append(7) 1018 1019 @jump_test(8, 11, [1, 3, 5, 11, 12]) 1020 def test_jump_out_of_complex_nested_blocks(output): 1021 output.append(1) 1022 for i in [1]: 1023 output.append(3) 1024 for j in [1, 2]: 1025 output.append(5) 1026 try: 1027 for k in [1, 2]: 1028 output.append(8) 1029 finally: 1030 output.append(10) 1031 output.append(11) 1032 output.append(12) 1033 1034 @jump_test(3, 5, [1, 2, 5]) 1035 def test_jump_out_of_with_assignment(output): 1036 output.append(1) 1037 with tracecontext(output, 2) \ 1038 as x: 1039 output.append(4) 1040 output.append(5) 1041 1042 @async_jump_test(3, 5, [1, 2, 5]) 1043 async def test_jump_out_of_async_with_assignment(output): 1044 output.append(1) 1045 async with asynctracecontext(output, 2) \ 1046 as x: 1047 output.append(4) 1048 output.append(5) 1049 1050 @jump_test(3, 6, [1, 6, 8, 9]) 1051 def test_jump_over_return_in_try_finally_block(output): 1052 output.append(1) 1053 try: 1054 output.append(3) 1055 if not output: # always false 1056 return 1057 output.append(6) 1058 finally: 1059 output.append(8) 1060 output.append(9) 1061 1062 @jump_test(5, 8, [1, 3, 8, 10, 11, 13]) 1063 def test_jump_over_break_in_try_finally_block(output): 1064 output.append(1) 1065 while True: 1066 output.append(3) 1067 try: 1068 output.append(5) 1069 if not output: # always false 1070 break 1071 output.append(8) 1072 finally: 1073 output.append(10) 1074 output.append(11) 1075 break 1076 output.append(13) 1077 1078 @jump_test(1, 7, [7, 8]) 1079 def test_jump_over_for_block_before_else(output): 1080 output.append(1) 1081 if not output: # always false 1082 for i in [3]: 1083 output.append(4) 1084 else: 1085 output.append(6) 1086 output.append(7) 1087 output.append(8) 1088 1089 @async_jump_test(1, 7, [7, 8]) 1090 async def test_jump_over_async_for_block_before_else(output): 1091 output.append(1) 1092 if not output: # always false 1093 async for i in asynciter([3]): 1094 output.append(4) 1095 else: 1096 output.append(6) 1097 output.append(7) 1098 output.append(8) 1099 1100 # The second set of 'jump' tests are for things that are not allowed: 1101 1102 @jump_test(2, 3, [1], (ValueError, 'after')) 1103 def test_no_jump_too_far_forwards(output): 1104 output.append(1) 1105 output.append(2) 1106 1107 @jump_test(2, -2, [1], (ValueError, 'before')) 1108 def test_no_jump_too_far_backwards(output): 1109 output.append(1) 1110 output.append(2) 1111 1112 # Test each kind of 'except' line. 1113 @jump_test(2, 3, [4], (ValueError, 'except')) 1114 def test_no_jump_to_except_1(output): 1115 try: 1116 output.append(2) 1117 except: 1118 output.append(4) 1119 raise 1120 1121 @jump_test(2, 3, [4], (ValueError, 'except')) 1122 def test_no_jump_to_except_2(output): 1123 try: 1124 output.append(2) 1125 except ValueError: 1126 output.append(4) 1127 raise 1128 1129 @jump_test(2, 3, [4], (ValueError, 'except')) 1130 def test_no_jump_to_except_3(output): 1131 try: 1132 output.append(2) 1133 except ValueError as e: 1134 output.append(4) 1135 raise e 1136 1137 @jump_test(2, 3, [4], (ValueError, 'except')) 1138 def test_no_jump_to_except_4(output): 1139 try: 1140 output.append(2) 1141 except (ValueError, RuntimeError) as e: 1142 output.append(4) 1143 raise e 1144 1145 @jump_test(1, 3, [], (ValueError, 'into')) 1146 def test_no_jump_forwards_into_for_block(output): 1147 output.append(1) 1148 for i in 1, 2: 1149 output.append(3) 1150 1151 @async_jump_test(1, 3, [], (ValueError, 'into')) 1152 async def test_no_jump_forwards_into_async_for_block(output): 1153 output.append(1) 1154 async for i in asynciter([1, 2]): 1155 output.append(3) 1156 1157 @jump_test(3, 2, [2, 2], (ValueError, 'into')) 1158 def test_no_jump_backwards_into_for_block(output): 1159 for i in 1, 2: 1160 output.append(2) 1161 output.append(3) 1162 1163 @async_jump_test(3, 2, [2, 2], (ValueError, 'into')) 1164 async def test_no_jump_backwards_into_async_for_block(output): 1165 async for i in asynciter([1, 2]): 1166 output.append(2) 1167 output.append(3) 1168 1169 @jump_test(1, 3, [], (ValueError, 'into')) 1170 def test_no_jump_forwards_into_with_block(output): 1171 output.append(1) 1172 with tracecontext(output, 2): 1173 output.append(3) 1174 1175 @async_jump_test(1, 3, [], (ValueError, 'into')) 1176 async def test_no_jump_forwards_into_async_with_block(output): 1177 output.append(1) 1178 async with asynctracecontext(output, 2): 1179 output.append(3) 1180 1181 @jump_test(3, 2, [1, 2, -1], (ValueError, 'into')) 1182 def test_no_jump_backwards_into_with_block(output): 1183 with tracecontext(output, 1): 1184 output.append(2) 1185 output.append(3) 1186 1187 @async_jump_test(3, 2, [1, 2, -1], (ValueError, 'into')) 1188 async def test_no_jump_backwards_into_async_with_block(output): 1189 async with asynctracecontext(output, 1): 1190 output.append(2) 1191 output.append(3) 1192 1193 @jump_test(1, 3, [], (ValueError, 'into')) 1194 def test_no_jump_forwards_into_try_finally_block(output): 1195 output.append(1) 1196 try: 1197 output.append(3) 1198 finally: 1199 output.append(5) 1200 1201 @jump_test(5, 2, [2, 4], (ValueError, 'into')) 1202 def test_no_jump_backwards_into_try_finally_block(output): 1203 try: 1204 output.append(2) 1205 finally: 1206 output.append(4) 1207 output.append(5) 1208 1209 @jump_test(1, 3, [], (ValueError, 'into')) 1210 def test_no_jump_forwards_into_try_except_block(output): 1211 output.append(1) 1212 try: 1213 output.append(3) 1214 except: 1215 output.append(5) 1216 raise 1217 1218 @jump_test(6, 2, [2], (ValueError, 'into')) 1219 def test_no_jump_backwards_into_try_except_block(output): 1220 try: 1221 output.append(2) 1222 except: 1223 output.append(4) 1224 raise 1225 output.append(6) 1226 1227 # 'except' with a variable creates an implicit finally block 1228 @jump_test(5, 7, [4], (ValueError, 'into')) 1229 def test_no_jump_between_except_blocks_2(output): 1230 try: 1231 1/0 1232 except ZeroDivisionError: 1233 output.append(4) 1234 output.append(5) 1235 except FloatingPointError as e: 1236 output.append(7) 1237 output.append(8) 1238 1239 @jump_test(1, 5, [], (ValueError, "into a 'finally'")) 1240 def test_no_jump_into_finally_block(output): 1241 output.append(1) 1242 try: 1243 output.append(3) 1244 finally: 1245 output.append(5) 1246 1247 @jump_test(3, 6, [2, 5, 6], (ValueError, "into a 'finally'")) 1248 def test_no_jump_into_finally_block_from_try_block(output): 1249 try: 1250 output.append(2) 1251 output.append(3) 1252 finally: # still executed if the jump is failed 1253 output.append(5) 1254 output.append(6) 1255 output.append(7) 1256 1257 @jump_test(5, 1, [1, 3], (ValueError, "out of a 'finally'")) 1258 def test_no_jump_out_of_finally_block(output): 1259 output.append(1) 1260 try: 1261 output.append(3) 1262 finally: 1263 output.append(5) 1264 1265 @jump_test(1, 5, [], (ValueError, "into an 'except'")) 1266 def test_no_jump_into_bare_except_block(output): 1267 output.append(1) 1268 try: 1269 output.append(3) 1270 except: 1271 output.append(5) 1272 1273 @jump_test(1, 5, [], (ValueError, "into an 'except'")) 1274 def test_no_jump_into_qualified_except_block(output): 1275 output.append(1) 1276 try: 1277 output.append(3) 1278 except Exception: 1279 output.append(5) 1280 1281 @jump_test(3, 6, [2, 5, 6], (ValueError, "into an 'except'")) 1282 def test_no_jump_into_bare_except_block_from_try_block(output): 1283 try: 1284 output.append(2) 1285 output.append(3) 1286 except: # executed if the jump is failed 1287 output.append(5) 1288 output.append(6) 1289 raise 1290 output.append(8) 1291 1292 @jump_test(3, 6, [2], (ValueError, "into an 'except'")) 1293 def test_no_jump_into_qualified_except_block_from_try_block(output): 1294 try: 1295 output.append(2) 1296 output.append(3) 1297 except ZeroDivisionError: 1298 output.append(5) 1299 output.append(6) 1300 raise 1301 output.append(8) 1302 1303 @jump_test(7, 1, [1, 3, 6], (ValueError, "out of an 'except'")) 1304 def test_no_jump_out_of_bare_except_block(output): 1305 output.append(1) 1306 try: 1307 output.append(3) 1308 1/0 1309 except: 1310 output.append(6) 1311 output.append(7) 1312 1313 @jump_test(7, 1, [1, 3, 6], (ValueError, "out of an 'except'")) 1314 def test_no_jump_out_of_qualified_except_block(output): 1315 output.append(1) 1316 try: 1317 output.append(3) 1318 1/0 1319 except Exception: 1320 output.append(6) 1321 output.append(7) 1322 1323 @jump_test(3, 5, [1, 2, -2], (ValueError, 'into')) 1324 def test_no_jump_between_with_blocks(output): 1325 output.append(1) 1326 with tracecontext(output, 2): 1327 output.append(3) 1328 with tracecontext(output, 4): 1329 output.append(5) 1330 1331 @async_jump_test(3, 5, [1, 2, -2], (ValueError, 'into')) 1332 async def test_no_jump_between_async_with_blocks(output): 1333 output.append(1) 1334 async with asynctracecontext(output, 2): 1335 output.append(3) 1336 async with asynctracecontext(output, 4): 1337 output.append(5) 1338 1339 @jump_test(5, 7, [2, 4], (ValueError, 'finally')) 1340 def test_no_jump_over_return_out_of_finally_block(output): 1341 try: 1342 output.append(2) 1343 finally: 1344 output.append(4) 1345 output.append(5) 1346 return 1347 output.append(7) 1348 1349 @jump_test(7, 4, [1, 6], (ValueError, 'into')) 1350 def test_no_jump_into_for_block_before_else(output): 1351 output.append(1) 1352 if not output: # always false 1353 for i in [3]: 1354 output.append(4) 1355 else: 1356 output.append(6) 1357 output.append(7) 1358 output.append(8) 1359 1360 @async_jump_test(7, 4, [1, 6], (ValueError, 'into')) 1361 async def test_no_jump_into_async_for_block_before_else(output): 1362 output.append(1) 1363 if not output: # always false 1364 async for i in asynciter([3]): 1365 output.append(4) 1366 else: 1367 output.append(6) 1368 output.append(7) 1369 output.append(8) 1370 1371 def test_no_jump_to_non_integers(self): 1372 self.run_test(no_jump_to_non_integers, 2, "Spam", [True]) 1373 1374 def test_no_jump_without_trace_function(self): 1375 # Must set sys.settrace(None) in setUp(), else condition is not 1376 # triggered. 1377 no_jump_without_trace_function() 1378 1379 def test_large_function(self): 1380 d = {} 1381 exec("""def f(output): # line 0 1382 x = 0 # line 1 1383 y = 1 # line 2 1384 ''' # line 3 1385 %s # lines 4-1004 1386 ''' # line 1005 1387 x += 1 # line 1006 1388 output.append(x) # line 1007 1389 return""" % ('\n' * 1000,), d) 1390 f = d['f'] 1391 self.run_test(f, 2, 1007, [0]) 1392 1393 def test_jump_to_firstlineno(self): 1394 # This tests that PDB can jump back to the first line in a 1395 # file. See issue #1689458. It can only be triggered in a 1396 # function call if the function is defined on a single line. 1397 code = compile(""" 1398# Comments don't count. 1399output.append(2) # firstlineno is here. 1400output.append(3) 1401output.append(4) 1402""", "<fake module>", "exec") 1403 class fake_function: 1404 __code__ = code 1405 tracer = JumpTracer(fake_function, 2, 0) 1406 sys.settrace(tracer.trace) 1407 namespace = {"output": []} 1408 exec(code, namespace) 1409 sys.settrace(None) 1410 self.compare_jump_output([2, 3, 2, 3, 4], namespace["output"]) 1411 1412 @jump_test(2, 3, [1], event='call', error=(ValueError, "can't jump from" 1413 " the 'call' trace event of a new frame")) 1414 def test_no_jump_from_call(output): 1415 output.append(1) 1416 def nested(): 1417 output.append(3) 1418 nested() 1419 output.append(5) 1420 1421 @jump_test(2, 1, [1], event='return', error=(ValueError, 1422 "can only jump from a 'line' trace event")) 1423 def test_no_jump_from_return_event(output): 1424 output.append(1) 1425 return 1426 1427 @jump_test(2, 1, [1], event='exception', error=(ValueError, 1428 "can only jump from a 'line' trace event")) 1429 def test_no_jump_from_exception_event(output): 1430 output.append(1) 1431 1 / 0 1432 1433 @jump_test(3, 2, [2], event='return', error=(ValueError, 1434 "can't jump from a yield statement")) 1435 def test_no_jump_from_yield(output): 1436 def gen(): 1437 output.append(2) 1438 yield 3 1439 next(gen()) 1440 output.append(5) 1441 1442 1443if __name__ == "__main__": 1444 unittest.main() 1445