1 | # Testing the line trace facility.
|
---|
2 |
|
---|
3 | from test import test_support
|
---|
4 | import unittest
|
---|
5 | import sys
|
---|
6 | import difflib
|
---|
7 | import gc
|
---|
8 |
|
---|
9 | # A very basic example. If this fails, we're in deep trouble.
|
---|
10 | def basic():
|
---|
11 | return 1
|
---|
12 |
|
---|
13 | basic.events = [(0, 'call'),
|
---|
14 | (1, 'line'),
|
---|
15 | (1, 'return')]
|
---|
16 |
|
---|
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?
|
---|
21 |
|
---|
22 |
|
---|
23 | # The entire "while 0:" statement is optimized away. No code
|
---|
24 | # exists for it, so the line numbers skip directly from "del x"
|
---|
25 | # to "x = 1".
|
---|
26 | def arigo_example():
|
---|
27 | x = 1
|
---|
28 | del x
|
---|
29 | while 0:
|
---|
30 | pass
|
---|
31 | x = 1
|
---|
32 |
|
---|
33 | arigo_example.events = [(0, 'call'),
|
---|
34 | (1, 'line'),
|
---|
35 | (2, 'line'),
|
---|
36 | (5, 'line'),
|
---|
37 | (5, 'return')]
|
---|
38 |
|
---|
39 | # check that lines consisting of just one instruction get traced:
|
---|
40 | def one_instr_line():
|
---|
41 | x = 1
|
---|
42 | del x
|
---|
43 | x = 1
|
---|
44 |
|
---|
45 | one_instr_line.events = [(0, 'call'),
|
---|
46 | (1, 'line'),
|
---|
47 | (2, 'line'),
|
---|
48 | (3, 'line'),
|
---|
49 | (3, 'return')]
|
---|
50 |
|
---|
51 | def no_pop_tops(): # 0
|
---|
52 | x = 1 # 1
|
---|
53 | for a in range(2): # 2
|
---|
54 | if a: # 3
|
---|
55 | x = 1 # 4
|
---|
56 | else: # 5
|
---|
57 | x = 1 # 6
|
---|
58 |
|
---|
59 | no_pop_tops.events = [(0, 'call'),
|
---|
60 | (1, 'line'),
|
---|
61 | (2, 'line'),
|
---|
62 | (3, 'line'),
|
---|
63 | (6, 'line'),
|
---|
64 | (2, 'line'),
|
---|
65 | (3, 'line'),
|
---|
66 | (4, 'line'),
|
---|
67 | (2, 'line'),
|
---|
68 | (2, 'return')]
|
---|
69 |
|
---|
70 | def no_pop_blocks():
|
---|
71 | y = 1
|
---|
72 | while not y:
|
---|
73 | bla
|
---|
74 | x = 1
|
---|
75 |
|
---|
76 | no_pop_blocks.events = [(0, 'call'),
|
---|
77 | (1, 'line'),
|
---|
78 | (2, 'line'),
|
---|
79 | (4, 'line'),
|
---|
80 | (4, 'return')]
|
---|
81 |
|
---|
82 | def called(): # line -3
|
---|
83 | x = 1
|
---|
84 |
|
---|
85 | def call(): # line 0
|
---|
86 | called()
|
---|
87 |
|
---|
88 | call.events = [(0, 'call'),
|
---|
89 | (1, 'line'),
|
---|
90 | (-3, 'call'),
|
---|
91 | (-2, 'line'),
|
---|
92 | (-2, 'return'),
|
---|
93 | (1, 'return')]
|
---|
94 |
|
---|
95 | def raises():
|
---|
96 | raise Exception
|
---|
97 |
|
---|
98 | def test_raise():
|
---|
99 | try:
|
---|
100 | raises()
|
---|
101 | except Exception, exc:
|
---|
102 | x = 1
|
---|
103 |
|
---|
104 | test_raise.events = [(0, 'call'),
|
---|
105 | (1, 'line'),
|
---|
106 | (2, 'line'),
|
---|
107 | (-3, 'call'),
|
---|
108 | (-2, 'line'),
|
---|
109 | (-2, 'exception'),
|
---|
110 | (-2, 'return'),
|
---|
111 | (2, 'exception'),
|
---|
112 | (3, 'line'),
|
---|
113 | (4, 'line'),
|
---|
114 | (4, 'return')]
|
---|
115 |
|
---|
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)
|
---|
121 |
|
---|
122 | settrace_and_return.events = [(1, 'return')]
|
---|
123 |
|
---|
124 | def _settrace_and_raise(tracefunc):
|
---|
125 | sys.settrace(tracefunc)
|
---|
126 | sys._getframe().f_back.f_trace = tracefunc
|
---|
127 | raise RuntimeError
|
---|
128 | def settrace_and_raise(tracefunc):
|
---|
129 | try:
|
---|
130 | _settrace_and_raise(tracefunc)
|
---|
131 | except RuntimeError, exc:
|
---|
132 | pass
|
---|
133 |
|
---|
134 | settrace_and_raise.events = [(2, 'exception'),
|
---|
135 | (3, 'line'),
|
---|
136 | (4, 'line'),
|
---|
137 | (4, 'return')]
|
---|
138 |
|
---|
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():
|
---|
148 | a = 5
|
---|
149 | b = 5
|
---|
150 | if a == b:
|
---|
151 | b = a+1
|
---|
152 | else:
|
---|
153 | pass
|
---|
154 |
|
---|
155 | ireturn_example.events = [(0, 'call'),
|
---|
156 | (1, 'line'),
|
---|
157 | (2, 'line'),
|
---|
158 | (3, 'line'),
|
---|
159 | (4, 'line'),
|
---|
160 | (6, 'line'),
|
---|
161 | (6, 'return')]
|
---|
162 |
|
---|
163 | # Tight loop with while(1) example (SF #765624)
|
---|
164 | def tightloop_example():
|
---|
165 | items = range(0, 3)
|
---|
166 | try:
|
---|
167 | i = 0
|
---|
168 | while 1:
|
---|
169 | b = items[i]; i+=1
|
---|
170 | except IndexError:
|
---|
171 | pass
|
---|
172 |
|
---|
173 | tightloop_example.events = [(0, 'call'),
|
---|
174 | (1, 'line'),
|
---|
175 | (2, 'line'),
|
---|
176 | (3, 'line'),
|
---|
177 | (4, 'line'),
|
---|
178 | (5, 'line'),
|
---|
179 | (5, 'line'),
|
---|
180 | (5, 'line'),
|
---|
181 | (5, 'line'),
|
---|
182 | (5, 'exception'),
|
---|
183 | (6, 'line'),
|
---|
184 | (7, 'line'),
|
---|
185 | (7, 'return')]
|
---|
186 |
|
---|
187 | def tighterloop_example():
|
---|
188 | items = range(1, 4)
|
---|
189 | try:
|
---|
190 | i = 0
|
---|
191 | while 1: i = items[i]
|
---|
192 | except IndexError:
|
---|
193 | pass
|
---|
194 |
|
---|
195 | tighterloop_example.events = [(0, 'call'),
|
---|
196 | (1, 'line'),
|
---|
197 | (2, 'line'),
|
---|
198 | (3, 'line'),
|
---|
199 | (4, 'line'),
|
---|
200 | (4, 'line'),
|
---|
201 | (4, 'line'),
|
---|
202 | (4, 'line'),
|
---|
203 | (4, 'exception'),
|
---|
204 | (5, 'line'),
|
---|
205 | (6, 'line'),
|
---|
206 | (6, 'return')]
|
---|
207 |
|
---|
208 | def generator_function():
|
---|
209 | try:
|
---|
210 | yield True
|
---|
211 | "continued"
|
---|
212 | finally:
|
---|
213 | "finally"
|
---|
214 | def generator_example():
|
---|
215 | # any() will leave the generator before its end
|
---|
216 | x = any(generator_function())
|
---|
217 |
|
---|
218 | # the following lines were not traced
|
---|
219 | for x in range(10):
|
---|
220 | y = x
|
---|
221 |
|
---|
222 | generator_example.events = ([(0, 'call'),
|
---|
223 | (2, 'line'),
|
---|
224 | (-6, 'call'),
|
---|
225 | (-5, 'line'),
|
---|
226 | (-4, 'line'),
|
---|
227 | (-4, 'return'),
|
---|
228 | (-4, 'call'),
|
---|
229 | (-4, 'exception'),
|
---|
230 | (-1, 'line'),
|
---|
231 | (-1, 'return')] +
|
---|
232 | [(5, 'line'), (6, 'line')] * 10 +
|
---|
233 | [(5, 'line'), (5, 'return')])
|
---|
234 |
|
---|
235 |
|
---|
236 | class Tracer:
|
---|
237 | def __init__(self):
|
---|
238 | self.events = []
|
---|
239 | def trace(self, frame, event, arg):
|
---|
240 | self.events.append((frame.f_lineno, event))
|
---|
241 | return self.trace
|
---|
242 | def traceWithGenexp(self, frame, event, arg):
|
---|
243 | (o for o in [1])
|
---|
244 | self.events.append((frame.f_lineno, event))
|
---|
245 | return self.trace
|
---|
246 |
|
---|
247 | class TraceTestCase(unittest.TestCase):
|
---|
248 |
|
---|
249 | # Disable gc collection when tracing, otherwise the
|
---|
250 | # deallocators may be traced as well.
|
---|
251 | def setUp(self):
|
---|
252 | self.using_gc = gc.isenabled()
|
---|
253 | gc.disable()
|
---|
254 |
|
---|
255 | def tearDown(self):
|
---|
256 | if self.using_gc:
|
---|
257 | gc.enable()
|
---|
258 |
|
---|
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:
|
---|
262 | self.fail(
|
---|
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])))
|
---|
266 |
|
---|
267 | def run_and_compare(self, func, events):
|
---|
268 | tracer = Tracer()
|
---|
269 | sys.settrace(tracer.trace)
|
---|
270 | func()
|
---|
271 | sys.settrace(None)
|
---|
272 | self.compare_events(func.func_code.co_firstlineno,
|
---|
273 | tracer.events, events)
|
---|
274 |
|
---|
275 | def run_test(self, func):
|
---|
276 | self.run_and_compare(func, func.events)
|
---|
277 |
|
---|
278 | def run_test2(self, func):
|
---|
279 | tracer = Tracer()
|
---|
280 | func(tracer.trace)
|
---|
281 | sys.settrace(None)
|
---|
282 | self.compare_events(func.func_code.co_firstlineno,
|
---|
283 | tracer.events, func.events)
|
---|
284 |
|
---|
285 | def test_set_and_retrieve_none(self):
|
---|
286 | sys.settrace(None)
|
---|
287 | assert sys.gettrace() is None
|
---|
288 |
|
---|
289 | def test_set_and_retrieve_func(self):
|
---|
290 | def fn(*args):
|
---|
291 | pass
|
---|
292 |
|
---|
293 | sys.settrace(fn)
|
---|
294 | try:
|
---|
295 | assert sys.gettrace() is fn
|
---|
296 | finally:
|
---|
297 | sys.settrace(None)
|
---|
298 |
|
---|
299 | def test_01_basic(self):
|
---|
300 | self.run_test(basic)
|
---|
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):
|
---|
310 | self.run_test(call)
|
---|
311 | def test_07_raise(self):
|
---|
312 | self.run_test(test_raise)
|
---|
313 |
|
---|
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)
|
---|
324 |
|
---|
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.
|
---|
331 | tracer = Tracer()
|
---|
332 | sys.settrace(tracer.traceWithGenexp)
|
---|
333 | generator_example()
|
---|
334 | sys.settrace(None)
|
---|
335 | self.compare_events(generator_example.__code__.co_firstlineno,
|
---|
336 | tracer.events, generator_example.events)
|
---|
337 |
|
---|
338 | def test_14_onliner_if(self):
|
---|
339 | def onliners():
|
---|
340 | if True: False
|
---|
341 | else: True
|
---|
342 | return 0
|
---|
343 | self.run_and_compare(
|
---|
344 | onliners,
|
---|
345 | [(0, 'call'),
|
---|
346 | (1, 'line'),
|
---|
347 | (3, 'line'),
|
---|
348 | (3, 'return')])
|
---|
349 |
|
---|
350 | def test_15_loops(self):
|
---|
351 | # issue1750076: "while" expression is skipped by debugger
|
---|
352 | def for_example():
|
---|
353 | for x in range(2):
|
---|
354 | pass
|
---|
355 | self.run_and_compare(
|
---|
356 | for_example,
|
---|
357 | [(0, 'call'),
|
---|
358 | (1, 'line'),
|
---|
359 | (2, 'line'),
|
---|
360 | (1, 'line'),
|
---|
361 | (2, 'line'),
|
---|
362 | (1, 'line'),
|
---|
363 | (1, 'return')])
|
---|
364 |
|
---|
365 | def while_example():
|
---|
366 | # While expression should be traced on every loop
|
---|
367 | x = 2
|
---|
368 | while x > 0:
|
---|
369 | x -= 1
|
---|
370 | self.run_and_compare(
|
---|
371 | while_example,
|
---|
372 | [(0, 'call'),
|
---|
373 | (2, 'line'),
|
---|
374 | (3, 'line'),
|
---|
375 | (4, 'line'),
|
---|
376 | (3, 'line'),
|
---|
377 | (4, 'line'),
|
---|
378 | (3, 'line'),
|
---|
379 | (3, 'return')])
|
---|
380 |
|
---|
381 | def test_16_blank_lines(self):
|
---|
382 | exec("def f():\n" + "\n" * 256 + " pass")
|
---|
383 | self.run_and_compare(
|
---|
384 | f,
|
---|
385 | [(0, 'call'),
|
---|
386 | (257, 'line'),
|
---|
387 | (257, 'return')])
|
---|
388 |
|
---|
389 |
|
---|
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
|
---|
396 | else:
|
---|
397 | return self.trace
|
---|
398 |
|
---|
399 | def f(self):
|
---|
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':
|
---|
403 | x = 0
|
---|
404 | y = 1 // x
|
---|
405 | else:
|
---|
406 | return 1
|
---|
407 |
|
---|
408 | def run_test_for_event(self, event):
|
---|
409 | """Tests that an exception raised in response to the given event is
|
---|
410 | handled OK."""
|
---|
411 | self.raiseOnEvent = event
|
---|
412 | try:
|
---|
413 | for i in xrange(sys.getrecursionlimit() + 1):
|
---|
414 | sys.settrace(self.trace)
|
---|
415 | try:
|
---|
416 | self.f()
|
---|
417 | except ValueError:
|
---|
418 | pass
|
---|
419 | else:
|
---|
420 | self.fail("exception not raised!")
|
---|
421 | except RuntimeError:
|
---|
422 | self.fail("recursion counter not reset")
|
---|
423 |
|
---|
424 | # Test the handling of exceptions raised by each kind of trace event.
|
---|
425 | def test_call(self):
|
---|
426 | self.run_test_for_event('call')
|
---|
427 | def test_line(self):
|
---|
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')
|
---|
433 |
|
---|
434 | def test_trash_stack(self):
|
---|
435 | def f():
|
---|
436 | for i in range(5):
|
---|
437 | print i # line tracing will raise an exception at this line
|
---|
438 |
|
---|
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"
|
---|
443 | return g
|
---|
444 |
|
---|
445 | sys.settrace(g)
|
---|
446 | try:
|
---|
447 | f()
|
---|
448 | except RuntimeError:
|
---|
449 | # the test is really that this doesn't segfault:
|
---|
450 | import gc
|
---|
451 | gc.collect()
|
---|
452 | else:
|
---|
453 | self.fail("exception not propagated")
|
---|
454 |
|
---|
455 |
|
---|
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").
|
---|
459 |
|
---|
460 | class JumpTracer:
|
---|
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."""
|
---|
464 |
|
---|
465 | def __init__(self, function):
|
---|
466 | self.function = function
|
---|
467 | self.jumpFrom = function.jump[0]
|
---|
468 | self.jumpTo = function.jump[1]
|
---|
469 | self.done = False
|
---|
470 |
|
---|
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).
|
---|
477 | try:
|
---|
478 | frame.f_lineno = firstLine + self.jumpTo
|
---|
479 | except TypeError:
|
---|
480 | frame.f_lineno = self.jumpTo
|
---|
481 | self.done = True
|
---|
482 | return self.trace
|
---|
483 |
|
---|
484 | # The first set of 'jump' tests are for things that are allowed:
|
---|
485 |
|
---|
486 | def jump_simple_forwards(output):
|
---|
487 | output.append(1)
|
---|
488 | output.append(2)
|
---|
489 | output.append(3)
|
---|
490 |
|
---|
491 | jump_simple_forwards.jump = (1, 3)
|
---|
492 | jump_simple_forwards.output = [3]
|
---|
493 |
|
---|
494 | def jump_simple_backwards(output):
|
---|
495 | output.append(1)
|
---|
496 | output.append(2)
|
---|
497 |
|
---|
498 | jump_simple_backwards.jump = (2, 1)
|
---|
499 | jump_simple_backwards.output = [1, 1, 2]
|
---|
500 |
|
---|
501 | def jump_out_of_block_forwards(output):
|
---|
502 | for i in 1, 2:
|
---|
503 | output.append(2)
|
---|
504 | for j in [3]: # Also tests jumping over a block
|
---|
505 | output.append(4)
|
---|
506 | output.append(5)
|
---|
507 |
|
---|
508 | jump_out_of_block_forwards.jump = (3, 5)
|
---|
509 | jump_out_of_block_forwards.output = [2, 5]
|
---|
510 |
|
---|
511 | def jump_out_of_block_backwards(output):
|
---|
512 | output.append(1)
|
---|
513 | for i in [1]:
|
---|
514 | output.append(3)
|
---|
515 | for j in [2]: # Also tests jumping over a block
|
---|
516 | output.append(5)
|
---|
517 | output.append(6)
|
---|
518 | output.append(7)
|
---|
519 |
|
---|
520 | jump_out_of_block_backwards.jump = (6, 1)
|
---|
521 | jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7]
|
---|
522 |
|
---|
523 | def jump_to_codeless_line(output):
|
---|
524 | output.append(1)
|
---|
525 | # Jumping to this line should skip to the next one.
|
---|
526 | output.append(3)
|
---|
527 |
|
---|
528 | jump_to_codeless_line.jump = (1, 2)
|
---|
529 | jump_to_codeless_line.output = [3]
|
---|
530 |
|
---|
531 | def jump_to_same_line(output):
|
---|
532 | output.append(1)
|
---|
533 | output.append(2)
|
---|
534 | output.append(3)
|
---|
535 |
|
---|
536 | jump_to_same_line.jump = (2, 2)
|
---|
537 | jump_to_same_line.output = [1, 2, 3]
|
---|
538 |
|
---|
539 | # Tests jumping within a finally block, and over one.
|
---|
540 | def jump_in_nested_finally(output):
|
---|
541 | try:
|
---|
542 | output.append(2)
|
---|
543 | finally:
|
---|
544 | output.append(4)
|
---|
545 | try:
|
---|
546 | output.append(6)
|
---|
547 | finally:
|
---|
548 | output.append(8)
|
---|
549 | output.append(9)
|
---|
550 |
|
---|
551 | jump_in_nested_finally.jump = (4, 9)
|
---|
552 | jump_in_nested_finally.output = [2, 9]
|
---|
553 |
|
---|
554 | # The second set of 'jump' tests are for things that are not allowed:
|
---|
555 |
|
---|
556 | def no_jump_too_far_forwards(output):
|
---|
557 | try:
|
---|
558 | output.append(2)
|
---|
559 | output.append(3)
|
---|
560 | except ValueError, e:
|
---|
561 | output.append('after' in str(e))
|
---|
562 |
|
---|
563 | no_jump_too_far_forwards.jump = (3, 6)
|
---|
564 | no_jump_too_far_forwards.output = [2, True]
|
---|
565 |
|
---|
566 | def no_jump_too_far_backwards(output):
|
---|
567 | try:
|
---|
568 | output.append(2)
|
---|
569 | output.append(3)
|
---|
570 | except ValueError, e:
|
---|
571 | output.append('before' in str(e))
|
---|
572 |
|
---|
573 | no_jump_too_far_backwards.jump = (3, -1)
|
---|
574 | no_jump_too_far_backwards.output = [2, True]
|
---|
575 |
|
---|
576 | # Test each kind of 'except' line.
|
---|
577 | def no_jump_to_except_1(output):
|
---|
578 | try:
|
---|
579 | output.append(2)
|
---|
580 | except:
|
---|
581 | e = sys.exc_info()[1]
|
---|
582 | output.append('except' in str(e))
|
---|
583 |
|
---|
584 | no_jump_to_except_1.jump = (2, 3)
|
---|
585 | no_jump_to_except_1.output = [True]
|
---|
586 |
|
---|
587 | def no_jump_to_except_2(output):
|
---|
588 | try:
|
---|
589 | output.append(2)
|
---|
590 | except ValueError:
|
---|
591 | e = sys.exc_info()[1]
|
---|
592 | output.append('except' in str(e))
|
---|
593 |
|
---|
594 | no_jump_to_except_2.jump = (2, 3)
|
---|
595 | no_jump_to_except_2.output = [True]
|
---|
596 |
|
---|
597 | def no_jump_to_except_3(output):
|
---|
598 | try:
|
---|
599 | output.append(2)
|
---|
600 | except ValueError, e:
|
---|
601 | output.append('except' in str(e))
|
---|
602 |
|
---|
603 | no_jump_to_except_3.jump = (2, 3)
|
---|
604 | no_jump_to_except_3.output = [True]
|
---|
605 |
|
---|
606 | def no_jump_to_except_4(output):
|
---|
607 | try:
|
---|
608 | output.append(2)
|
---|
609 | except (ValueError, RuntimeError), e:
|
---|
610 | output.append('except' in str(e))
|
---|
611 |
|
---|
612 | no_jump_to_except_4.jump = (2, 3)
|
---|
613 | no_jump_to_except_4.output = [True]
|
---|
614 |
|
---|
615 | def no_jump_forwards_into_block(output):
|
---|
616 | try:
|
---|
617 | output.append(2)
|
---|
618 | for i in 1, 2:
|
---|
619 | output.append(4)
|
---|
620 | except ValueError, e:
|
---|
621 | output.append('into' in str(e))
|
---|
622 |
|
---|
623 | no_jump_forwards_into_block.jump = (2, 4)
|
---|
624 | no_jump_forwards_into_block.output = [True]
|
---|
625 |
|
---|
626 | def no_jump_backwards_into_block(output):
|
---|
627 | try:
|
---|
628 | for i in 1, 2:
|
---|
629 | output.append(3)
|
---|
630 | output.append(4)
|
---|
631 | except ValueError, e:
|
---|
632 | output.append('into' in str(e))
|
---|
633 |
|
---|
634 | no_jump_backwards_into_block.jump = (4, 3)
|
---|
635 | no_jump_backwards_into_block.output = [3, 3, True]
|
---|
636 |
|
---|
637 | def no_jump_into_finally_block(output):
|
---|
638 | try:
|
---|
639 | try:
|
---|
640 | output.append(3)
|
---|
641 | x = 1
|
---|
642 | finally:
|
---|
643 | output.append(6)
|
---|
644 | except ValueError, e:
|
---|
645 | output.append('finally' in str(e))
|
---|
646 |
|
---|
647 | no_jump_into_finally_block.jump = (4, 6)
|
---|
648 | no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs
|
---|
649 |
|
---|
650 | def no_jump_out_of_finally_block(output):
|
---|
651 | try:
|
---|
652 | try:
|
---|
653 | output.append(3)
|
---|
654 | finally:
|
---|
655 | output.append(5)
|
---|
656 | output.append(6)
|
---|
657 | except ValueError, e:
|
---|
658 | output.append('finally' in str(e))
|
---|
659 |
|
---|
660 | no_jump_out_of_finally_block.jump = (5, 1)
|
---|
661 | no_jump_out_of_finally_block.output = [3, True]
|
---|
662 |
|
---|
663 | # This verifies the line-numbers-must-be-integers rule.
|
---|
664 | def no_jump_to_non_integers(output):
|
---|
665 | try:
|
---|
666 | output.append(2)
|
---|
667 | except ValueError, e:
|
---|
668 | output.append('integer' in str(e))
|
---|
669 |
|
---|
670 | no_jump_to_non_integers.jump = (2, "Spam")
|
---|
671 | no_jump_to_non_integers.output = [True]
|
---|
672 |
|
---|
673 | def jump_across_with(output):
|
---|
674 | with open(test_support.TESTFN, "wb") as fp:
|
---|
675 | pass
|
---|
676 | with open(test_support.TESTFN, "wb") as fp:
|
---|
677 | pass
|
---|
678 | jump_across_with.jump = (1, 3)
|
---|
679 | jump_across_with.output = []
|
---|
680 |
|
---|
681 | # This verifies that you can't set f_lineno via _getframe or similar
|
---|
682 | # trickery.
|
---|
683 | def no_jump_without_trace_function():
|
---|
684 | try:
|
---|
685 | previous_frame = sys._getframe().f_back
|
---|
686 | previous_frame.f_lineno = previous_frame.f_lineno
|
---|
687 | except ValueError, e:
|
---|
688 | # This is the exception we wanted; make sure the error message
|
---|
689 | # talks about trace functions.
|
---|
690 | if 'trace' not in str(e):
|
---|
691 | raise
|
---|
692 | else:
|
---|
693 | # Something's wrong - the expected exception wasn't raised.
|
---|
694 | raise RuntimeError, "Trace-function-less jump failed to fail"
|
---|
695 |
|
---|
696 |
|
---|
697 | class JumpTestCase(unittest.TestCase):
|
---|
698 | def compare_jump_output(self, expected, received):
|
---|
699 | if received != expected:
|
---|
700 | self.fail( "Outputs don't match:\n" +
|
---|
701 | "Expected: " + repr(expected) + "\n" +
|
---|
702 | "Received: " + repr(received))
|
---|
703 |
|
---|
704 | def run_test(self, func):
|
---|
705 | tracer = JumpTracer(func)
|
---|
706 | sys.settrace(tracer.trace)
|
---|
707 | output = []
|
---|
708 | func(output)
|
---|
709 | sys.settrace(None)
|
---|
710 | self.compare_jump_output(func.output, output)
|
---|
711 |
|
---|
712 | def test_01_jump_simple_forwards(self):
|
---|
713 | self.run_test(jump_simple_forwards)
|
---|
714 | def test_02_jump_simple_backwards(self):
|
---|
715 | self.run_test(jump_simple_backwards)
|
---|
716 | def test_03_jump_out_of_block_forwards(self):
|
---|
717 | self.run_test(jump_out_of_block_forwards)
|
---|
718 | def test_04_jump_out_of_block_backwards(self):
|
---|
719 | self.run_test(jump_out_of_block_backwards)
|
---|
720 | def test_05_jump_to_codeless_line(self):
|
---|
721 | self.run_test(jump_to_codeless_line)
|
---|
722 | def test_06_jump_to_same_line(self):
|
---|
723 | self.run_test(jump_to_same_line)
|
---|
724 | def test_07_jump_in_nested_finally(self):
|
---|
725 | self.run_test(jump_in_nested_finally)
|
---|
726 | def test_08_no_jump_too_far_forwards(self):
|
---|
727 | self.run_test(no_jump_too_far_forwards)
|
---|
728 | def test_09_no_jump_too_far_backwards(self):
|
---|
729 | self.run_test(no_jump_too_far_backwards)
|
---|
730 | def test_10_no_jump_to_except_1(self):
|
---|
731 | self.run_test(no_jump_to_except_1)
|
---|
732 | def test_11_no_jump_to_except_2(self):
|
---|
733 | self.run_test(no_jump_to_except_2)
|
---|
734 | def test_12_no_jump_to_except_3(self):
|
---|
735 | self.run_test(no_jump_to_except_3)
|
---|
736 | def test_13_no_jump_to_except_4(self):
|
---|
737 | self.run_test(no_jump_to_except_4)
|
---|
738 | def test_14_no_jump_forwards_into_block(self):
|
---|
739 | self.run_test(no_jump_forwards_into_block)
|
---|
740 | def test_15_no_jump_backwards_into_block(self):
|
---|
741 | self.run_test(no_jump_backwards_into_block)
|
---|
742 | def test_16_no_jump_into_finally_block(self):
|
---|
743 | self.run_test(no_jump_into_finally_block)
|
---|
744 | def test_17_no_jump_out_of_finally_block(self):
|
---|
745 | self.run_test(no_jump_out_of_finally_block)
|
---|
746 | def test_18_no_jump_to_non_integers(self):
|
---|
747 | self.run_test(no_jump_to_non_integers)
|
---|
748 | def test_19_no_jump_without_trace_function(self):
|
---|
749 | no_jump_without_trace_function()
|
---|
750 | def test_jump_across_with(self):
|
---|
751 | self.addCleanup(test_support.unlink, test_support.TESTFN)
|
---|
752 | self.run_test(jump_across_with)
|
---|
753 |
|
---|
754 | def test_20_large_function(self):
|
---|
755 | d = {}
|
---|
756 | exec("""def f(output): # line 0
|
---|
757 | x = 0 # line 1
|
---|
758 | y = 1 # line 2
|
---|
759 | ''' # line 3
|
---|
760 | %s # lines 4-1004
|
---|
761 | ''' # line 1005
|
---|
762 | x += 1 # line 1006
|
---|
763 | output.append(x) # line 1007
|
---|
764 | return""" % ('\n' * 1000,), d)
|
---|
765 | f = d['f']
|
---|
766 |
|
---|
767 | f.jump = (2, 1007)
|
---|
768 | f.output = [0]
|
---|
769 | self.run_test(f)
|
---|
770 |
|
---|
771 | def test_jump_to_firstlineno(self):
|
---|
772 | # This tests that PDB can jump back to the first line in a
|
---|
773 | # file. See issue #1689458. It can only be triggered in a
|
---|
774 | # function call if the function is defined on a single line.
|
---|
775 | code = compile("""
|
---|
776 | # Comments don't count.
|
---|
777 | output.append(2) # firstlineno is here.
|
---|
778 | output.append(3)
|
---|
779 | output.append(4)
|
---|
780 | """, "<fake module>", "exec")
|
---|
781 | class fake_function:
|
---|
782 | func_code = code
|
---|
783 | jump = (2, 0)
|
---|
784 | tracer = JumpTracer(fake_function)
|
---|
785 | sys.settrace(tracer.trace)
|
---|
786 | namespace = {"output": []}
|
---|
787 | exec code in namespace
|
---|
788 | sys.settrace(None)
|
---|
789 | self.compare_jump_output([2, 3, 2, 3, 4], namespace["output"])
|
---|
790 |
|
---|
791 |
|
---|
792 | def test_main():
|
---|
793 | test_support.run_unittest(
|
---|
794 | TraceTestCase,
|
---|
795 | RaisingTraceFuncTestCase,
|
---|
796 | JumpTestCase
|
---|
797 | )
|
---|
798 |
|
---|
799 | if __name__ == "__main__":
|
---|
800 | test_main()
|
---|