# Testing the line trace facility.
from test
import test_support
# A very basic example. If this fails, we're in deep trouble.
basic
.events
= [(0, 'call'),
# Armin Rigo's failing example:
arigo_example
.events
= [(0, 'call'),
# check that lines consisting of just one instruction get traced:
one_instr_line
.events
= [(0, 'call'),
no_pop_tops
.events
= [(0, 'call'),
no_pop_blocks
.events
= [(0, 'call'),
call
.events
= [(0, 'call'),
test_raise
.events
= [(0, 'call'),
def _settrace_and_return(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
._getframe
().f_back
.f_trace
= tracefunc
def settrace_and_raise(tracefunc
):
_settrace_and_raise(tracefunc
)
except RuntimeError, exc
:
settrace_and_raise
.events
= [(2, 'exception'),
# implicit return example
ireturn_example
.events
= [(0, 'call'),
# Tight loop with while(1) example (SF #765624)
tightloop_example
.events
= [(0, 'call'),
def tighterloop_example():
tighterloop_example
.events
= [(0, 'call'),
def trace(self
, frame
, event
, arg
):
self
.events
.append((frame
.f_lineno
, event
))
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
:
"events did not match expectation:\n" +
"\n".join(difflib
.ndiff(map(str, expected_events
),
def run_test(self
, func
):
sys
.settrace(tracer
.trace
)
self
.compare_events(func
.func_code
.co_firstlineno
,
tracer
.events
, func
.events
)
def run_test2(self
, func
):
self
.compare_events(func
.func_code
.co_firstlineno
,
tracer
.events
, func
.events
)
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
)
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
)
class RaisingTraceFuncTestCase(unittest
.TestCase
):
def trace(self
, frame
, event
, arg
):
"""A trace function that raises an exception in response to a
if event
== self
.raiseOnEvent
:
raise ValueError # just something that isn't RuntimeError
"""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':
def run_test_for_event(self
, event
):
"""Tests that an exception raised in response to the given event is
self
.raiseOnEvent
= event
for i
in xrange(sys
.getrecursionlimit() + 1):
self
.fail("exception not thrown!")
self
.fail("recursion counter not reset")
# Test the handling of exceptions raised by each kind of trace event.
self
.run_test_for_event('call')
self
.run_test_for_event('line')
self
.run_test_for_event('return')
def test_exception(self
):
self
.run_test_for_event('exception')
def test_trash_stack(self
):
print i
# line tracing will raise an exception at this line
def g(frame
, why
, extra
):
frame
.f_lineno
== f
.func_code
.co_firstlineno
+ 2):
raise RuntimeError, "i am crashing"
# the test is really that this doesn't segfault:
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").
"""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
.jumpFrom
= function
.jump
[0]
self
.jumpTo
= function
.jump
[1]
def trace(self
, frame
, event
, arg
):
if not self
.done
and frame
.f_code
== self
.function
.func_code
:
firstLine
= frame
.f_code
.co_firstlineno
if frame
.f_lineno
== firstLine
+ self
.jumpFrom
:
# Cope with non-integer self.jumpTo (because of
# no_jump_to_non_integers below).
frame
.f_lineno
= firstLine
+ self
.jumpTo
frame
.f_lineno
= self
.jumpTo
# The first set of 'jump' tests are for things that are allowed:
def jump_simple_forwards(output
):
jump_simple_forwards
.jump
= (1, 3)
jump_simple_forwards
.output
= [3]
def jump_simple_backwards(output
):
jump_simple_backwards
.jump
= (2, 1)
jump_simple_backwards
.output
= [1, 1, 2]
def jump_out_of_block_forwards(output
):
for j
in [3]: # Also tests jumping over a block
jump_out_of_block_forwards
.jump
= (3, 5)
jump_out_of_block_forwards
.output
= [2, 5]
def jump_out_of_block_backwards(output
):
for j
in [2]: # Also tests jumping over a block
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
):
# Jumping to this line should skip to the next one.
jump_to_codeless_line
.jump
= (1, 2)
jump_to_codeless_line
.output
= [3]
def jump_to_same_line(output
):
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
):
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
):
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
):
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
):
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
):
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
):
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
):
except (ValueError, RuntimeError), 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
):
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
):
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
):
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
):
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
):
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
def no_jump_without_trace_function():
previous_frame
= sys
._getframe
().f_back
previous_frame
.f_lineno
= previous_frame
.f_lineno
# This is the exception we wanted; make sure the error message
# talks about trace functions.
if 'trace' not in str(e
):
# 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
):
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
)
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()
test_support
.run_unittest(
RaisingTraceFuncTestCase
,
if __name__
== "__main__":