|
|
|
@ -555,20 +555,35 @@ class RaisingTraceFuncTestCase(unittest.TestCase): |
|
|
|
class JumpTracer: |
|
|
|
"""Defines a trace function that jumps from one place to another.""" |
|
|
|
|
|
|
|
def __init__(self, function, jumpFrom, jumpTo): |
|
|
|
self.function = function |
|
|
|
def __init__(self, function, jumpFrom, jumpTo, event='line', |
|
|
|
decorated=False): |
|
|
|
self.code = function.__code__ |
|
|
|
self.jumpFrom = jumpFrom |
|
|
|
self.jumpTo = jumpTo |
|
|
|
self.event = event |
|
|
|
self.firstLine = None if decorated else self.code.co_firstlineno |
|
|
|
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: |
|
|
|
if self.done: |
|
|
|
return |
|
|
|
# frame.f_code.co_firstlineno is the first line of the decorator when |
|
|
|
# 'function' is decorated and the decorator may be written using |
|
|
|
# multiple physical lines when it is too long. Use the first line |
|
|
|
# trace event in 'function' to find the first line of 'function'. |
|
|
|
if (self.firstLine is None and frame.f_code == self.code and |
|
|
|
event == 'line'): |
|
|
|
self.firstLine = frame.f_lineno - 1 |
|
|
|
if (event == self.event and self.firstLine and |
|
|
|
frame.f_lineno == self.firstLine + self.jumpFrom): |
|
|
|
f = frame |
|
|
|
while f is not None and f.f_code != self.code: |
|
|
|
f = f.f_back |
|
|
|
if f is not None: |
|
|
|
# Cope with non-integer self.jumpTo (because of |
|
|
|
# no_jump_to_non_integers below). |
|
|
|
try: |
|
|
|
frame.f_lineno = firstLine + self.jumpTo |
|
|
|
frame.f_lineno = self.firstLine + self.jumpTo |
|
|
|
except TypeError: |
|
|
|
frame.f_lineno = self.jumpTo |
|
|
|
self.done = True |
|
|
|
@ -608,8 +623,9 @@ class JumpTestCase(unittest.TestCase): |
|
|
|
"Expected: " + repr(expected) + "\n" + |
|
|
|
"Received: " + repr(received)) |
|
|
|
|
|
|
|
def run_test(self, func, jumpFrom, jumpTo, expected, error=None): |
|
|
|
tracer = JumpTracer(func, jumpFrom, jumpTo) |
|
|
|
def run_test(self, func, jumpFrom, jumpTo, expected, error=None, |
|
|
|
event='line', decorated=False): |
|
|
|
tracer = JumpTracer(func, jumpFrom, jumpTo, event, decorated) |
|
|
|
sys.settrace(tracer.trace) |
|
|
|
output = [] |
|
|
|
if error is None: |
|
|
|
@ -620,15 +636,15 @@ class JumpTestCase(unittest.TestCase): |
|
|
|
sys.settrace(None) |
|
|
|
self.compare_jump_output(expected, output) |
|
|
|
|
|
|
|
def jump_test(jumpFrom, jumpTo, expected, error=None): |
|
|
|
def jump_test(jumpFrom, jumpTo, expected, error=None, event='line'): |
|
|
|
"""Decorator that creates a test that makes a jump |
|
|
|
from one place to another in the following code. |
|
|
|
""" |
|
|
|
def decorator(func): |
|
|
|
@wraps(func) |
|
|
|
def test(self): |
|
|
|
# +1 to compensate a decorator line |
|
|
|
self.run_test(func, jumpFrom+1, jumpTo+1, expected, error) |
|
|
|
self.run_test(func, jumpFrom, jumpTo, expected, |
|
|
|
error=error, event=event, decorated=True) |
|
|
|
return test |
|
|
|
return decorator |
|
|
|
|
|
|
|
@ -1128,6 +1144,36 @@ output.append(4) |
|
|
|
sys.settrace(None) |
|
|
|
self.compare_jump_output([2, 3, 2, 3, 4], namespace["output"]) |
|
|
|
|
|
|
|
@jump_test(2, 3, [1], event='call', error=(ValueError, "can't jump from" |
|
|
|
" the 'call' trace event of a new frame")) |
|
|
|
def test_no_jump_from_call(output): |
|
|
|
output.append(1) |
|
|
|
def nested(): |
|
|
|
output.append(3) |
|
|
|
nested() |
|
|
|
output.append(5) |
|
|
|
|
|
|
|
@jump_test(2, 1, [1], event='return', error=(ValueError, |
|
|
|
"can only jump from a 'line' trace event")) |
|
|
|
def test_no_jump_from_return_event(output): |
|
|
|
output.append(1) |
|
|
|
return |
|
|
|
|
|
|
|
@jump_test(2, 1, [1], event='exception', error=(ValueError, |
|
|
|
"can only jump from a 'line' trace event")) |
|
|
|
def test_no_jump_from_exception_event(output): |
|
|
|
output.append(1) |
|
|
|
1 / 0 |
|
|
|
|
|
|
|
@jump_test(3, 2, [2], event='return', error=(ValueError, |
|
|
|
"can't jump from a yield statement")) |
|
|
|
def test_no_jump_from_yield(output): |
|
|
|
def gen(): |
|
|
|
output.append(2) |
|
|
|
yield 3 |
|
|
|
next(gen()) |
|
|
|
output.append(5) |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
unittest.main() |