# Testing the line trace facility. import test_support import unittest import sys import difflib if not __debug__: raise test_support.TestSkipped, "tracing not supported under -O" # A very basic example. If this fails, we're in deep trouble. def basic(): return 1 basic.events = [(0, 'call'), (0, 'line'), (1, 'line'), (1, 'return')] # Armin Rigo's failing example: def arigo_example(): x = 1 del x while 0: pass x = 1 arigo_example.events = [(0, 'call'), (0, 'line'), (1, 'line'), (2, 'line'), (3, 'line'), (3, 'line'), (5, 'line'), (5, 'return')] # check that lines consisting of just one instruction get traced: def one_instr_line(): x = 1 del x x = 1 one_instr_line.events = [(0, 'call'), (0, 'line'), (1, 'line'), (2, 'line'), (3, 'line'), (3, 'return')] def no_pop_tops(): # 0 x = 1 # 1 for a in range(2): # 2 if a: # 3 x = 1 # 4 else: # 5 x = 1 # 6 no_pop_tops.events = [(0, 'call'), (0, 'line'), (1, 'line'), (2, 'line'), (2, 'line'), (3, 'line'), (6, 'line'), (2, 'line'), (3, 'line'), (4, 'line'), (2, 'line'), (2, 'return')] def no_pop_blocks(): while 0: bla x = 1 no_pop_blocks.events = [(0, 'call'), (0, 'line'), (1, 'line'), (1, 'line'), (3, 'line'), (3, 'return')] def called(): # line -3 x = 1 def call(): # line 0 called() call.events = [(0, 'call'), (0, 'line'), (1, 'line'), (-3, 'call'), (-3, 'line'), (-2, 'line'), (-2, 'return'), (1, 'return')] def raises(): raise Exception def test_raise(): try: raises() except Exception, exc: x = 1 test_raise.events = [(0, 'call'), (0, 'line'), (1, 'line'), (2, 'line'), (-3, 'call'), (-3, 'line'), (-2, 'line'), (-2, 'exception'), (2, 'exception'), (3, 'line'), (4, 'line'), (4, 'return')] def _settrace_and_return(tracefunc): sys.settrace(tracefunc) sys._getframe().f_back.f_trace = tracefunc def settrace_and_return(tracefunc): _settrace_and_return(tracefunc) settrace_and_return.events = [(1, 'return')] def _settrace_and_raise(tracefunc): sys.settrace(tracefunc) sys._getframe().f_back.f_trace = tracefunc raise RuntimeError def settrace_and_raise(tracefunc): try: _settrace_and_raise(tracefunc) except RuntimeError, exc: pass settrace_and_raise.events = [(2, 'exception'), (3, 'line'), (4, 'line'), (4, 'return')] class Tracer: def __init__(self): self.events = [] def trace(self, frame, event, arg): self.events.append((frame.f_lineno, event)) return self.trace class TraceTestCase(unittest.TestCase): def compare_events(self, line_offset, events, expected_events): events = [(l - line_offset, e) for (l, e) in events] if events != expected_events: self.fail( "events did not match expectation:\n" + "\n".join(difflib.ndiff(map(str, expected_events), map(str, events)))) def run_test(self, func): tracer = Tracer() sys.settrace(tracer.trace) func() sys.settrace(None) self.compare_events(func.func_code.co_firstlineno, tracer.events, func.events) def run_test2(self, func): tracer = Tracer() func(tracer.trace) sys.settrace(None) self.compare_events(func.func_code.co_firstlineno, tracer.events, func.events) def test_1_basic(self): self.run_test(basic) def test_2_arigo(self): self.run_test(arigo_example) def test_3_one_instr(self): self.run_test(one_instr_line) def test_4_no_pop_blocks(self): self.run_test(no_pop_blocks) def test_5_no_pop_tops(self): self.run_test(no_pop_tops) def test_6_call(self): self.run_test(call) def test_7_raise(self): self.run_test(test_raise) def test_8_settrace_and_return(self): self.run_test2(settrace_and_return) def test_9_settrace_and_raise(self): self.run_test2(settrace_and_raise) class RaisingTraceFuncTestCase(unittest.TestCase): def test_it(self): def tr(frame, event, arg): raise ValueError # just something that isn't RuntimeError def f(): return 1 try: for i in xrange(sys.getrecursionlimit() + 1): sys.settrace(tr) try: f() except ValueError: pass else: self.fail("exception not thrown!") except RuntimeError: self.fail("recursion counter not reset") def test_main(): test_support.run_unittest(TraceTestCase) test_support.run_unittest(RaisingTraceFuncTestCase) if __name__ == "__main__": test_main()