1 # Testing the line trace facility.
3 from test
import test_support
9 # A very basic example. If this fails, we're in deep trouble.
13 basic
.events
= [(0, 'call'),
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?
23 # The entire "while 0:" statement is optimized away. No code
24 # exists for it, so the line numbers skip directly from "del x"
33 arigo_example
.events
= [(0, 'call'),
39 # check that lines consisting of just one instruction get traced:
45 one_instr_line
.events
= [(0, 'call'),
51 def no_pop_tops(): # 0
53 for a
in range(2): # 2
59 no_pop_tops
.events
= [(0, 'call'),
76 no_pop_blocks
.events
= [(0, 'call'),
82 def called(): # line -3
88 call
.events
= [(0, 'call'),
101 except Exception, exc
:
104 test_raise
.events
= [(0, 'call'),
116 def _settrace_and_return(tracefunc
):
117 sys
.settrace(tracefunc
)
118 sys
._getframe
().f_back
.f_trace
= tracefunc
119 def settrace_and_return(tracefunc
):
120 _settrace_and_return(tracefunc
)
122 settrace_and_return
.events
= [(1, 'return')]
124 def _settrace_and_raise(tracefunc
):
125 sys
.settrace(tracefunc
)
126 sys
._getframe
().f_back
.f_trace
= tracefunc
128 def settrace_and_raise(tracefunc
):
130 _settrace_and_raise(tracefunc
)
131 except RuntimeError, exc
:
134 settrace_and_raise
.events
= [(2, 'exception'),
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.
147 def ireturn_example():
155 ireturn_example
.events
= [(0, 'call'),
163 # Tight loop with while(1) example (SF #765624)
164 def tightloop_example():
173 tightloop_example
.events
= [(0, 'call'),
187 def tighterloop_example():
191 while 1: i
= items
[i
]
195 tighterloop_example
.events
= [(0, 'call'),
208 def generator_function():
214 def generator_example():
215 # any() will leave the generator before its end
216 x
= any(generator_function())
218 # the following lines were not traced
222 generator_example
.events
= ([(0, 'call'),
232 [(5, 'line'), (6, 'line')] * 10 +
233 [(5, 'line'), (5, 'return')])
239 def trace(self
, frame
, event
, arg
):
240 self
.events
.append((frame
.f_lineno
, event
))
242 def traceWithGenexp(self
, frame
, event
, arg
):
244 self
.events
.append((frame
.f_lineno
, event
))
247 class TraceTestCase(unittest
.TestCase
):
249 # Disable gc collection when tracing, otherwise the
250 # deallocators may be traced as well.
252 self
.using_gc
= gc
.isenabled()
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
:
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
])))
267 def run_and_compare(self
, func
, events
):
269 sys
.settrace(tracer
.trace
)
272 self
.compare_events(func
.func_code
.co_firstlineno
,
273 tracer
.events
, events
)
275 def run_test(self
, func
):
276 self
.run_and_compare(func
, func
.events
)
278 def run_test2(self
, func
):
282 self
.compare_events(func
.func_code
.co_firstlineno
,
283 tracer
.events
, func
.events
)
285 def set_and_retrieve_none(self
):
287 assert sys
.gettrace() is None
289 def set_and_retrieve_func(self
):
295 assert sys
.gettrace() is fn
299 def test_01_basic(self
):
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
):
311 def test_07_raise(self
):
312 self
.run_test(test_raise
)
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
)
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.
332 sys
.settrace(tracer
.traceWithGenexp
)
335 self
.compare_events(generator_example
.__code
__.co_firstlineno
,
336 tracer
.events
, generator_example
.events
)
338 def test_14_onliner_if(self
):
343 self
.run_and_compare(
350 def test_15_loops(self
):
351 # issue1750076: "while" expression is skipped by debugger
355 self
.run_and_compare(
366 # While expression should be traced on every loop
370 self
.run_and_compare(
381 def test_16_blank_lines(self
):
382 exec("def f():\n" + "\n" * 256 + " pass")
383 self
.run_and_compare(
390 class RaisingTraceFuncTestCase(unittest
.TestCase
):
391 def trace(self
, frame
, event
, arg
):
392 """A trace function that raises an exception in response to a
393 specific trace event."""
394 if event
== self
.raiseOnEvent
:
395 raise ValueError # just something that isn't RuntimeError
400 """The function to trace; raises an exception if that's the case
401 we're testing, so that the 'exception' trace event fires."""
402 if self
.raiseOnEvent
== 'exception':
408 def run_test_for_event(self
, event
):
409 """Tests that an exception raised in response to the given event is
411 self
.raiseOnEvent
= event
413 for i
in xrange(sys
.getrecursionlimit() + 1):
414 sys
.settrace(self
.trace
)
420 self
.fail("exception not thrown!")
422 self
.fail("recursion counter not reset")
424 # Test the handling of exceptions raised by each kind of trace event.
426 self
.run_test_for_event('call')
428 self
.run_test_for_event('line')
429 def test_return(self
):
430 self
.run_test_for_event('return')
431 def test_exception(self
):
432 self
.run_test_for_event('exception')
434 def test_trash_stack(self
):
437 print i
# line tracing will raise an exception at this line
439 def g(frame
, why
, extra
):
440 if (why
== 'line' and
441 frame
.f_lineno
== f
.func_code
.co_firstlineno
+ 2):
442 raise RuntimeError, "i am crashing"
449 # the test is really that this doesn't segfault:
453 self
.fail("exception not propagated")
456 # 'Jump' tests: assigning to frame.f_lineno within a trace function
457 # moves the execution position - it's how debuggers implement a Jump
458 # command (aka. "Set next statement").
461 """Defines a trace function that jumps from one place to another,
462 with the source and destination lines of the jump being defined by
463 the 'jump' property of the function under test."""
465 def __init__(self
, function
):
466 self
.function
= function
467 self
.jumpFrom
= function
.jump
[0]
468 self
.jumpTo
= function
.jump
[1]
471 def trace(self
, frame
, event
, arg
):
472 if not self
.done
and frame
.f_code
== self
.function
.func_code
:
473 firstLine
= frame
.f_code
.co_firstlineno
474 if event
== 'line' and frame
.f_lineno
== firstLine
+ self
.jumpFrom
:
475 # Cope with non-integer self.jumpTo (because of
476 # no_jump_to_non_integers below).
478 frame
.f_lineno
= firstLine
+ self
.jumpTo
480 frame
.f_lineno
= self
.jumpTo
484 # The first set of 'jump' tests are for things that are allowed:
486 def jump_simple_forwards(output
):
491 jump_simple_forwards
.jump
= (1, 3)
492 jump_simple_forwards
.output
= [3]
494 def jump_simple_backwards(output
):
498 jump_simple_backwards
.jump
= (2, 1)
499 jump_simple_backwards
.output
= [1, 1, 2]
501 def jump_out_of_block_forwards(output
):
504 for j
in [3]: # Also tests jumping over a block
508 jump_out_of_block_forwards
.jump
= (3, 5)
509 jump_out_of_block_forwards
.output
= [2, 5]
511 def jump_out_of_block_backwards(output
):
515 for j
in [2]: # Also tests jumping over a block
520 jump_out_of_block_backwards
.jump
= (6, 1)
521 jump_out_of_block_backwards
.output
= [1, 3, 5, 1, 3, 5, 6, 7]
523 def jump_to_codeless_line(output
):
525 # Jumping to this line should skip to the next one.
528 jump_to_codeless_line
.jump
= (1, 2)
529 jump_to_codeless_line
.output
= [3]
531 def jump_to_same_line(output
):
536 jump_to_same_line
.jump
= (2, 2)
537 jump_to_same_line
.output
= [1, 2, 3]
539 # Tests jumping within a finally block, and over one.
540 def jump_in_nested_finally(output
):
551 jump_in_nested_finally
.jump
= (4, 9)
552 jump_in_nested_finally
.output
= [2, 9]
554 # The second set of 'jump' tests are for things that are not allowed:
556 def no_jump_too_far_forwards(output
):
560 except ValueError, e
:
561 output
.append('after' in str(e
))
563 no_jump_too_far_forwards
.jump
= (3, 6)
564 no_jump_too_far_forwards
.output
= [2, True]
566 def no_jump_too_far_backwards(output
):
570 except ValueError, e
:
571 output
.append('before' in str(e
))
573 no_jump_too_far_backwards
.jump
= (3, -1)
574 no_jump_too_far_backwards
.output
= [2, True]
576 # Test each kind of 'except' line.
577 def no_jump_to_except_1(output
):
581 e
= sys
.exc_info()[1]
582 output
.append('except' in str(e
))
584 no_jump_to_except_1
.jump
= (2, 3)
585 no_jump_to_except_1
.output
= [True]
587 def no_jump_to_except_2(output
):
591 e
= sys
.exc_info()[1]
592 output
.append('except' in str(e
))
594 no_jump_to_except_2
.jump
= (2, 3)
595 no_jump_to_except_2
.output
= [True]
597 def no_jump_to_except_3(output
):
600 except ValueError, e
:
601 output
.append('except' in str(e
))
603 no_jump_to_except_3
.jump
= (2, 3)
604 no_jump_to_except_3
.output
= [True]
606 def no_jump_to_except_4(output
):
609 except (ValueError, RuntimeError), e
:
610 output
.append('except' in str(e
))
612 no_jump_to_except_4
.jump
= (2, 3)
613 no_jump_to_except_4
.output
= [True]
615 def no_jump_forwards_into_block(output
):
620 except ValueError, e
:
621 output
.append('into' in str(e
))
623 no_jump_forwards_into_block
.jump
= (2, 4)
624 no_jump_forwards_into_block
.output
= [True]
626 def no_jump_backwards_into_block(output
):
631 except ValueError, e
:
632 output
.append('into' in str(e
))
634 no_jump_backwards_into_block
.jump
= (4, 3)
635 no_jump_backwards_into_block
.output
= [3, 3, True]
637 def no_jump_into_finally_block(output
):
644 except ValueError, e
:
645 output
.append('finally' in str(e
))
647 no_jump_into_finally_block
.jump
= (4, 6)
648 no_jump_into_finally_block
.output
= [3, 6, True] # The 'finally' still runs
650 def no_jump_out_of_finally_block(output
):
657 except ValueError, e
:
658 output
.append('finally' in str(e
))
660 no_jump_out_of_finally_block
.jump
= (5, 1)
661 no_jump_out_of_finally_block
.output
= [3, True]
663 # This verifies the line-numbers-must-be-integers rule.
664 def no_jump_to_non_integers(output
):
667 except ValueError, e
:
668 output
.append('integer' in str(e
))
670 no_jump_to_non_integers
.jump
= (2, "Spam")
671 no_jump_to_non_integers
.output
= [True]
673 # This verifies that you can't set f_lineno via _getframe or similar
675 def no_jump_without_trace_function():
677 previous_frame
= sys
._getframe
().f_back
678 previous_frame
.f_lineno
= previous_frame
.f_lineno
679 except ValueError, e
:
680 # This is the exception we wanted; make sure the error message
681 # talks about trace functions.
682 if 'trace' not in str(e
):
685 # Something's wrong - the expected exception wasn't raised.
686 raise RuntimeError, "Trace-function-less jump failed to fail"
689 class JumpTestCase(unittest
.TestCase
):
690 def compare_jump_output(self
, expected
, received
):
691 if received
!= expected
:
692 self
.fail( "Outputs don't match:\n" +
693 "Expected: " + repr(expected
) + "\n" +
694 "Received: " + repr(received
))
696 def run_test(self
, func
):
697 tracer
= JumpTracer(func
)
698 sys
.settrace(tracer
.trace
)
702 self
.compare_jump_output(func
.output
, output
)
704 def test_01_jump_simple_forwards(self
):
705 self
.run_test(jump_simple_forwards
)
706 def test_02_jump_simple_backwards(self
):
707 self
.run_test(jump_simple_backwards
)
708 def test_03_jump_out_of_block_forwards(self
):
709 self
.run_test(jump_out_of_block_forwards
)
710 def test_04_jump_out_of_block_backwards(self
):
711 self
.run_test(jump_out_of_block_backwards
)
712 def test_05_jump_to_codeless_line(self
):
713 self
.run_test(jump_to_codeless_line
)
714 def test_06_jump_to_same_line(self
):
715 self
.run_test(jump_to_same_line
)
716 def test_07_jump_in_nested_finally(self
):
717 self
.run_test(jump_in_nested_finally
)
718 def test_08_no_jump_too_far_forwards(self
):
719 self
.run_test(no_jump_too_far_forwards
)
720 def test_09_no_jump_too_far_backwards(self
):
721 self
.run_test(no_jump_too_far_backwards
)
722 def test_10_no_jump_to_except_1(self
):
723 self
.run_test(no_jump_to_except_1
)
724 def test_11_no_jump_to_except_2(self
):
725 self
.run_test(no_jump_to_except_2
)
726 def test_12_no_jump_to_except_3(self
):
727 self
.run_test(no_jump_to_except_3
)
728 def test_13_no_jump_to_except_4(self
):
729 self
.run_test(no_jump_to_except_4
)
730 def test_14_no_jump_forwards_into_block(self
):
731 self
.run_test(no_jump_forwards_into_block
)
732 def test_15_no_jump_backwards_into_block(self
):
733 self
.run_test(no_jump_backwards_into_block
)
734 def test_16_no_jump_into_finally_block(self
):
735 self
.run_test(no_jump_into_finally_block
)
736 def test_17_no_jump_out_of_finally_block(self
):
737 self
.run_test(no_jump_out_of_finally_block
)
738 def test_18_no_jump_to_non_integers(self
):
739 self
.run_test(no_jump_to_non_integers
)
740 def test_19_no_jump_without_trace_function(self
):
741 no_jump_without_trace_function()
743 def test_20_large_function(self
):
745 exec("""def f(output): # line 0
752 output.append(x) # line 1007
753 return""" % ('\n' * 1000,), d
)
760 def test_jump_to_firstlineno(self
):
761 # This tests that PDB can jump back to the first line in a
762 # file. See issue #1689458. It can only be triggered in a
763 # function call if the function is defined on a single line.
765 # Comments don't count.
766 output.append(2) # firstlineno is here.
769 """, "<fake module>", "exec")
773 tracer
= JumpTracer(fake_function
)
774 sys
.settrace(tracer
.trace
)
775 namespace
= {"output": []}
776 exec code
in namespace
778 self
.compare_jump_output([2, 3, 2, 3, 4], namespace
["output"])
782 test_support
.run_unittest(
784 RaisingTraceFuncTestCase
,
788 if __name__
== "__main__":