blob: 33c3f534d07161264c06fcbb77e7db177301f957 [file] [log] [blame]
Fred Drake0099ba72001-10-03 21:15:32 +00001from __future__ import generators
2
Fred Drake3208d4b2001-09-22 04:28:19 +00003import pprint
4import sys
5import unittest
6
7import test_support
8
9
10class HookWatcher:
11 def __init__(self):
12 self.frames = []
13 self.events = []
14
15 def callback(self, frame, event, arg):
16 self.add_event(event, frame)
17
18 def add_event(self, event, frame=None):
19 """Add an event to the log."""
20 if frame is None:
21 frame = sys._getframe(1)
22
23 try:
24 frameno = self.frames.index(frame)
25 except ValueError:
26 frameno = len(self.frames)
27 self.frames.append(frame)
28
29 self.events.append((frameno, event, ident(frame)))
30
31 def get_events(self):
32 """Remove calls to add_event()."""
Fred Drake2d879012001-09-24 18:44:11 +000033 disallowed = [ident(self.add_event.im_func), ident(ident)]
34 self.frames = None
Fred Drake3208d4b2001-09-22 04:28:19 +000035
Fred Drake2d879012001-09-24 18:44:11 +000036 return [item for item in self.events if item[2] not in disallowed]
Fred Drake3208d4b2001-09-22 04:28:19 +000037
38
Fred Drake39cd6032001-09-26 21:00:33 +000039class ProfileSimulator(HookWatcher):
40 def __init__(self):
41 self.stack = []
42 HookWatcher.__init__(self)
Fred Drake3208d4b2001-09-22 04:28:19 +000043
Fred Drake39cd6032001-09-26 21:00:33 +000044 def callback(self, frame, event, arg):
Fred Drake0099ba72001-10-03 21:15:32 +000045 # Callback registered with sys.setprofile()/sys.settrace()
Fred Drake39cd6032001-09-26 21:00:33 +000046 self.dispatch[event](self, frame)
47
48 def trace_call(self, frame):
49 self.add_event('call', frame)
50 self.stack.append(frame)
51
52 def trace_return(self, frame):
53 self.add_event('return', frame)
54 self.stack.pop()
55
56 def trace_exception(self, frame):
57 if len(self.stack) >= 2 and frame is self.stack[-2]:
58 self.add_event('propogate-from', self.stack[-1])
59 self.stack.pop()
60 else:
Fred Drake0099ba72001-10-03 21:15:32 +000061 # Either an exception was raised in Python or a C function
62 # raised an exception; this does not represent propogation.
Fred Drake39cd6032001-09-26 21:00:33 +000063 self.add_event('ignore', frame)
64
65 dispatch = {
66 'call': trace_call,
67 'exception': trace_exception,
68 'return': trace_return,
69 }
70
71
72class TestCaseBase(unittest.TestCase):
Fred Drake3208d4b2001-09-22 04:28:19 +000073 def check_events(self, callable, expected):
Fred Drake39cd6032001-09-26 21:00:33 +000074 events = capture_events(callable, self.new_watcher())
Fred Drake3208d4b2001-09-22 04:28:19 +000075 if events != expected:
76 self.fail("Expected events:\n%s\nReceived events:\n%s"
77 % (pprint.pformat(expected), pprint.pformat(events)))
78
Fred Drake39cd6032001-09-26 21:00:33 +000079
80class ProfileHookTestCase(TestCaseBase):
81 def new_watcher(self):
82 return HookWatcher()
83
Fred Drake3208d4b2001-09-22 04:28:19 +000084 def test_simple(self):
85 def f(p):
86 pass
87 f_ident = ident(f)
Fred Drakecc91ac02001-09-25 20:48:14 +000088 self.check_events(f, [(1, 'call', f_ident),
89 (1, 'return', f_ident),
Fred Drake3208d4b2001-09-22 04:28:19 +000090 ])
91
92 def test_exception(self):
93 def f(p):
Fred Drakecc91ac02001-09-25 20:48:14 +000094 1/0
95 f_ident = ident(f)
96 self.check_events(f, [(1, 'call', f_ident),
97 (1, 'exception', f_ident),
98 (0, 'exception', protect_ident),
99 ])
100
101 def test_caught_exception(self):
102 def f(p):
Fred Drake2d879012001-09-24 18:44:11 +0000103 try: 1/0
104 except: pass
Fred Drake3208d4b2001-09-22 04:28:19 +0000105 f_ident = ident(f)
Fred Drakecc91ac02001-09-25 20:48:14 +0000106 self.check_events(f, [(1, 'call', f_ident),
107 (1, 'exception', f_ident),
108 (1, 'return', f_ident),
Fred Drake3208d4b2001-09-22 04:28:19 +0000109 ])
110
Fred Drake2d879012001-09-24 18:44:11 +0000111 def test_caught_nested_exception(self):
112 def f(p):
113 try: 1/0
114 except: pass
Fred Drake2d879012001-09-24 18:44:11 +0000115 f_ident = ident(f)
Fred Drakecc91ac02001-09-25 20:48:14 +0000116 self.check_events(f, [(1, 'call', f_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000117 (1, 'exception', f_ident),
118 (1, 'return', f_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000119 ])
120
Fred Drake3208d4b2001-09-22 04:28:19 +0000121 def test_nested_exception(self):
122 def f(p):
123 1/0
Fred Drake3208d4b2001-09-22 04:28:19 +0000124 f_ident = ident(f)
Fred Drakecc91ac02001-09-25 20:48:14 +0000125 self.check_events(f, [(1, 'call', f_ident),
Fred Drake3208d4b2001-09-22 04:28:19 +0000126 (1, 'exception', f_ident),
127 # This isn't what I expected:
Fred Drakecc91ac02001-09-25 20:48:14 +0000128 (0, 'exception', protect_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000129 # I expected this again:
130 # (1, 'exception', f_ident),
Fred Drake3208d4b2001-09-22 04:28:19 +0000131 ])
132
Fred Drake2d879012001-09-24 18:44:11 +0000133 def test_exception_in_except_clause(self):
134 def f(p):
135 1/0
136 def g(p):
137 try:
138 f(p)
139 except:
140 try: f(p)
141 except: pass
142 f_ident = ident(f)
143 g_ident = ident(g)
Fred Drakecc91ac02001-09-25 20:48:14 +0000144 self.check_events(g, [(1, 'call', g_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000145 (2, 'call', f_ident),
146 (2, 'exception', f_ident),
Fred Drakecc91ac02001-09-25 20:48:14 +0000147 (1, 'exception', g_ident),
148 (3, 'call', f_ident),
149 (3, 'exception', f_ident),
150 (1, 'exception', g_ident),
151 (1, 'return', g_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000152 ])
153
154 def test_exception_propogation(self):
155 def f(p):
156 1/0
157 def g(p):
158 try: f(p)
159 finally: p.add_event("falling through")
Fred Drake2d879012001-09-24 18:44:11 +0000160 f_ident = ident(f)
161 g_ident = ident(g)
Fred Drakecc91ac02001-09-25 20:48:14 +0000162 self.check_events(g, [(1, 'call', g_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000163 (2, 'call', f_ident),
164 (2, 'exception', f_ident),
165 (1, 'exception', g_ident),
166 (1, 'falling through', g_ident),
Fred Drakecc91ac02001-09-25 20:48:14 +0000167 (0, 'exception', protect_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000168 ])
Fred Drake3208d4b2001-09-22 04:28:19 +0000169
Fred Drakecc91ac02001-09-25 20:48:14 +0000170 def test_raise_twice(self):
171 def f(p):
172 try: 1/0
173 except: 1/0
174 f_ident = ident(f)
175 self.check_events(f, [(1, 'call', f_ident),
176 (1, 'exception', f_ident),
177 (1, 'exception', f_ident),
178 (0, 'exception', protect_ident)
179 ])
180
181 def test_raise_reraise(self):
182 def f(p):
183 try: 1/0
184 except: raise
185 f_ident = ident(f)
186 self.check_events(f, [(1, 'call', f_ident),
187 (1, 'exception', f_ident),
188 (0, 'exception', protect_ident)
189 ])
190
191 def test_raise(self):
192 def f(p):
193 raise Exception()
194 f_ident = ident(f)
195 self.check_events(f, [(1, 'call', f_ident),
196 (1, 'exception', f_ident),
197 (0, 'exception', protect_ident)
198 ])
199
Fred Drake0099ba72001-10-03 21:15:32 +0000200 def test_distant_exception(self):
201 def f():
202 1/0
203 def g():
204 f()
205 def h():
206 g()
207 def i():
208 h()
209 def j(p):
210 i()
211 f_ident = ident(f)
212 g_ident = ident(g)
213 h_ident = ident(h)
214 i_ident = ident(i)
215 j_ident = ident(j)
216 self.check_events(j, [(1, 'call', j_ident),
217 (2, 'call', i_ident),
218 (3, 'call', h_ident),
219 (4, 'call', g_ident),
220 (5, 'call', f_ident),
221 (5, 'exception', f_ident),
222 (4, 'exception', g_ident),
223 (3, 'exception', h_ident),
224 (2, 'exception', i_ident),
225 (1, 'exception', j_ident),
226 (0, 'exception', protect_ident),
227 ])
228
229 def test_generator(self):
230 def f():
231 for i in range(2):
232 yield i
233 def g(p):
234 for i in f():
235 pass
236 f_ident = ident(f)
237 g_ident = ident(g)
238 self.check_events(g, [(1, 'call', g_ident),
239 # call the iterator twice to generate values
240 (2, 'call', f_ident),
241 (2, 'return', f_ident),
242 (2, 'call', f_ident),
243 (2, 'return', f_ident),
244 # once more; returns end-of-iteration with
245 # actually raising an exception
246 (2, 'call', f_ident),
247 (2, 'return', f_ident),
248 (1, 'return', g_ident),
249 ])
250
251 def test_stop_iteration(self):
252 def f():
253 for i in range(2):
254 yield i
255 raise StopIteration
256 def g(p):
257 for i in f():
258 pass
259 f_ident = ident(f)
260 g_ident = ident(g)
261 self.check_events(g, [(1, 'call', g_ident),
262 # call the iterator twice to generate values
263 (2, 'call', f_ident),
264 (2, 'return', f_ident),
265 (2, 'call', f_ident),
266 (2, 'return', f_ident),
267 # once more to hit the raise:
268 (2, 'call', f_ident),
269 (2, 'exception', f_ident),
270 (1, 'return', g_ident),
271 ])
272
Fred Drakecc91ac02001-09-25 20:48:14 +0000273
Fred Drake39cd6032001-09-26 21:00:33 +0000274class ProfileSimulatorTestCase(TestCaseBase):
275 def new_watcher(self):
276 return ProfileSimulator()
277
278 def test_simple(self):
279 def f(p):
280 pass
281 f_ident = ident(f)
282 self.check_events(f, [(1, 'call', f_ident),
283 (1, 'return', f_ident),
284 ])
285
286 def test_basic_exception(self):
287 def f(p):
288 1/0
289 f_ident = ident(f)
290 self.check_events(f, [(1, 'call', f_ident),
291 (1, 'ignore', f_ident),
292 (1, 'propogate-from', f_ident),
293 ])
294
Fred Drake0099ba72001-10-03 21:15:32 +0000295 def test_caught_exception(self):
296 def f(p):
297 try: 1/0
298 except: pass
299 f_ident = ident(f)
300 self.check_events(f, [(1, 'call', f_ident),
301 (1, 'ignore', f_ident),
302 (1, 'return', f_ident),
303 ])
304
305 def test_distant_exception(self):
306 def f():
307 1/0
308 def g():
309 f()
310 def h():
311 g()
312 def i():
313 h()
314 def j(p):
315 i()
316 f_ident = ident(f)
317 g_ident = ident(g)
318 h_ident = ident(h)
319 i_ident = ident(i)
320 j_ident = ident(j)
321 self.check_events(j, [(1, 'call', j_ident),
322 (2, 'call', i_ident),
323 (3, 'call', h_ident),
324 (4, 'call', g_ident),
325 (5, 'call', f_ident),
326 (5, 'ignore', f_ident),
327 (5, 'propogate-from', f_ident),
328 (4, 'propogate-from', g_ident),
329 (3, 'propogate-from', h_ident),
330 (2, 'propogate-from', i_ident),
331 (1, 'propogate-from', j_ident),
332 ])
333
Fred Drake39cd6032001-09-26 21:00:33 +0000334
Fred Drake3208d4b2001-09-22 04:28:19 +0000335def ident(function):
336 if hasattr(function, "f_code"):
337 code = function.f_code
338 else:
339 code = function.func_code
340 return code.co_firstlineno, code.co_name
341
342
Fred Drakecc91ac02001-09-25 20:48:14 +0000343def protect(f, p):
344 try: f(p)
345 except: pass
346
347protect_ident = ident(protect)
348
349
Fred Drake39cd6032001-09-26 21:00:33 +0000350def capture_events(callable, p=None):
351 if p is None:
352 p = HookWatcher()
Fred Drake3208d4b2001-09-22 04:28:19 +0000353 sys.setprofile(p.callback)
Fred Drakecc91ac02001-09-25 20:48:14 +0000354 protect(callable, p)
Fred Drake3208d4b2001-09-22 04:28:19 +0000355 sys.setprofile(None)
Fred Drakecc91ac02001-09-25 20:48:14 +0000356 return p.get_events()[1:-1]
Fred Drake3208d4b2001-09-22 04:28:19 +0000357
358
359def show_events(callable):
360 import pprint
361 pprint.pprint(capture_events(callable))
362
363
364def test_main():
Fred Drake39cd6032001-09-26 21:00:33 +0000365 loader = unittest.TestLoader()
366 suite = unittest.TestSuite()
367 suite.addTest(loader.loadTestsFromTestCase(ProfileHookTestCase))
368 suite.addTest(loader.loadTestsFromTestCase(ProfileSimulatorTestCase))
369 test_support.run_suite(suite)
Fred Drake3208d4b2001-09-22 04:28:19 +0000370
371
372if __name__ == "__main__":
373 test_main()