From 6161077c93603e05790440c391bb55f381fe1443 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Sun, 25 Jul 2010 15:13:42 +0000 Subject: [PATCH] Merged revisions 83140-83141 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r83140 | alexander.belopolsky | 2010-07-25 11:02:55 -0400 (Sun, 25 Jul 2010) | 5 lines Issue #9315: Renamed test_trace to test_sys_settrace and test_profilehooks to test_sys_setprofile so that test_trace can be used for testing the trace module and for naming consistency. ........ r83141 | alexander.belopolsky | 2010-07-25 11:05:42 -0400 (Sun, 25 Jul 2010) | 1 line Corrected comments on where settrace and setprofile are tested. ........ --- Lib/test/test_profilehooks.py | 385 ----------------- Lib/test/test_sys.py | 4 +- Lib/test/test_trace.py | 790 ---------------------------------- 3 files changed, 2 insertions(+), 1177 deletions(-) delete mode 100644 Lib/test/test_profilehooks.py delete mode 100644 Lib/test/test_trace.py diff --git a/Lib/test/test_profilehooks.py b/Lib/test/test_profilehooks.py deleted file mode 100644 index 296c997c53e..00000000000 --- a/Lib/test/test_profilehooks.py +++ /dev/null @@ -1,385 +0,0 @@ -import pprint -import sys -import unittest - -from test import support - -class TestGetProfile(unittest.TestCase): - def setUp(self): - sys.setprofile(None) - - def tearDown(self): - sys.setprofile(None) - - def test_empty(self): - assert sys.getprofile() == None - - def test_setget(self): - def fn(*args): - pass - - sys.setprofile(fn) - assert sys.getprofile() == fn - -class HookWatcher: - def __init__(self): - self.frames = [] - self.events = [] - - def callback(self, frame, event, arg): - if (event == "call" - or event == "return" - or event == "exception"): - self.add_event(event, frame) - - def add_event(self, event, frame=None): - """Add an event to the log.""" - if frame is None: - frame = sys._getframe(1) - - try: - frameno = self.frames.index(frame) - except ValueError: - frameno = len(self.frames) - self.frames.append(frame) - - self.events.append((frameno, event, ident(frame))) - - def get_events(self): - """Remove calls to add_event().""" - disallowed = [ident(self.add_event.__func__), ident(ident)] - self.frames = None - - return [item for item in self.events if item[2] not in disallowed] - - -class ProfileSimulator(HookWatcher): - def __init__(self, testcase): - self.testcase = testcase - self.stack = [] - HookWatcher.__init__(self) - - def callback(self, frame, event, arg): - # Callback registered with sys.setprofile()/sys.settrace() - self.dispatch[event](self, frame) - - def trace_call(self, frame): - self.add_event('call', frame) - self.stack.append(frame) - - def trace_return(self, frame): - self.add_event('return', frame) - self.stack.pop() - - def trace_exception(self, frame): - self.testcase.fail( - "the profiler should never receive exception events") - - def trace_pass(self, frame): - pass - - dispatch = { - 'call': trace_call, - 'exception': trace_exception, - 'return': trace_return, - 'c_call': trace_pass, - 'c_return': trace_pass, - 'c_exception': trace_pass, - } - - -class TestCaseBase(unittest.TestCase): - def check_events(self, callable, expected): - events = capture_events(callable, self.new_watcher()) - if events != expected: - self.fail("Expected events:\n%s\nReceived events:\n%s" - % (pprint.pformat(expected), pprint.pformat(events))) - - -class ProfileHookTestCase(TestCaseBase): - def new_watcher(self): - return HookWatcher() - - def test_simple(self): - def f(p): - pass - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - (1, 'return', f_ident), - ]) - - def test_exception(self): - def f(p): - 1/0 - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - (1, 'return', f_ident), - ]) - - def test_caught_exception(self): - def f(p): - try: 1/0 - except: pass - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - (1, 'return', f_ident), - ]) - - def test_caught_nested_exception(self): - def f(p): - try: 1/0 - except: pass - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - (1, 'return', f_ident), - ]) - - def test_nested_exception(self): - def f(p): - 1/0 - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - # This isn't what I expected: - # (0, 'exception', protect_ident), - # I expected this again: - (1, 'return', f_ident), - ]) - - def test_exception_in_except_clause(self): - def f(p): - 1/0 - def g(p): - try: - f(p) - except: - try: f(p) - except: pass - f_ident = ident(f) - g_ident = ident(g) - self.check_events(g, [(1, 'call', g_ident), - (2, 'call', f_ident), - (2, 'return', f_ident), - (3, 'call', f_ident), - (3, 'return', f_ident), - (1, 'return', g_ident), - ]) - - def test_exception_propogation(self): - def f(p): - 1/0 - def g(p): - try: f(p) - finally: p.add_event("falling through") - f_ident = ident(f) - g_ident = ident(g) - self.check_events(g, [(1, 'call', g_ident), - (2, 'call', f_ident), - (2, 'return', f_ident), - (1, 'falling through', g_ident), - (1, 'return', g_ident), - ]) - - def test_raise_twice(self): - def f(p): - try: 1/0 - except: 1/0 - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - (1, 'return', f_ident), - ]) - - def test_raise_reraise(self): - def f(p): - try: 1/0 - except: raise - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - (1, 'return', f_ident), - ]) - - def test_raise(self): - def f(p): - raise Exception() - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - (1, 'return', f_ident), - ]) - - def test_distant_exception(self): - def f(): - 1/0 - def g(): - f() - def h(): - g() - def i(): - h() - def j(p): - i() - f_ident = ident(f) - g_ident = ident(g) - h_ident = ident(h) - i_ident = ident(i) - j_ident = ident(j) - self.check_events(j, [(1, 'call', j_ident), - (2, 'call', i_ident), - (3, 'call', h_ident), - (4, 'call', g_ident), - (5, 'call', f_ident), - (5, 'return', f_ident), - (4, 'return', g_ident), - (3, 'return', h_ident), - (2, 'return', i_ident), - (1, 'return', j_ident), - ]) - - def test_generator(self): - def f(): - for i in range(2): - yield i - def g(p): - for i in f(): - pass - f_ident = ident(f) - g_ident = ident(g) - self.check_events(g, [(1, 'call', g_ident), - # call the iterator twice to generate values - (2, 'call', f_ident), - (2, 'return', f_ident), - (2, 'call', f_ident), - (2, 'return', f_ident), - # once more; returns end-of-iteration with - # actually raising an exception - (2, 'call', f_ident), - (2, 'return', f_ident), - (1, 'return', g_ident), - ]) - - def test_stop_iteration(self): - def f(): - for i in range(2): - yield i - raise StopIteration - def g(p): - for i in f(): - pass - f_ident = ident(f) - g_ident = ident(g) - self.check_events(g, [(1, 'call', g_ident), - # call the iterator twice to generate values - (2, 'call', f_ident), - (2, 'return', f_ident), - (2, 'call', f_ident), - (2, 'return', f_ident), - # once more to hit the raise: - (2, 'call', f_ident), - (2, 'return', f_ident), - (1, 'return', g_ident), - ]) - - -class ProfileSimulatorTestCase(TestCaseBase): - def new_watcher(self): - return ProfileSimulator(self) - - def test_simple(self): - def f(p): - pass - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - (1, 'return', f_ident), - ]) - - def test_basic_exception(self): - def f(p): - 1/0 - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - (1, 'return', f_ident), - ]) - - def test_caught_exception(self): - def f(p): - try: 1/0 - except: pass - f_ident = ident(f) - self.check_events(f, [(1, 'call', f_ident), - (1, 'return', f_ident), - ]) - - def test_distant_exception(self): - def f(): - 1/0 - def g(): - f() - def h(): - g() - def i(): - h() - def j(p): - i() - f_ident = ident(f) - g_ident = ident(g) - h_ident = ident(h) - i_ident = ident(i) - j_ident = ident(j) - self.check_events(j, [(1, 'call', j_ident), - (2, 'call', i_ident), - (3, 'call', h_ident), - (4, 'call', g_ident), - (5, 'call', f_ident), - (5, 'return', f_ident), - (4, 'return', g_ident), - (3, 'return', h_ident), - (2, 'return', i_ident), - (1, 'return', j_ident), - ]) - - -def ident(function): - if hasattr(function, "f_code"): - code = function.f_code - else: - code = function.__code__ - return code.co_firstlineno, code.co_name - - -def protect(f, p): - try: f(p) - except: pass - -protect_ident = ident(protect) - - -def capture_events(callable, p=None): - try: - sys.setprofile() - except TypeError: - pass - else: - raise support.TestFailed( - 'sys.setprofile() did not raise TypeError') - - if p is None: - p = HookWatcher() - sys.setprofile(p.callback) - protect(callable, p) - sys.setprofile(None) - return p.get_events()[1:-1] - - -def show_events(callable): - import pprint - pprint.pprint(capture_events(callable)) - - -def test_main(): - support.run_unittest( - TestGetProfile, - ProfileHookTestCase, - ProfileSimulatorTestCase - ) - - -if __name__ == "__main__": - test_main() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 24f17fb4ac8..398243a7733 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -164,8 +164,8 @@ class SysModuleTest(unittest.TestCase): # can't check more than the type, as the user might have changed it self.assertTrue(isinstance(sys.getdefaultencoding(), str)) - # testing sys.settrace() is done in test_trace.py - # testing sys.setprofile() is done in test_profile.py + # testing sys.settrace() is done in test_sys_settrace.py + # testing sys.setprofile() is done in test_sys_setprofile.py def test_setcheckinterval(self): self.assertRaises(TypeError, sys.setcheckinterval) diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py deleted file mode 100644 index 43134e98bd4..00000000000 --- a/Lib/test/test_trace.py +++ /dev/null @@ -1,790 +0,0 @@ -# Testing the line trace facility. - -from test import support -import unittest -import sys -import difflib -import gc - -# A very basic example. If this fails, we're in deep trouble. -def basic(): - return 1 - -basic.events = [(0, 'call'), - (1, 'line'), - (1, 'return')] - -# Many of the tests below are tricky because they involve pass statements. -# If there is implicit control flow around a pass statement (in an except -# clause or else caluse) under what conditions do you set a line number -# following that clause? - - -# The entire "while 0:" statement is optimized away. No code -# exists for it, so the line numbers skip directly from "del x" -# to "x = 1". -def arigo_example(): - x = 1 - del x - while 0: - pass - x = 1 - -arigo_example.events = [(0, 'call'), - (1, 'line'), - (2, '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'), - (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'), - (1, 'line'), - (2, 'line'), - (3, 'line'), - (6, 'line'), - (2, 'line'), - (3, 'line'), - (4, 'line'), - (2, 'line'), - (2, 'return')] - -def no_pop_blocks(): - y = 1 - while not y: - bla - x = 1 - -no_pop_blocks.events = [(0, 'call'), - (1, 'line'), - (2, 'line'), - (4, 'line'), - (4, 'return')] - -def called(): # line -3 - x = 1 - -def call(): # line 0 - called() - -call.events = [(0, 'call'), - (1, 'line'), - (-3, 'call'), - (-2, 'line'), - (-2, 'return'), - (1, 'return')] - -def raises(): - raise Exception - -def test_raise(): - try: - raises() - except Exception as exc: - x = 1 - -test_raise.events = [(0, 'call'), - (1, 'line'), - (2, 'line'), - (-3, 'call'), - (-2, 'line'), - (-2, 'exception'), - (-2, 'return'), - (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 as exc: - pass - -settrace_and_raise.events = [(2, 'exception'), - (3, 'line'), - (4, 'line'), - (4, 'return')] - -# implicit return example -# This test is interesting because of the else: pass -# part of the code. The code generate for the true -# part of the if contains a jump past the else branch. -# The compiler then generates an implicit "return None" -# Internally, the compiler visits the pass statement -# and stores its line number for use on the next instruction. -# The next instruction is the implicit return None. -def ireturn_example(): - a = 5 - b = 5 - if a == b: - b = a+1 - else: - pass - -ireturn_example.events = [(0, 'call'), - (1, 'line'), - (2, 'line'), - (3, 'line'), - (4, 'line'), - (6, 'line'), - (6, 'return')] - -# Tight loop with while(1) example (SF #765624) -def tightloop_example(): - items = range(0, 3) - try: - i = 0 - while 1: - b = items[i]; i+=1 - except IndexError: - pass - -tightloop_example.events = [(0, 'call'), - (1, 'line'), - (2, 'line'), - (3, 'line'), - (4, 'line'), - (5, 'line'), - (5, 'line'), - (5, 'line'), - (5, 'line'), - (5, 'exception'), - (6, 'line'), - (7, 'line'), - (7, 'return')] - -def tighterloop_example(): - items = range(1, 4) - try: - i = 0 - while 1: i = items[i] - except IndexError: - pass - -tighterloop_example.events = [(0, 'call'), - (1, 'line'), - (2, 'line'), - (3, 'line'), - (4, 'line'), - (4, 'line'), - (4, 'line'), - (4, 'line'), - (4, 'exception'), - (5, 'line'), - (6, 'line'), - (6, 'return')] - -def generator_function(): - try: - yield True - "continued" - finally: - "finally" -def generator_example(): - # any() will leave the generator before its end - x = any(generator_function()) - - # the following lines were not traced - for x in range(10): - y = x - -generator_example.events = ([(0, 'call'), - (2, 'line'), - (-6, 'call'), - (-5, 'line'), - (-4, 'line'), - (-4, 'return'), - (-4, 'call'), - (-4, 'exception'), - (-1, 'line'), - (-1, 'return')] + - [(5, 'line'), (6, 'line')] * 10 + - [(5, 'line'), (5, 'return')]) - - -class Tracer: - def __init__(self): - self.events = [] - def trace(self, frame, event, arg): - self.events.append((frame.f_lineno, event)) - return self.trace - def traceWithGenexp(self, frame, event, arg): - (o for o in [1]) - self.events.append((frame.f_lineno, event)) - return self.trace - -class TraceTestCase(unittest.TestCase): - - # Disable gc collection when tracing, otherwise the - # deallocators may be traced as well. - def setUp(self): - self.using_gc = gc.isenabled() - gc.disable() - - def tearDown(self): - if self.using_gc: - gc.enable() - - 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([str(x) for x in expected_events], - [str(x) for x in events]))) - - def run_and_compare(self, func, events): - tracer = Tracer() - sys.settrace(tracer.trace) - func() - sys.settrace(None) - self.compare_events(func.__code__.co_firstlineno, - tracer.events, events) - - def run_test(self, func): - self.run_and_compare(func, func.events) - - def run_test2(self, func): - tracer = Tracer() - func(tracer.trace) - sys.settrace(None) - self.compare_events(func.__code__.co_firstlineno, - tracer.events, func.events) - - def set_and_retrieve_none(self): - sys.settrace(None) - assert sys.gettrace() is None - - def set_and_retrieve_func(self): - def fn(*args): - pass - - sys.settrace(fn) - try: - assert sys.gettrace() is fn - finally: - sys.settrace(None) - - def test_01_basic(self): - self.run_test(basic) - def test_02_arigo(self): - self.run_test(arigo_example) - def test_03_one_instr(self): - self.run_test(one_instr_line) - def test_04_no_pop_blocks(self): - self.run_test(no_pop_blocks) - def test_05_no_pop_tops(self): - self.run_test(no_pop_tops) - def test_06_call(self): - self.run_test(call) - def test_07_raise(self): - self.run_test(test_raise) - - def test_08_settrace_and_return(self): - self.run_test2(settrace_and_return) - def test_09_settrace_and_raise(self): - self.run_test2(settrace_and_raise) - def test_10_ireturn(self): - self.run_test(ireturn_example) - def test_11_tightloop(self): - self.run_test(tightloop_example) - def test_12_tighterloop(self): - self.run_test(tighterloop_example) - - def test_13_genexp(self): - self.run_test(generator_example) - # issue1265: if the trace function contains a generator, - # and if the traced function contains another generator - # that is not completely exhausted, the trace stopped. - # Worse: the 'finally' clause was not invoked. - tracer = Tracer() - sys.settrace(tracer.traceWithGenexp) - generator_example() - sys.settrace(None) - self.compare_events(generator_example.__code__.co_firstlineno, - tracer.events, generator_example.events) - - def test_14_onliner_if(self): - def onliners(): - if True: False - else: True - return 0 - self.run_and_compare( - onliners, - [(0, 'call'), - (1, 'line'), - (3, 'line'), - (3, 'return')]) - - def test_15_loops(self): - # issue1750076: "while" expression is skipped by debugger - def for_example(): - for x in range(2): - pass - self.run_and_compare( - for_example, - [(0, 'call'), - (1, 'line'), - (2, 'line'), - (1, 'line'), - (2, 'line'), - (1, 'line'), - (1, 'return')]) - - def while_example(): - # While expression should be traced on every loop - x = 2 - while x > 0: - x -= 1 - self.run_and_compare( - while_example, - [(0, 'call'), - (2, 'line'), - (3, 'line'), - (4, 'line'), - (3, 'line'), - (4, 'line'), - (3, 'line'), - (3, 'return')]) - - def test_16_blank_lines(self): - namespace = {} - exec("def f():\n" + "\n" * 256 + " pass", namespace) - self.run_and_compare( - namespace["f"], - [(0, 'call'), - (257, 'line'), - (257, 'return')]) - - -class RaisingTraceFuncTestCase(unittest.TestCase): - def trace(self, frame, event, arg): - """A trace function that raises an exception in response to a - specific trace event.""" - if event == self.raiseOnEvent: - raise ValueError # just something that isn't RuntimeError - else: - return self.trace - - def f(self): - """The function to trace; raises an exception if that's the case - we're testing, so that the 'exception' trace event fires.""" - if self.raiseOnEvent == 'exception': - x = 0 - y = 1/x - else: - return 1 - - def run_test_for_event(self, event): - """Tests that an exception raised in response to the given event is - handled OK.""" - self.raiseOnEvent = event - try: - for i in range(sys.getrecursionlimit() + 1): - sys.settrace(self.trace) - try: - self.f() - except ValueError: - pass - else: - self.fail("exception not thrown!") - except RuntimeError: - self.fail("recursion counter not reset") - - # Test the handling of exceptions raised by each kind of trace event. - def test_call(self): - self.run_test_for_event('call') - def test_line(self): - self.run_test_for_event('line') - def test_return(self): - self.run_test_for_event('return') - def test_exception(self): - self.run_test_for_event('exception') - - def test_trash_stack(self): - def f(): - for i in range(5): - print(i) # line tracing will raise an exception at this line - - def g(frame, why, extra): - if (why == 'line' and - frame.f_lineno == f.__code__.co_firstlineno + 2): - raise RuntimeError("i am crashing") - return g - - sys.settrace(g) - try: - f() - except RuntimeError: - # the test is really that this doesn't segfault: - import gc - gc.collect() - else: - self.fail("exception not propagated") - - -# 'Jump' tests: assigning to frame.f_lineno within a trace function -# moves the execution position - it's how debuggers implement a Jump -# command (aka. "Set next statement"). - -class JumpTracer: - """Defines a trace function that jumps from one place to another, - with the source and destination lines of the jump being defined by - the 'jump' property of the function under test.""" - - def __init__(self, function): - self.function = function - self.jumpFrom = function.jump[0] - self.jumpTo = function.jump[1] - self.done = False - - def trace(self, frame, event, arg): - if not self.done and frame.f_code == self.function.__code__: - firstLine = frame.f_code.co_firstlineno - if event == 'line' and frame.f_lineno == firstLine + self.jumpFrom: - # Cope with non-integer self.jumpTo (because of - # no_jump_to_non_integers below). - try: - frame.f_lineno = firstLine + self.jumpTo - except TypeError: - frame.f_lineno = self.jumpTo - self.done = True - return self.trace - -# The first set of 'jump' tests are for things that are allowed: - -def jump_simple_forwards(output): - output.append(1) - output.append(2) - output.append(3) - -jump_simple_forwards.jump = (1, 3) -jump_simple_forwards.output = [3] - -def jump_simple_backwards(output): - output.append(1) - output.append(2) - -jump_simple_backwards.jump = (2, 1) -jump_simple_backwards.output = [1, 1, 2] - -def jump_out_of_block_forwards(output): - for i in 1, 2: - output.append(2) - for j in [3]: # Also tests jumping over a block - output.append(4) - output.append(5) - -jump_out_of_block_forwards.jump = (3, 5) -jump_out_of_block_forwards.output = [2, 5] - -def jump_out_of_block_backwards(output): - output.append(1) - for i in [1]: - output.append(3) - for j in [2]: # Also tests jumping over a block - output.append(5) - output.append(6) - output.append(7) - -jump_out_of_block_backwards.jump = (6, 1) -jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7] - -def jump_to_codeless_line(output): - output.append(1) - # Jumping to this line should skip to the next one. - output.append(3) - -jump_to_codeless_line.jump = (1, 2) -jump_to_codeless_line.output = [3] - -def jump_to_same_line(output): - output.append(1) - output.append(2) - output.append(3) - -jump_to_same_line.jump = (2, 2) -jump_to_same_line.output = [1, 2, 3] - -# Tests jumping within a finally block, and over one. -def jump_in_nested_finally(output): - try: - output.append(2) - finally: - output.append(4) - try: - output.append(6) - finally: - output.append(8) - output.append(9) - -jump_in_nested_finally.jump = (4, 9) -jump_in_nested_finally.output = [2, 9] - -# The second set of 'jump' tests are for things that are not allowed: - -def no_jump_too_far_forwards(output): - try: - output.append(2) - output.append(3) - except ValueError as e: - output.append('after' in str(e)) - -no_jump_too_far_forwards.jump = (3, 6) -no_jump_too_far_forwards.output = [2, True] - -def no_jump_too_far_backwards(output): - try: - output.append(2) - output.append(3) - except ValueError as e: - output.append('before' in str(e)) - -no_jump_too_far_backwards.jump = (3, -1) -no_jump_too_far_backwards.output = [2, True] - -# Test each kind of 'except' line. -def no_jump_to_except_1(output): - try: - output.append(2) - except: - e = sys.exc_info()[1] - output.append('except' in str(e)) - -no_jump_to_except_1.jump = (2, 3) -no_jump_to_except_1.output = [True] - -def no_jump_to_except_2(output): - try: - output.append(2) - except ValueError: - e = sys.exc_info()[1] - output.append('except' in str(e)) - -no_jump_to_except_2.jump = (2, 3) -no_jump_to_except_2.output = [True] - -def no_jump_to_except_3(output): - try: - output.append(2) - except ValueError as e: - output.append('except' in str(e)) - -no_jump_to_except_3.jump = (2, 3) -no_jump_to_except_3.output = [True] - -def no_jump_to_except_4(output): - try: - output.append(2) - except (ValueError, RuntimeError) as e: - output.append('except' in str(e)) - -no_jump_to_except_4.jump = (2, 3) -no_jump_to_except_4.output = [True] - -def no_jump_forwards_into_block(output): - try: - output.append(2) - for i in 1, 2: - output.append(4) - except ValueError as e: - output.append('into' in str(e)) - -no_jump_forwards_into_block.jump = (2, 4) -no_jump_forwards_into_block.output = [True] - -def no_jump_backwards_into_block(output): - try: - for i in 1, 2: - output.append(3) - output.append(4) - except ValueError as e: - output.append('into' in str(e)) - -no_jump_backwards_into_block.jump = (4, 3) -no_jump_backwards_into_block.output = [3, 3, True] - -def no_jump_into_finally_block(output): - try: - try: - output.append(3) - x = 1 - finally: - output.append(6) - except ValueError as e: - output.append('finally' in str(e)) - -no_jump_into_finally_block.jump = (4, 6) -no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs - -def no_jump_out_of_finally_block(output): - try: - try: - output.append(3) - finally: - output.append(5) - output.append(6) - except ValueError as e: - output.append('finally' in str(e)) - -no_jump_out_of_finally_block.jump = (5, 1) -no_jump_out_of_finally_block.output = [3, True] - -# This verifies the line-numbers-must-be-integers rule. -def no_jump_to_non_integers(output): - try: - output.append(2) - except ValueError as e: - output.append('integer' in str(e)) - -no_jump_to_non_integers.jump = (2, "Spam") -no_jump_to_non_integers.output = [True] - -# This verifies that you can't set f_lineno via _getframe or similar -# trickery. -def no_jump_without_trace_function(): - try: - previous_frame = sys._getframe().f_back - previous_frame.f_lineno = previous_frame.f_lineno - except ValueError as e: - # This is the exception we wanted; make sure the error message - # talks about trace functions. - if 'trace' not in str(e): - raise - else: - # Something's wrong - the expected exception wasn't raised. - raise RuntimeError("Trace-function-less jump failed to fail") - - -class JumpTestCase(unittest.TestCase): - def compare_jump_output(self, expected, received): - if received != expected: - self.fail( "Outputs don't match:\n" + - "Expected: " + repr(expected) + "\n" + - "Received: " + repr(received)) - - def run_test(self, func): - tracer = JumpTracer(func) - sys.settrace(tracer.trace) - output = [] - func(output) - sys.settrace(None) - self.compare_jump_output(func.output, output) - - def test_01_jump_simple_forwards(self): - self.run_test(jump_simple_forwards) - def test_02_jump_simple_backwards(self): - self.run_test(jump_simple_backwards) - def test_03_jump_out_of_block_forwards(self): - self.run_test(jump_out_of_block_forwards) - def test_04_jump_out_of_block_backwards(self): - self.run_test(jump_out_of_block_backwards) - def test_05_jump_to_codeless_line(self): - self.run_test(jump_to_codeless_line) - def test_06_jump_to_same_line(self): - self.run_test(jump_to_same_line) - def test_07_jump_in_nested_finally(self): - self.run_test(jump_in_nested_finally) - def test_08_no_jump_too_far_forwards(self): - self.run_test(no_jump_too_far_forwards) - def test_09_no_jump_too_far_backwards(self): - self.run_test(no_jump_too_far_backwards) - def test_10_no_jump_to_except_1(self): - self.run_test(no_jump_to_except_1) - def test_11_no_jump_to_except_2(self): - self.run_test(no_jump_to_except_2) - def test_12_no_jump_to_except_3(self): - self.run_test(no_jump_to_except_3) - def test_13_no_jump_to_except_4(self): - self.run_test(no_jump_to_except_4) - def test_14_no_jump_forwards_into_block(self): - self.run_test(no_jump_forwards_into_block) - def test_15_no_jump_backwards_into_block(self): - self.run_test(no_jump_backwards_into_block) - def test_16_no_jump_into_finally_block(self): - self.run_test(no_jump_into_finally_block) - def test_17_no_jump_out_of_finally_block(self): - self.run_test(no_jump_out_of_finally_block) - def test_18_no_jump_to_non_integers(self): - self.run_test(no_jump_to_non_integers) - def test_19_no_jump_without_trace_function(self): - no_jump_without_trace_function() - - def test_20_large_function(self): - d = {} - exec("""def f(output): # line 0 - x = 0 # line 1 - y = 1 # line 2 - ''' # line 3 - %s # lines 4-1004 - ''' # line 1005 - x += 1 # line 1006 - output.append(x) # line 1007 - return""" % ('\n' * 1000,), d) - f = d['f'] - - f.jump = (2, 1007) - f.output = [0] - self.run_test(f) - - def test_jump_to_firstlineno(self): - # This tests that PDB can jump back to the first line in a - # file. See issue #1689458. It can only be triggered in a - # function call if the function is defined on a single line. - code = compile(""" -# Comments don't count. -output.append(2) # firstlineno is here. -output.append(3) -output.append(4) -""", "", "exec") - class fake_function: - __code__ = code - jump = (2, 0) - tracer = JumpTracer(fake_function) - sys.settrace(tracer.trace) - namespace = {"output": []} - exec(code, namespace) - sys.settrace(None) - self.compare_jump_output([2, 3, 2, 3, 4], namespace["output"]) - - -def test_main(): - support.run_unittest( - TraceTestCase, - RaisingTraceFuncTestCase, - JumpTestCase - ) - -if __name__ == "__main__": - test_main()