1# Testing the line trace facility. 2 3from test import test_support 4import unittest 5import sys 6import difflib 7import gc 8 9# A very basic example. If this fails, we're in deep trouble. 10def basic(): 11 return 1 12 13basic.events = [(0, 'call'), 14 (1, 'line'), 15 (1, 'return')] 16 17# Many of the tests below are tricky because they involve pass statements. 18# If there is implicit control flow around a pass statement (in an except 19# clause or else caluse) under what conditions do you set a line number 20# following that clause? 21 22 23# The entire "while 0:" statement is optimized away. No code 24# exists for it, so the line numbers skip directly from "del x" 25# to "x = 1". 26def arigo_example(): 27 x = 1 28 del x 29 while 0: 30 pass 31 x = 1 32 33arigo_example.events = [(0, 'call'), 34 (1, 'line'), 35 (2, 'line'), 36 (5, 'line'), 37 (5, 'return')] 38 39# check that lines consisting of just one instruction get traced: 40def one_instr_line(): 41 x = 1 42 del x 43 x = 1 44 45one_instr_line.events = [(0, 'call'), 46 (1, 'line'), 47 (2, 'line'), 48 (3, 'line'), 49 (3, 'return')] 50 51def no_pop_tops(): # 0 52 x = 1 # 1 53 for a in range(2): # 2 54 if a: # 3 55 x = 1 # 4 56 else: # 5 57 x = 1 # 6 58 59no_pop_tops.events = [(0, 'call'), 60 (1, 'line'), 61 (2, 'line'), 62 (3, 'line'), 63 (6, 'line'), 64 (2, 'line'), 65 (3, 'line'), 66 (4, 'line'), 67 (2, 'line'), 68 (2, 'return')] 69 70def no_pop_blocks(): 71 y = 1 72 while not y: 73 bla 74 x = 1 75 76no_pop_blocks.events = [(0, 'call'), 77 (1, 'line'), 78 (2, 'line'), 79 (4, 'line'), 80 (4, 'return')] 81 82def called(): # line -3 83 x = 1 84 85def call(): # line 0 86 called() 87 88call.events = [(0, 'call'), 89 (1, 'line'), 90 (-3, 'call'), 91 (-2, 'line'), 92 (-2, 'return'), 93 (1, 'return')] 94 95def raises(): 96 raise Exception 97 98def test_raise(): 99 try: 100 raises() 101 except Exception, exc: 102 x = 1 103 104test_raise.events = [(0, 'call'), 105 (1, 'line'), 106 (2, 'line'), 107 (-3, 'call'), 108 (-2, 'line'), 109 (-2, 'exception'), 110 (-2, 'return'), 111 (2, 'exception'), 112 (3, 'line'), 113 (4, 'line'), 114 (4, 'return')] 115 116def _settrace_and_return(tracefunc): 117 sys.settrace(tracefunc) 118 sys._getframe().f_back.f_trace = tracefunc 119def settrace_and_return(tracefunc): 120 _settrace_and_return(tracefunc) 121 122settrace_and_return.events = [(1, 'return')] 123 124def _settrace_and_raise(tracefunc): 125 sys.settrace(tracefunc) 126 sys._getframe().f_back.f_trace = tracefunc 127 raise RuntimeError 128def settrace_and_raise(tracefunc): 129 try: 130 _settrace_and_raise(tracefunc) 131 except RuntimeError, exc: 132 pass 133 134settrace_and_raise.events = [(2, 'exception'), 135 (3, 'line'), 136 (4, 'line'), 137 (4, 'return')] 138 139# implicit return example 140# This test is interesting because of the else: pass 141# part of the code. The code generate for the true 142# part of the if contains a jump past the else branch. 143# The compiler then generates an implicit "return None" 144# Internally, the compiler visits the pass statement 145# and stores its line number for use on the next instruction. 146# The next instruction is the implicit return None. 147def ireturn_example(): 148 a = 5 149 b = 5 150 if a == b: 151 b = a+1 152 else: 153 pass 154 155ireturn_example.events = [(0, 'call'), 156 (1, 'line'), 157 (2, 'line'), 158 (3, 'line'), 159 (4, 'line'), 160 (6, 'line'), 161 (6, 'return')] 162 163# Tight loop with while(1) example (SF #765624) 164def tightloop_example(): 165 items = range(0, 3) 166 try: 167 i = 0 168 while 1: 169 b = items[i]; i+=1 170 except IndexError: 171 pass 172 173tightloop_example.events = [(0, 'call'), 174 (1, 'line'), 175 (2, 'line'), 176 (3, 'line'), 177 (4, 'line'), 178 (5, 'line'), 179 (5, 'line'), 180 (5, 'line'), 181 (5, 'line'), 182 (5, 'exception'), 183 (6, 'line'), 184 (7, 'line'), 185 (7, 'return')] 186 187def tighterloop_example(): 188 items = range(1, 4) 189 try: 190 i = 0 191 while 1: i = items[i] 192 except IndexError: 193 pass 194 195tighterloop_example.events = [(0, 'call'), 196 (1, 'line'), 197 (2, 'line'), 198 (3, 'line'), 199 (4, 'line'), 200 (4, 'line'), 201 (4, 'line'), 202 (4, 'line'), 203 (4, 'exception'), 204 (5, 'line'), 205 (6, 'line'), 206 (6, 'return')] 207 208def generator_function(): 209 try: 210 yield True 211 "continued" 212 finally: 213 "finally" 214def generator_example(): 215 # any() will leave the generator before its end 216 x = any(generator_function()) 217 218 # the following lines were not traced 219 for x in range(10): 220 y = x 221 222generator_example.events = ([(0, 'call'), 223 (2, 'line'), 224 (-6, 'call'), 225 (-5, 'line'), 226 (-4, 'line'), 227 (-4, 'return'), 228 (-4, 'call'), 229 (-4, 'exception'), 230 (-1, 'line'), 231 (-1, 'return')] + 232 [(5, 'line'), (6, 'line')] * 10 + 233 [(5, 'line'), (5, 'return')]) 234 235 236class Tracer: 237 def __init__(self): 238 self.events = [] 239 def trace(self, frame, event, arg): 240 self.events.append((frame.f_lineno, event)) 241 return self.trace 242 def traceWithGenexp(self, frame, event, arg): 243 (o for o in [1]) 244 self.events.append((frame.f_lineno, event)) 245 return self.trace 246 247class TraceTestCase(unittest.TestCase): 248 249 # Disable gc collection when tracing, otherwise the 250 # deallocators may be traced as well. 251 def setUp(self): 252 self.using_gc = gc.isenabled() 253 gc.disable() 254 255 def tearDown(self): 256 if self.using_gc: 257 gc.enable() 258 259 def compare_events(self, line_offset, events, expected_events): 260 events = [(l - line_offset, e) for (l, e) in events] 261 if events != expected_events: 262 self.fail( 263 "events did not match expectation:\n" + 264 "\n".join(difflib.ndiff([str(x) for x in expected_events], 265 [str(x) for x in events]))) 266 267 def run_and_compare(self, func, events): 268 tracer = Tracer() 269 sys.settrace(tracer.trace) 270 func() 271 sys.settrace(None) 272 self.compare_events(func.func_code.co_firstlineno, 273 tracer.events, events) 274 275 def run_test(self, func): 276 self.run_and_compare(func, func.events) 277 278 def run_test2(self, func): 279 tracer = Tracer() 280 func(tracer.trace) 281 sys.settrace(None) 282 self.compare_events(func.func_code.co_firstlineno, 283 tracer.events, func.events) 284 285 def test_set_and_retrieve_none(self): 286 sys.settrace(None) 287 assert sys.gettrace() is None 288 289 def test_set_and_retrieve_func(self): 290 def fn(*args): 291 pass 292 293 sys.settrace(fn) 294 try: 295 assert sys.gettrace() is fn 296 finally: 297 sys.settrace(None) 298 299 def test_01_basic(self): 300 self.run_test(basic) 301 def test_02_arigo(self): 302 self.run_test(arigo_example) 303 def test_03_one_instr(self): 304 self.run_test(one_instr_line) 305 def test_04_no_pop_blocks(self): 306 self.run_test(no_pop_blocks) 307 def test_05_no_pop_tops(self): 308 self.run_test(no_pop_tops) 309 def test_06_call(self): 310 self.run_test(call) 311 def test_07_raise(self): 312 self.run_test(test_raise) 313 314 def test_08_settrace_and_return(self): 315 self.run_test2(settrace_and_return) 316 def test_09_settrace_and_raise(self): 317 self.run_test2(settrace_and_raise) 318 def test_10_ireturn(self): 319 self.run_test(ireturn_example) 320 def test_11_tightloop(self): 321 self.run_test(tightloop_example) 322 def test_12_tighterloop(self): 323 self.run_test(tighterloop_example) 324 325 def test_13_genexp(self): 326 self.run_test(generator_example) 327 # issue1265: if the trace function contains a generator, 328 # and if the traced function contains another generator 329 # that is not completely exhausted, the trace stopped. 330 # Worse: the 'finally' clause was not invoked. 331 tracer = Tracer() 332 sys.settrace(tracer.traceWithGenexp) 333 generator_example() 334 sys.settrace(None) 335 self.compare_events(generator_example.__code__.co_firstlineno, 336 tracer.events, generator_example.events) 337 338 def test_14_onliner_if(self): 339 def onliners(): 340 if True: False 341 else: True 342 return 0 343 self.run_and_compare( 344 onliners, 345 [(0, 'call'), 346 (1, 'line'), 347 (3, 'line'), 348 (3, 'return')]) 349 350 def test_15_loops(self): 351 # issue1750076: "while" expression is skipped by debugger 352 def for_example(): 353 for x in range(2): 354 pass 355 self.run_and_compare( 356 for_example, 357 [(0, 'call'), 358 (1, 'line'), 359 (2, 'line'), 360 (1, 'line'), 361 (2, 'line'), 362 (1, 'line'), 363 (1, 'return')]) 364 365 def while_example(): 366 # While expression should be traced on every loop 367 x = 2 368 while x > 0: 369 x -= 1 370 self.run_and_compare( 371 while_example, 372 [(0, 'call'), 373 (2, 'line'), 374 (3, 'line'), 375 (4, 'line'), 376 (3, 'line'), 377 (4, 'line'), 378 (3, 'line'), 379 (3, 'return')]) 380 381 def test_16_blank_lines(self): 382 exec("def f():\n" + "\n" * 256 + " pass") 383 self.run_and_compare( 384 f, 385 [(0, 'call'), 386 (257, 'line'), 387 (257, 'return')]) 388 389 def test_17_none_f_trace(self): 390 # Issue 20041: fix TypeError when f_trace is set to None. 391 def func(): 392 sys._getframe().f_trace = None 393 lineno = 2 394 self.run_and_compare(func, 395 [(0, 'call'), 396 (1, 'line')]) 397 398 399class RaisingTraceFuncTestCase(unittest.TestCase): 400 def trace(self, frame, event, arg): 401 """A trace function that raises an exception in response to a 402 specific trace event.""" 403 if event == self.raiseOnEvent: 404 raise ValueError # just something that isn't RuntimeError 405 else: 406 return self.trace 407 408 def f(self): 409 """The function to trace; raises an exception if that's the case 410 we're testing, so that the 'exception' trace event fires.""" 411 if self.raiseOnEvent == 'exception': 412 x = 0 413 y = 1 // x 414 else: 415 return 1 416 417 def run_test_for_event(self, event): 418 """Tests that an exception raised in response to the given event is 419 handled OK.""" 420 self.raiseOnEvent = event 421 try: 422 for i in xrange(sys.getrecursionlimit() + 1): 423 sys.settrace(self.trace) 424 try: 425 self.f() 426 except ValueError: 427 pass 428 else: 429 self.fail("exception not raised!") 430 except RuntimeError: 431 self.fail("recursion counter not reset") 432 433 # Test the handling of exceptions raised by each kind of trace event. 434 def test_call(self): 435 self.run_test_for_event('call') 436 def test_line(self): 437 self.run_test_for_event('line') 438 def test_return(self): 439 self.run_test_for_event('return') 440 def test_exception(self): 441 self.run_test_for_event('exception') 442 443 def test_trash_stack(self): 444 def f(): 445 for i in range(5): 446 print i # line tracing will raise an exception at this line 447 448 def g(frame, why, extra): 449 if (why == 'line' and 450 frame.f_lineno == f.func_code.co_firstlineno + 2): 451 raise RuntimeError, "i am crashing" 452 return g 453 454 sys.settrace(g) 455 try: 456 f() 457 except RuntimeError: 458 # the test is really that this doesn't segfault: 459 import gc 460 gc.collect() 461 else: 462 self.fail("exception not propagated") 463 464 465# 'Jump' tests: assigning to frame.f_lineno within a trace function 466# moves the execution position - it's how debuggers implement a Jump 467# command (aka. "Set next statement"). 468 469class JumpTracer: 470 """Defines a trace function that jumps from one place to another, 471 with the source and destination lines of the jump being defined by 472 the 'jump' property of the function under test.""" 473 474 def __init__(self, function): 475 self.function = function 476 self.jumpFrom = function.jump[0] 477 self.jumpTo = function.jump[1] 478 self.done = False 479 480 def trace(self, frame, event, arg): 481 if not self.done and frame.f_code == self.function.func_code: 482 firstLine = frame.f_code.co_firstlineno 483 if event == 'line' and frame.f_lineno == firstLine + self.jumpFrom: 484 # Cope with non-integer self.jumpTo (because of 485 # no_jump_to_non_integers below). 486 try: 487 frame.f_lineno = firstLine + self.jumpTo 488 except TypeError: 489 frame.f_lineno = self.jumpTo 490 self.done = True 491 return self.trace 492 493# The first set of 'jump' tests are for things that are allowed: 494 495def jump_simple_forwards(output): 496 output.append(1) 497 output.append(2) 498 output.append(3) 499 500jump_simple_forwards.jump = (1, 3) 501jump_simple_forwards.output = [3] 502 503def jump_simple_backwards(output): 504 output.append(1) 505 output.append(2) 506 507jump_simple_backwards.jump = (2, 1) 508jump_simple_backwards.output = [1, 1, 2] 509 510def jump_out_of_block_forwards(output): 511 for i in 1, 2: 512 output.append(2) 513 for j in [3]: # Also tests jumping over a block 514 output.append(4) 515 output.append(5) 516 517jump_out_of_block_forwards.jump = (3, 5) 518jump_out_of_block_forwards.output = [2, 5] 519 520def jump_out_of_block_backwards(output): 521 output.append(1) 522 for i in [1]: 523 output.append(3) 524 for j in [2]: # Also tests jumping over a block 525 output.append(5) 526 output.append(6) 527 output.append(7) 528 529jump_out_of_block_backwards.jump = (6, 1) 530jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7] 531 532def jump_to_codeless_line(output): 533 output.append(1) 534 # Jumping to this line should skip to the next one. 535 output.append(3) 536 537jump_to_codeless_line.jump = (1, 2) 538jump_to_codeless_line.output = [3] 539 540def jump_to_same_line(output): 541 output.append(1) 542 output.append(2) 543 output.append(3) 544 545jump_to_same_line.jump = (2, 2) 546jump_to_same_line.output = [1, 2, 3] 547 548# Tests jumping within a finally block, and over one. 549def jump_in_nested_finally(output): 550 try: 551 output.append(2) 552 finally: 553 output.append(4) 554 try: 555 output.append(6) 556 finally: 557 output.append(8) 558 output.append(9) 559 560jump_in_nested_finally.jump = (4, 9) 561jump_in_nested_finally.output = [2, 9] 562 563def jump_infinite_while_loop(output): 564 output.append(1) 565 while 1: 566 output.append(2) 567 output.append(3) 568 569jump_infinite_while_loop.jump = (3, 4) 570jump_infinite_while_loop.output = [1, 3] 571 572# The second set of 'jump' tests are for things that are not allowed: 573 574def no_jump_too_far_forwards(output): 575 try: 576 output.append(2) 577 output.append(3) 578 except ValueError, e: 579 output.append('after' in str(e)) 580 581no_jump_too_far_forwards.jump = (3, 6) 582no_jump_too_far_forwards.output = [2, True] 583 584def no_jump_too_far_backwards(output): 585 try: 586 output.append(2) 587 output.append(3) 588 except ValueError, e: 589 output.append('before' in str(e)) 590 591no_jump_too_far_backwards.jump = (3, -1) 592no_jump_too_far_backwards.output = [2, True] 593 594# Test each kind of 'except' line. 595def no_jump_to_except_1(output): 596 try: 597 output.append(2) 598 except: 599 e = sys.exc_info()[1] 600 output.append('except' in str(e)) 601 602no_jump_to_except_1.jump = (2, 3) 603no_jump_to_except_1.output = [True] 604 605def no_jump_to_except_2(output): 606 try: 607 output.append(2) 608 except ValueError: 609 e = sys.exc_info()[1] 610 output.append('except' in str(e)) 611 612no_jump_to_except_2.jump = (2, 3) 613no_jump_to_except_2.output = [True] 614 615def no_jump_to_except_3(output): 616 try: 617 output.append(2) 618 except ValueError, e: 619 output.append('except' in str(e)) 620 621no_jump_to_except_3.jump = (2, 3) 622no_jump_to_except_3.output = [True] 623 624def no_jump_to_except_4(output): 625 try: 626 output.append(2) 627 except (ValueError, RuntimeError), e: 628 output.append('except' in str(e)) 629 630no_jump_to_except_4.jump = (2, 3) 631no_jump_to_except_4.output = [True] 632 633def no_jump_forwards_into_block(output): 634 try: 635 output.append(2) 636 for i in 1, 2: 637 output.append(4) 638 except ValueError, e: 639 output.append('into' in str(e)) 640 641no_jump_forwards_into_block.jump = (2, 4) 642no_jump_forwards_into_block.output = [True] 643 644def no_jump_backwards_into_block(output): 645 try: 646 for i in 1, 2: 647 output.append(3) 648 output.append(4) 649 except ValueError, e: 650 output.append('into' in str(e)) 651 652no_jump_backwards_into_block.jump = (4, 3) 653no_jump_backwards_into_block.output = [3, 3, True] 654 655def no_jump_into_finally_block(output): 656 try: 657 try: 658 output.append(3) 659 x = 1 660 finally: 661 output.append(6) 662 except ValueError, e: 663 output.append('finally' in str(e)) 664 665no_jump_into_finally_block.jump = (4, 6) 666no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs 667 668def no_jump_out_of_finally_block(output): 669 try: 670 try: 671 output.append(3) 672 finally: 673 output.append(5) 674 output.append(6) 675 except ValueError, e: 676 output.append('finally' in str(e)) 677 678no_jump_out_of_finally_block.jump = (5, 1) 679no_jump_out_of_finally_block.output = [3, True] 680 681# This verifies the line-numbers-must-be-integers rule. 682def no_jump_to_non_integers(output): 683 try: 684 output.append(2) 685 except ValueError, e: 686 output.append('integer' in str(e)) 687 688no_jump_to_non_integers.jump = (2, "Spam") 689no_jump_to_non_integers.output = [True] 690 691def jump_across_with(output): 692 with open(test_support.TESTFN, "wb") as fp: 693 pass 694 with open(test_support.TESTFN, "wb") as fp: 695 pass 696jump_across_with.jump = (1, 3) 697jump_across_with.output = [] 698 699# This verifies that you can't set f_lineno via _getframe or similar 700# trickery. 701def no_jump_without_trace_function(): 702 try: 703 previous_frame = sys._getframe().f_back 704 previous_frame.f_lineno = previous_frame.f_lineno 705 except ValueError, e: 706 # This is the exception we wanted; make sure the error message 707 # talks about trace functions. 708 if 'trace' not in str(e): 709 raise 710 else: 711 # Something's wrong - the expected exception wasn't raised. 712 raise RuntimeError, "Trace-function-less jump failed to fail" 713 714 715class JumpTestCase(unittest.TestCase): 716 def compare_jump_output(self, expected, received): 717 if received != expected: 718 self.fail( "Outputs don't match:\n" + 719 "Expected: " + repr(expected) + "\n" + 720 "Received: " + repr(received)) 721 722 def run_test(self, func): 723 tracer = JumpTracer(func) 724 sys.settrace(tracer.trace) 725 output = [] 726 func(output) 727 sys.settrace(None) 728 self.compare_jump_output(func.output, output) 729 730 def test_01_jump_simple_forwards(self): 731 self.run_test(jump_simple_forwards) 732 def test_02_jump_simple_backwards(self): 733 self.run_test(jump_simple_backwards) 734 def test_03_jump_out_of_block_forwards(self): 735 self.run_test(jump_out_of_block_forwards) 736 def test_04_jump_out_of_block_backwards(self): 737 self.run_test(jump_out_of_block_backwards) 738 def test_05_jump_to_codeless_line(self): 739 self.run_test(jump_to_codeless_line) 740 def test_06_jump_to_same_line(self): 741 self.run_test(jump_to_same_line) 742 def test_07_jump_in_nested_finally(self): 743 self.run_test(jump_in_nested_finally) 744 def test_jump_infinite_while_loop(self): 745 self.run_test(jump_infinite_while_loop) 746 def test_08_no_jump_too_far_forwards(self): 747 self.run_test(no_jump_too_far_forwards) 748 def test_09_no_jump_too_far_backwards(self): 749 self.run_test(no_jump_too_far_backwards) 750 def test_10_no_jump_to_except_1(self): 751 self.run_test(no_jump_to_except_1) 752 def test_11_no_jump_to_except_2(self): 753 self.run_test(no_jump_to_except_2) 754 def test_12_no_jump_to_except_3(self): 755 self.run_test(no_jump_to_except_3) 756 def test_13_no_jump_to_except_4(self): 757 self.run_test(no_jump_to_except_4) 758 def test_14_no_jump_forwards_into_block(self): 759 self.run_test(no_jump_forwards_into_block) 760 def test_15_no_jump_backwards_into_block(self): 761 self.run_test(no_jump_backwards_into_block) 762 def test_16_no_jump_into_finally_block(self): 763 self.run_test(no_jump_into_finally_block) 764 def test_17_no_jump_out_of_finally_block(self): 765 self.run_test(no_jump_out_of_finally_block) 766 def test_18_no_jump_to_non_integers(self): 767 self.run_test(no_jump_to_non_integers) 768 def test_19_no_jump_without_trace_function(self): 769 no_jump_without_trace_function() 770 def test_jump_across_with(self): 771 self.addCleanup(test_support.unlink, test_support.TESTFN) 772 self.run_test(jump_across_with) 773 774 def test_20_large_function(self): 775 d = {} 776 exec("""def f(output): # line 0 777 x = 0 # line 1 778 y = 1 # line 2 779 ''' # line 3 780 %s # lines 4-1004 781 ''' # line 1005 782 x += 1 # line 1006 783 output.append(x) # line 1007 784 return""" % ('\n' * 1000,), d) 785 f = d['f'] 786 787 f.jump = (2, 1007) 788 f.output = [0] 789 self.run_test(f) 790 791 def test_jump_to_firstlineno(self): 792 # This tests that PDB can jump back to the first line in a 793 # file. See issue #1689458. It can only be triggered in a 794 # function call if the function is defined on a single line. 795 code = compile(""" 796# Comments don't count. 797output.append(2) # firstlineno is here. 798output.append(3) 799output.append(4) 800""", "<fake module>", "exec") 801 class fake_function: 802 func_code = code 803 jump = (2, 0) 804 tracer = JumpTracer(fake_function) 805 sys.settrace(tracer.trace) 806 namespace = {"output": []} 807 exec code in namespace 808 sys.settrace(None) 809 self.compare_jump_output([2, 3, 2, 3, 4], namespace["output"]) 810 811 812def test_main(): 813 test_support.run_unittest( 814 TraceTestCase, 815 RaisingTraceFuncTestCase, 816 JumpTestCase 817 ) 818 819if __name__ == "__main__": 820 test_main() 821