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