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 set_and_retrieve_none(self):
286        sys.settrace(None)
287        assert sys.gettrace() is None
288
289    def 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
390class RaisingTraceFuncTestCase(unittest.TestCase):
391    def trace(self, frame, event, arg):
392        """A trace function that raises an exception in response to a
393        specific trace event."""
394        if event == self.raiseOnEvent:
395            raise ValueError # just something that isn't RuntimeError
396        else:
397            return self.trace
398
399    def f(self):
400        """The function to trace; raises an exception if that's the case
401        we're testing, so that the 'exception' trace event fires."""
402        if self.raiseOnEvent == 'exception':
403            x = 0
404            y = 1 // x
405        else:
406            return 1
407
408    def run_test_for_event(self, event):
409        """Tests that an exception raised in response to the given event is
410        handled OK."""
411        self.raiseOnEvent = event
412        try:
413            for i in xrange(sys.getrecursionlimit() + 1):
414                sys.settrace(self.trace)
415                try:
416                    self.f()
417                except ValueError:
418                    pass
419                else:
420                    self.fail("exception not thrown!")
421        except RuntimeError:
422            self.fail("recursion counter not reset")
423
424    # Test the handling of exceptions raised by each kind of trace event.
425    def test_call(self):
426        self.run_test_for_event('call')
427    def test_line(self):
428        self.run_test_for_event('line')
429    def test_return(self):
430        self.run_test_for_event('return')
431    def test_exception(self):
432        self.run_test_for_event('exception')
433
434    def test_trash_stack(self):
435        def f():
436            for i in range(5):
437                print i  # line tracing will raise an exception at this line
438
439        def g(frame, why, extra):
440            if (why == 'line' and
441                frame.f_lineno == f.func_code.co_firstlineno + 2):
442                raise RuntimeError, "i am crashing"
443            return g
444
445        sys.settrace(g)
446        try:
447            f()
448        except RuntimeError:
449            # the test is really that this doesn't segfault:
450            import gc
451            gc.collect()
452        else:
453            self.fail("exception not propagated")
454
455
456# 'Jump' tests: assigning to frame.f_lineno within a trace function
457# moves the execution position - it's how debuggers implement a Jump
458# command (aka. "Set next statement").
459
460class JumpTracer:
461    """Defines a trace function that jumps from one place to another,
462    with the source and destination lines of the jump being defined by
463    the 'jump' property of the function under test."""
464
465    def __init__(self, function):
466        self.function = function
467        self.jumpFrom = function.jump[0]
468        self.jumpTo = function.jump[1]
469        self.done = False
470
471    def trace(self, frame, event, arg):
472        if not self.done and frame.f_code == self.function.func_code:
473            firstLine = frame.f_code.co_firstlineno
474            if event == 'line' and frame.f_lineno == firstLine + self.jumpFrom:
475                # Cope with non-integer self.jumpTo (because of
476                # no_jump_to_non_integers below).
477                try:
478                    frame.f_lineno = firstLine + self.jumpTo
479                except TypeError:
480                    frame.f_lineno = self.jumpTo
481                self.done = True
482        return self.trace
483
484# The first set of 'jump' tests are for things that are allowed:
485
486def jump_simple_forwards(output):
487    output.append(1)
488    output.append(2)
489    output.append(3)
490
491jump_simple_forwards.jump = (1, 3)
492jump_simple_forwards.output = [3]
493
494def jump_simple_backwards(output):
495    output.append(1)
496    output.append(2)
497
498jump_simple_backwards.jump = (2, 1)
499jump_simple_backwards.output = [1, 1, 2]
500
501def jump_out_of_block_forwards(output):
502    for i in 1, 2:
503        output.append(2)
504        for j in [3]:  # Also tests jumping over a block
505            output.append(4)
506    output.append(5)
507
508jump_out_of_block_forwards.jump = (3, 5)
509jump_out_of_block_forwards.output = [2, 5]
510
511def jump_out_of_block_backwards(output):
512    output.append(1)
513    for i in [1]:
514        output.append(3)
515        for j in [2]:  # Also tests jumping over a block
516            output.append(5)
517        output.append(6)
518    output.append(7)
519
520jump_out_of_block_backwards.jump = (6, 1)
521jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7]
522
523def jump_to_codeless_line(output):
524    output.append(1)
525    # Jumping to this line should skip to the next one.
526    output.append(3)
527
528jump_to_codeless_line.jump = (1, 2)
529jump_to_codeless_line.output = [3]
530
531def jump_to_same_line(output):
532    output.append(1)
533    output.append(2)
534    output.append(3)
535
536jump_to_same_line.jump = (2, 2)
537jump_to_same_line.output = [1, 2, 3]
538
539# Tests jumping within a finally block, and over one.
540def jump_in_nested_finally(output):
541    try:
542        output.append(2)
543    finally:
544        output.append(4)
545        try:
546            output.append(6)
547        finally:
548            output.append(8)
549        output.append(9)
550
551jump_in_nested_finally.jump = (4, 9)
552jump_in_nested_finally.output = [2, 9]
553
554# The second set of 'jump' tests are for things that are not allowed:
555
556def no_jump_too_far_forwards(output):
557    try:
558        output.append(2)
559        output.append(3)
560    except ValueError, e:
561        output.append('after' in str(e))
562
563no_jump_too_far_forwards.jump = (3, 6)
564no_jump_too_far_forwards.output = [2, True]
565
566def no_jump_too_far_backwards(output):
567    try:
568        output.append(2)
569        output.append(3)
570    except ValueError, e:
571        output.append('before' in str(e))
572
573no_jump_too_far_backwards.jump = (3, -1)
574no_jump_too_far_backwards.output = [2, True]
575
576# Test each kind of 'except' line.
577def no_jump_to_except_1(output):
578    try:
579        output.append(2)
580    except:
581        e = sys.exc_info()[1]
582        output.append('except' in str(e))
583
584no_jump_to_except_1.jump = (2, 3)
585no_jump_to_except_1.output = [True]
586
587def no_jump_to_except_2(output):
588    try:
589        output.append(2)
590    except ValueError:
591        e = sys.exc_info()[1]
592        output.append('except' in str(e))
593
594no_jump_to_except_2.jump = (2, 3)
595no_jump_to_except_2.output = [True]
596
597def no_jump_to_except_3(output):
598    try:
599        output.append(2)
600    except ValueError, e:
601        output.append('except' in str(e))
602
603no_jump_to_except_3.jump = (2, 3)
604no_jump_to_except_3.output = [True]
605
606def no_jump_to_except_4(output):
607    try:
608        output.append(2)
609    except (ValueError, RuntimeError), e:
610        output.append('except' in str(e))
611
612no_jump_to_except_4.jump = (2, 3)
613no_jump_to_except_4.output = [True]
614
615def no_jump_forwards_into_block(output):
616    try:
617        output.append(2)
618        for i in 1, 2:
619            output.append(4)
620    except ValueError, e:
621        output.append('into' in str(e))
622
623no_jump_forwards_into_block.jump = (2, 4)
624no_jump_forwards_into_block.output = [True]
625
626def no_jump_backwards_into_block(output):
627    try:
628        for i in 1, 2:
629            output.append(3)
630        output.append(4)
631    except ValueError, e:
632        output.append('into' in str(e))
633
634no_jump_backwards_into_block.jump = (4, 3)
635no_jump_backwards_into_block.output = [3, 3, True]
636
637def no_jump_into_finally_block(output):
638    try:
639        try:
640            output.append(3)
641            x = 1
642        finally:
643            output.append(6)
644    except ValueError, e:
645        output.append('finally' in str(e))
646
647no_jump_into_finally_block.jump = (4, 6)
648no_jump_into_finally_block.output = [3, 6, True]  # The 'finally' still runs
649
650def no_jump_out_of_finally_block(output):
651    try:
652        try:
653            output.append(3)
654        finally:
655            output.append(5)
656            output.append(6)
657    except ValueError, e:
658        output.append('finally' in str(e))
659
660no_jump_out_of_finally_block.jump = (5, 1)
661no_jump_out_of_finally_block.output = [3, True]
662
663# This verifies the line-numbers-must-be-integers rule.
664def no_jump_to_non_integers(output):
665    try:
666        output.append(2)
667    except ValueError, e:
668        output.append('integer' in str(e))
669
670no_jump_to_non_integers.jump = (2, "Spam")
671no_jump_to_non_integers.output = [True]
672
673# This verifies that you can't set f_lineno via _getframe or similar
674# trickery.
675def no_jump_without_trace_function():
676    try:
677        previous_frame = sys._getframe().f_back
678        previous_frame.f_lineno = previous_frame.f_lineno
679    except ValueError, e:
680        # This is the exception we wanted; make sure the error message
681        # talks about trace functions.
682        if 'trace' not in str(e):
683            raise
684    else:
685        # Something's wrong - the expected exception wasn't raised.
686        raise RuntimeError, "Trace-function-less jump failed to fail"
687
688
689class JumpTestCase(unittest.TestCase):
690    def compare_jump_output(self, expected, received):
691        if received != expected:
692            self.fail( "Outputs don't match:\n" +
693                       "Expected: " + repr(expected) + "\n" +
694                       "Received: " + repr(received))
695
696    def run_test(self, func):
697        tracer = JumpTracer(func)
698        sys.settrace(tracer.trace)
699        output = []
700        func(output)
701        sys.settrace(None)
702        self.compare_jump_output(func.output, output)
703
704    def test_01_jump_simple_forwards(self):
705        self.run_test(jump_simple_forwards)
706    def test_02_jump_simple_backwards(self):
707        self.run_test(jump_simple_backwards)
708    def test_03_jump_out_of_block_forwards(self):
709        self.run_test(jump_out_of_block_forwards)
710    def test_04_jump_out_of_block_backwards(self):
711        self.run_test(jump_out_of_block_backwards)
712    def test_05_jump_to_codeless_line(self):
713        self.run_test(jump_to_codeless_line)
714    def test_06_jump_to_same_line(self):
715        self.run_test(jump_to_same_line)
716    def test_07_jump_in_nested_finally(self):
717        self.run_test(jump_in_nested_finally)
718    def test_08_no_jump_too_far_forwards(self):
719        self.run_test(no_jump_too_far_forwards)
720    def test_09_no_jump_too_far_backwards(self):
721        self.run_test(no_jump_too_far_backwards)
722    def test_10_no_jump_to_except_1(self):
723        self.run_test(no_jump_to_except_1)
724    def test_11_no_jump_to_except_2(self):
725        self.run_test(no_jump_to_except_2)
726    def test_12_no_jump_to_except_3(self):
727        self.run_test(no_jump_to_except_3)
728    def test_13_no_jump_to_except_4(self):
729        self.run_test(no_jump_to_except_4)
730    def test_14_no_jump_forwards_into_block(self):
731        self.run_test(no_jump_forwards_into_block)
732    def test_15_no_jump_backwards_into_block(self):
733        self.run_test(no_jump_backwards_into_block)
734    def test_16_no_jump_into_finally_block(self):
735        self.run_test(no_jump_into_finally_block)
736    def test_17_no_jump_out_of_finally_block(self):
737        self.run_test(no_jump_out_of_finally_block)
738    def test_18_no_jump_to_non_integers(self):
739        self.run_test(no_jump_to_non_integers)
740    def test_19_no_jump_without_trace_function(self):
741        no_jump_without_trace_function()
742
743    def test_20_large_function(self):
744        d = {}
745        exec("""def f(output):        # line 0
746            x = 0                     # line 1
747            y = 1                     # line 2
748            '''                       # line 3
749            %s                        # lines 4-1004
750            '''                       # line 1005
751            x += 1                    # line 1006
752            output.append(x)          # line 1007
753            return""" % ('\n' * 1000,), d)
754        f = d['f']
755
756        f.jump = (2, 1007)
757        f.output = [0]
758        self.run_test(f)
759
760    def test_jump_to_firstlineno(self):
761        # This tests that PDB can jump back to the first line in a
762        # file.  See issue #1689458.  It can only be triggered in a
763        # function call if the function is defined on a single line.
764        code = compile("""
765# Comments don't count.
766output.append(2)  # firstlineno is here.
767output.append(3)
768output.append(4)
769""", "<fake module>", "exec")
770        class fake_function:
771            func_code = code
772            jump = (2, 0)
773        tracer = JumpTracer(fake_function)
774        sys.settrace(tracer.trace)
775        namespace = {"output": []}
776        exec code in namespace
777        sys.settrace(None)
778        self.compare_jump_output([2, 3, 2, 3, 4], namespace["output"])
779
780
781def test_main():
782    test_support.run_unittest(
783        TraceTestCase,
784        RaisingTraceFuncTestCase,
785        JumpTestCase
786    )
787
788if __name__ == "__main__":
789    test_main()
790