• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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