Catch situations where currentframe() returns None. See SF patch #1447410, this is...
[python.git] / Lib / test / test_trace.py
blob944ff9ac701925b224263f04930246d1636117d1
1 # Testing the line trace facility.
3 from test import test_support
4 import unittest
5 import sys
6 import difflib
8 # A very basic example. If this fails, we're in deep trouble.
9 def basic():
10 return 1
12 basic.events = [(0, 'call'),
13 (1, 'line'),
14 (1, 'return')]
16 # Armin Rigo's failing example:
17 def arigo_example():
18 x = 1
19 del x
20 while 0:
21 pass
22 x = 1
24 arigo_example.events = [(0, 'call'),
25 (1, 'line'),
26 (2, 'line'),
27 (3, 'line'),
28 (5, 'line'),
29 (5, 'return')]
31 # check that lines consisting of just one instruction get traced:
32 def one_instr_line():
33 x = 1
34 del x
35 x = 1
37 one_instr_line.events = [(0, 'call'),
38 (1, 'line'),
39 (2, 'line'),
40 (3, 'line'),
41 (3, 'return')]
43 def no_pop_tops(): # 0
44 x = 1 # 1
45 for a in range(2): # 2
46 if a: # 3
47 x = 1 # 4
48 else: # 5
49 x = 1 # 6
51 no_pop_tops.events = [(0, 'call'),
52 (1, 'line'),
53 (2, 'line'),
54 (3, 'line'),
55 (6, 'line'),
56 (2, 'line'),
57 (3, 'line'),
58 (4, 'line'),
59 (2, 'line'),
60 (2, 'return')]
62 def no_pop_blocks():
63 while 0:
64 bla
65 x = 1
67 no_pop_blocks.events = [(0, 'call'),
68 (1, 'line'),
69 (3, 'line'),
70 (3, 'return')]
72 def called(): # line -3
73 x = 1
75 def call(): # line 0
76 called()
78 call.events = [(0, 'call'),
79 (1, 'line'),
80 (-3, 'call'),
81 (-2, 'line'),
82 (-2, 'return'),
83 (1, 'return')]
85 def raises():
86 raise Exception
88 def test_raise():
89 try:
90 raises()
91 except Exception, exc:
92 x = 1
94 test_raise.events = [(0, 'call'),
95 (1, 'line'),
96 (2, 'line'),
97 (-3, 'call'),
98 (-2, 'line'),
99 (-2, 'exception'),
100 (-2, 'return'),
101 (2, 'exception'),
102 (3, 'line'),
103 (4, 'line'),
104 (4, 'return')]
106 def _settrace_and_return(tracefunc):
107 sys.settrace(tracefunc)
108 sys._getframe().f_back.f_trace = tracefunc
109 def settrace_and_return(tracefunc):
110 _settrace_and_return(tracefunc)
112 settrace_and_return.events = [(1, 'return')]
114 def _settrace_and_raise(tracefunc):
115 sys.settrace(tracefunc)
116 sys._getframe().f_back.f_trace = tracefunc
117 raise RuntimeError
118 def settrace_and_raise(tracefunc):
119 try:
120 _settrace_and_raise(tracefunc)
121 except RuntimeError, exc:
122 pass
124 settrace_and_raise.events = [(2, 'exception'),
125 (3, 'line'),
126 (4, 'line'),
127 (4, 'return')]
129 # implicit return example
130 def ireturn_example():
131 a = 5
132 b = 5
133 if a == b:
134 b = a+1
135 else:
136 pass
138 ireturn_example.events = [(0, 'call'),
139 (1, 'line'),
140 (2, 'line'),
141 (3, 'line'),
142 (4, 'line'),
143 (4, 'return')]
145 # Tight loop with while(1) example (SF #765624)
146 def tightloop_example():
147 items = range(0, 3)
148 try:
149 i = 0
150 while 1:
151 b = items[i]; i+=1
152 except IndexError:
153 pass
155 tightloop_example.events = [(0, 'call'),
156 (1, 'line'),
157 (2, 'line'),
158 (3, 'line'),
159 (4, 'line'),
160 (5, 'line'),
161 (5, 'line'),
162 (5, 'line'),
163 (5, 'line'),
164 (5, 'exception'),
165 (6, 'line'),
166 (7, 'line'),
167 (7, 'return')]
169 def tighterloop_example():
170 items = range(1, 4)
171 try:
172 i = 0
173 while 1: i = items[i]
174 except IndexError:
175 pass
177 tighterloop_example.events = [(0, 'call'),
178 (1, 'line'),
179 (2, 'line'),
180 (3, 'line'),
181 (4, 'line'),
182 (4, 'line'),
183 (4, 'line'),
184 (4, 'line'),
185 (4, 'exception'),
186 (5, 'line'),
187 (6, 'line'),
188 (6, 'return')]
190 class Tracer:
191 def __init__(self):
192 self.events = []
193 def trace(self, frame, event, arg):
194 self.events.append((frame.f_lineno, event))
195 return self.trace
197 class TraceTestCase(unittest.TestCase):
198 def compare_events(self, line_offset, events, expected_events):
199 events = [(l - line_offset, e) for (l, e) in events]
200 if events != expected_events:
201 self.fail(
202 "events did not match expectation:\n" +
203 "\n".join(difflib.ndiff(map(str, expected_events),
204 map(str, events))))
207 def run_test(self, func):
208 tracer = Tracer()
209 sys.settrace(tracer.trace)
210 func()
211 sys.settrace(None)
212 self.compare_events(func.func_code.co_firstlineno,
213 tracer.events, func.events)
215 def run_test2(self, func):
216 tracer = Tracer()
217 func(tracer.trace)
218 sys.settrace(None)
219 self.compare_events(func.func_code.co_firstlineno,
220 tracer.events, func.events)
222 def test_01_basic(self):
223 self.run_test(basic)
224 ## XXX: These tests fail with the new ast compiler. They must
225 ## be fixed before a release.
226 ## def test_02_arigo(self):
227 ## self.run_test(arigo_example)
228 def test_03_one_instr(self):
229 self.run_test(one_instr_line)
230 ## def test_04_no_pop_blocks(self):
231 ## self.run_test(no_pop_blocks)
232 ## def test_05_no_pop_tops(self):
233 ## self.run_test(no_pop_tops)
234 def test_06_call(self):
235 self.run_test(call)
236 def test_07_raise(self):
237 self.run_test(test_raise)
239 def test_08_settrace_and_return(self):
240 self.run_test2(settrace_and_return)
241 def test_09_settrace_and_raise(self):
242 self.run_test2(settrace_and_raise)
243 ## def test_10_ireturn(self):
244 ## self.run_test(ireturn_example)
245 def test_11_tightloop(self):
246 self.run_test(tightloop_example)
247 def test_12_tighterloop(self):
248 self.run_test(tighterloop_example)
250 class RaisingTraceFuncTestCase(unittest.TestCase):
251 def trace(self, frame, event, arg):
252 """A trace function that raises an exception in response to a
253 specific trace event."""
254 if event == self.raiseOnEvent:
255 raise ValueError # just something that isn't RuntimeError
256 else:
257 return self.trace
259 def f(self):
260 """The function to trace; raises an exception if that's the case
261 we're testing, so that the 'exception' trace event fires."""
262 if self.raiseOnEvent == 'exception':
263 x = 0
264 y = 1/x
265 else:
266 return 1
268 def run_test_for_event(self, event):
269 """Tests that an exception raised in response to the given event is
270 handled OK."""
271 self.raiseOnEvent = event
272 try:
273 for i in xrange(sys.getrecursionlimit() + 1):
274 sys.settrace(self.trace)
275 try:
276 self.f()
277 except ValueError:
278 pass
279 else:
280 self.fail("exception not thrown!")
281 except RuntimeError:
282 self.fail("recursion counter not reset")
284 # Test the handling of exceptions raised by each kind of trace event.
285 def test_call(self):
286 self.run_test_for_event('call')
287 def test_line(self):
288 self.run_test_for_event('line')
289 def test_return(self):
290 self.run_test_for_event('return')
291 def test_exception(self):
292 self.run_test_for_event('exception')
294 def test_trash_stack(self):
295 def f():
296 for i in range(5):
297 print i # line tracing will raise an exception at this line
299 def g(frame, why, extra):
300 if (why == 'line' and
301 frame.f_lineno == f.func_code.co_firstlineno + 2):
302 raise RuntimeError, "i am crashing"
303 return g
305 sys.settrace(g)
306 try:
308 except RuntimeError:
309 # the test is really that this doesn't segfault:
310 import gc
311 gc.collect()
312 else:
313 self.fail("exception not propagated")
316 # 'Jump' tests: assigning to frame.f_lineno within a trace function
317 # moves the execution position - it's how debuggers implement a Jump
318 # command (aka. "Set next statement").
320 class JumpTracer:
321 """Defines a trace function that jumps from one place to another,
322 with the source and destination lines of the jump being defined by
323 the 'jump' property of the function under test."""
325 def __init__(self, function):
326 self.function = function
327 self.jumpFrom = function.jump[0]
328 self.jumpTo = function.jump[1]
329 self.done = False
331 def trace(self, frame, event, arg):
332 if not self.done and frame.f_code == self.function.func_code:
333 firstLine = frame.f_code.co_firstlineno
334 if frame.f_lineno == firstLine + self.jumpFrom:
335 # Cope with non-integer self.jumpTo (because of
336 # no_jump_to_non_integers below).
337 try:
338 frame.f_lineno = firstLine + self.jumpTo
339 except TypeError:
340 frame.f_lineno = self.jumpTo
341 self.done = True
342 return self.trace
344 # The first set of 'jump' tests are for things that are allowed:
346 def jump_simple_forwards(output):
347 output.append(1)
348 output.append(2)
349 output.append(3)
351 jump_simple_forwards.jump = (1, 3)
352 jump_simple_forwards.output = [3]
354 def jump_simple_backwards(output):
355 output.append(1)
356 output.append(2)
358 jump_simple_backwards.jump = (2, 1)
359 jump_simple_backwards.output = [1, 1, 2]
361 def jump_out_of_block_forwards(output):
362 for i in 1, 2:
363 output.append(2)
364 for j in [3]: # Also tests jumping over a block
365 output.append(4)
366 output.append(5)
368 jump_out_of_block_forwards.jump = (3, 5)
369 jump_out_of_block_forwards.output = [2, 5]
371 def jump_out_of_block_backwards(output):
372 output.append(1)
373 for i in [1]:
374 output.append(3)
375 for j in [2]: # Also tests jumping over a block
376 output.append(5)
377 output.append(6)
378 output.append(7)
380 jump_out_of_block_backwards.jump = (6, 1)
381 jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7]
383 def jump_to_codeless_line(output):
384 output.append(1)
385 # Jumping to this line should skip to the next one.
386 output.append(3)
388 jump_to_codeless_line.jump = (1, 2)
389 jump_to_codeless_line.output = [3]
391 def jump_to_same_line(output):
392 output.append(1)
393 output.append(2)
394 output.append(3)
396 jump_to_same_line.jump = (2, 2)
397 jump_to_same_line.output = [1, 2, 3]
399 # Tests jumping within a finally block, and over one.
400 def jump_in_nested_finally(output):
401 try:
402 output.append(2)
403 finally:
404 output.append(4)
405 try:
406 output.append(6)
407 finally:
408 output.append(8)
409 output.append(9)
411 jump_in_nested_finally.jump = (4, 9)
412 jump_in_nested_finally.output = [2, 9]
414 # The second set of 'jump' tests are for things that are not allowed:
416 def no_jump_too_far_forwards(output):
417 try:
418 output.append(2)
419 output.append(3)
420 except ValueError, e:
421 output.append('after' in str(e))
423 no_jump_too_far_forwards.jump = (3, 6)
424 no_jump_too_far_forwards.output = [2, True]
426 def no_jump_too_far_backwards(output):
427 try:
428 output.append(2)
429 output.append(3)
430 except ValueError, e:
431 output.append('before' in str(e))
433 no_jump_too_far_backwards.jump = (3, -1)
434 no_jump_too_far_backwards.output = [2, True]
436 # Test each kind of 'except' line.
437 def no_jump_to_except_1(output):
438 try:
439 output.append(2)
440 except:
441 e = sys.exc_info()[1]
442 output.append('except' in str(e))
444 no_jump_to_except_1.jump = (2, 3)
445 no_jump_to_except_1.output = [True]
447 def no_jump_to_except_2(output):
448 try:
449 output.append(2)
450 except ValueError:
451 e = sys.exc_info()[1]
452 output.append('except' in str(e))
454 no_jump_to_except_2.jump = (2, 3)
455 no_jump_to_except_2.output = [True]
457 def no_jump_to_except_3(output):
458 try:
459 output.append(2)
460 except ValueError, e:
461 output.append('except' in str(e))
463 no_jump_to_except_3.jump = (2, 3)
464 no_jump_to_except_3.output = [True]
466 def no_jump_to_except_4(output):
467 try:
468 output.append(2)
469 except (ValueError, RuntimeError), e:
470 output.append('except' in str(e))
472 no_jump_to_except_4.jump = (2, 3)
473 no_jump_to_except_4.output = [True]
475 def no_jump_forwards_into_block(output):
476 try:
477 output.append(2)
478 for i in 1, 2:
479 output.append(4)
480 except ValueError, e:
481 output.append('into' in str(e))
483 no_jump_forwards_into_block.jump = (2, 4)
484 no_jump_forwards_into_block.output = [True]
486 def no_jump_backwards_into_block(output):
487 try:
488 for i in 1, 2:
489 output.append(3)
490 output.append(4)
491 except ValueError, e:
492 output.append('into' in str(e))
494 no_jump_backwards_into_block.jump = (4, 3)
495 no_jump_backwards_into_block.output = [3, 3, True]
497 def no_jump_into_finally_block(output):
498 try:
499 try:
500 output.append(3)
501 x = 1
502 finally:
503 output.append(6)
504 except ValueError, e:
505 output.append('finally' in str(e))
507 no_jump_into_finally_block.jump = (4, 6)
508 no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs
510 def no_jump_out_of_finally_block(output):
511 try:
512 try:
513 output.append(3)
514 finally:
515 output.append(5)
516 output.append(6)
517 except ValueError, e:
518 output.append('finally' in str(e))
520 no_jump_out_of_finally_block.jump = (5, 1)
521 no_jump_out_of_finally_block.output = [3, True]
523 # This verifies the line-numbers-must-be-integers rule.
524 def no_jump_to_non_integers(output):
525 try:
526 output.append(2)
527 except ValueError, e:
528 output.append('integer' in str(e))
530 no_jump_to_non_integers.jump = (2, "Spam")
531 no_jump_to_non_integers.output = [True]
533 # This verifies that you can't set f_lineno via _getframe or similar
534 # trickery.
535 def no_jump_without_trace_function():
536 try:
537 previous_frame = sys._getframe().f_back
538 previous_frame.f_lineno = previous_frame.f_lineno
539 except ValueError, e:
540 # This is the exception we wanted; make sure the error message
541 # talks about trace functions.
542 if 'trace' not in str(e):
543 raise
544 else:
545 # Something's wrong - the expected exception wasn't raised.
546 raise RuntimeError, "Trace-function-less jump failed to fail"
549 class JumpTestCase(unittest.TestCase):
550 def compare_jump_output(self, expected, received):
551 if received != expected:
552 self.fail( "Outputs don't match:\n" +
553 "Expected: " + repr(expected) + "\n" +
554 "Received: " + repr(received))
556 def run_test(self, func):
557 tracer = JumpTracer(func)
558 sys.settrace(tracer.trace)
559 output = []
560 func(output)
561 sys.settrace(None)
562 self.compare_jump_output(func.output, output)
564 def test_01_jump_simple_forwards(self):
565 self.run_test(jump_simple_forwards)
566 def test_02_jump_simple_backwards(self):
567 self.run_test(jump_simple_backwards)
568 def test_03_jump_out_of_block_forwards(self):
569 self.run_test(jump_out_of_block_forwards)
570 def test_04_jump_out_of_block_backwards(self):
571 self.run_test(jump_out_of_block_backwards)
572 def test_05_jump_to_codeless_line(self):
573 self.run_test(jump_to_codeless_line)
574 def test_06_jump_to_same_line(self):
575 self.run_test(jump_to_same_line)
576 def test_07_jump_in_nested_finally(self):
577 self.run_test(jump_in_nested_finally)
578 def test_08_no_jump_too_far_forwards(self):
579 self.run_test(no_jump_too_far_forwards)
580 def test_09_no_jump_too_far_backwards(self):
581 self.run_test(no_jump_too_far_backwards)
582 # XXX: These tests cause the interpreter to crash. The frame_setlineno()
583 # function no longer works correctly because the lineno table generated by
584 # the AST compiler is slightly different than with the old compiler.
585 # def test_10_no_jump_to_except_1(self):
586 # self.run_test(no_jump_to_except_1)
587 # def test_11_no_jump_to_except_2(self):
588 # self.run_test(no_jump_to_except_2)
589 # def test_12_no_jump_to_except_3(self):
590 # self.run_test(no_jump_to_except_3)
591 # def test_13_no_jump_to_except_4(self):
592 # self.run_test(no_jump_to_except_4)
593 def test_14_no_jump_forwards_into_block(self):
594 self.run_test(no_jump_forwards_into_block)
595 def test_15_no_jump_backwards_into_block(self):
596 self.run_test(no_jump_backwards_into_block)
597 def test_16_no_jump_into_finally_block(self):
598 self.run_test(no_jump_into_finally_block)
599 def test_17_no_jump_out_of_finally_block(self):
600 self.run_test(no_jump_out_of_finally_block)
601 def test_18_no_jump_to_non_integers(self):
602 self.run_test(no_jump_to_non_integers)
603 def test_19_no_jump_without_trace_function(self):
604 no_jump_without_trace_function()
606 def test_main():
607 test_support.run_unittest(
608 TraceTestCase,
609 RaisingTraceFuncTestCase,
610 JumpTestCase
613 if __name__ == "__main__":
614 test_main()