blob: 9816e3ed6466b411cce23fc9f9056cbf34643e73 [file] [log] [blame]
Benjamin Petersoncb170942010-10-17 01:29:11 +00001import gc
Fred Drake3208d4b2001-09-22 04:28:19 +00002import pprint
3import sys
4import unittest
5
Benjamin Petersonee8712c2008-05-20 21:35:26 +00006from test import support
Fred Drake3208d4b2001-09-22 04:28:19 +00007
Christian Heimes9bd667a2008-01-20 15:14:11 +00008class TestGetProfile(unittest.TestCase):
9 def setUp(self):
10 sys.setprofile(None)
11
12 def tearDown(self):
13 sys.setprofile(None)
14
15 def test_empty(self):
Benjamin Petersonca9f1282010-10-17 01:30:26 +000016 self.assertIsNone(sys.getprofile())
Christian Heimes9bd667a2008-01-20 15:14:11 +000017
18 def test_setget(self):
19 def fn(*args):
20 pass
21
22 sys.setprofile(fn)
Benjamin Petersonca9f1282010-10-17 01:30:26 +000023 self.assertIs(sys.getprofile(), fn)
Fred Drake3208d4b2001-09-22 04:28:19 +000024
25class HookWatcher:
26 def __init__(self):
27 self.frames = []
28 self.events = []
29
30 def callback(self, frame, event, arg):
Nicholas Bastinc69ebe82004-03-24 21:57:10 +000031 if (event == "call"
32 or event == "return"
33 or event == "exception"):
34 self.add_event(event, frame)
Fred Drake3208d4b2001-09-22 04:28:19 +000035
36 def add_event(self, event, frame=None):
37 """Add an event to the log."""
38 if frame is None:
39 frame = sys._getframe(1)
40
41 try:
42 frameno = self.frames.index(frame)
43 except ValueError:
44 frameno = len(self.frames)
45 self.frames.append(frame)
46
47 self.events.append((frameno, event, ident(frame)))
48
49 def get_events(self):
50 """Remove calls to add_event()."""
Christian Heimesff737952007-11-27 10:40:20 +000051 disallowed = [ident(self.add_event.__func__), ident(ident)]
Fred Drake2d879012001-09-24 18:44:11 +000052 self.frames = None
Fred Drake3208d4b2001-09-22 04:28:19 +000053
Fred Drake2d879012001-09-24 18:44:11 +000054 return [item for item in self.events if item[2] not in disallowed]
Fred Drake3208d4b2001-09-22 04:28:19 +000055
56
Fred Drake39cd6032001-09-26 21:00:33 +000057class ProfileSimulator(HookWatcher):
Fred Drake7c0a93d2001-10-04 14:49:46 +000058 def __init__(self, testcase):
59 self.testcase = testcase
Fred Drake39cd6032001-09-26 21:00:33 +000060 self.stack = []
61 HookWatcher.__init__(self)
Fred Drake3208d4b2001-09-22 04:28:19 +000062
Fred Drake39cd6032001-09-26 21:00:33 +000063 def callback(self, frame, event, arg):
Fred Drake0099ba72001-10-03 21:15:32 +000064 # Callback registered with sys.setprofile()/sys.settrace()
Fred Drake39cd6032001-09-26 21:00:33 +000065 self.dispatch[event](self, frame)
66
67 def trace_call(self, frame):
68 self.add_event('call', frame)
69 self.stack.append(frame)
70
71 def trace_return(self, frame):
72 self.add_event('return', frame)
73 self.stack.pop()
74
75 def trace_exception(self, frame):
Fred Drake7c0a93d2001-10-04 14:49:46 +000076 self.testcase.fail(
77 "the profiler should never receive exception events")
Fred Drake39cd6032001-09-26 21:00:33 +000078
Nicholas Bastinc69ebe82004-03-24 21:57:10 +000079 def trace_pass(self, frame):
80 pass
81
Fred Drake39cd6032001-09-26 21:00:33 +000082 dispatch = {
83 'call': trace_call,
84 'exception': trace_exception,
85 'return': trace_return,
Nicholas Bastinc69ebe82004-03-24 21:57:10 +000086 'c_call': trace_pass,
87 'c_return': trace_pass,
88 'c_exception': trace_pass,
Fred Drake39cd6032001-09-26 21:00:33 +000089 }
90
91
92class TestCaseBase(unittest.TestCase):
Fred Drake3208d4b2001-09-22 04:28:19 +000093 def check_events(self, callable, expected):
Fred Drake39cd6032001-09-26 21:00:33 +000094 events = capture_events(callable, self.new_watcher())
Fred Drake3208d4b2001-09-22 04:28:19 +000095 if events != expected:
96 self.fail("Expected events:\n%s\nReceived events:\n%s"
97 % (pprint.pformat(expected), pprint.pformat(events)))
98
Fred Drake39cd6032001-09-26 21:00:33 +000099
100class ProfileHookTestCase(TestCaseBase):
101 def new_watcher(self):
102 return HookWatcher()
103
Fred Drake3208d4b2001-09-22 04:28:19 +0000104 def test_simple(self):
105 def f(p):
106 pass
107 f_ident = ident(f)
Fred Drakecc91ac02001-09-25 20:48:14 +0000108 self.check_events(f, [(1, 'call', f_ident),
109 (1, 'return', f_ident),
Fred Drake3208d4b2001-09-22 04:28:19 +0000110 ])
111
112 def test_exception(self):
113 def f(p):
Fred Drakecc91ac02001-09-25 20:48:14 +0000114 1/0
115 f_ident = ident(f)
116 self.check_events(f, [(1, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000117 (1, 'return', f_ident),
Fred Drakecc91ac02001-09-25 20:48:14 +0000118 ])
119
120 def test_caught_exception(self):
121 def f(p):
Fred Drake2d879012001-09-24 18:44:11 +0000122 try: 1/0
123 except: pass
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 Drakecc91ac02001-09-25 20:48:14 +0000126 (1, 'return', f_ident),
Fred Drake3208d4b2001-09-22 04:28:19 +0000127 ])
128
Fred Drake2d879012001-09-24 18:44:11 +0000129 def test_caught_nested_exception(self):
130 def f(p):
131 try: 1/0
132 except: pass
Fred Drake2d879012001-09-24 18:44:11 +0000133 f_ident = ident(f)
Fred Drakecc91ac02001-09-25 20:48:14 +0000134 self.check_events(f, [(1, 'call', f_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000135 (1, 'return', f_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000136 ])
137
Fred Drake3208d4b2001-09-22 04:28:19 +0000138 def test_nested_exception(self):
139 def f(p):
140 1/0
Fred Drake3208d4b2001-09-22 04:28:19 +0000141 f_ident = ident(f)
Fred Drakecc91ac02001-09-25 20:48:14 +0000142 self.check_events(f, [(1, 'call', f_ident),
Fred Drake3208d4b2001-09-22 04:28:19 +0000143 # This isn't what I expected:
Fred Drake7c0a93d2001-10-04 14:49:46 +0000144 # (0, 'exception', protect_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000145 # I expected this again:
Fred Drake7c0a93d2001-10-04 14:49:46 +0000146 (1, 'return', f_ident),
Fred Drake3208d4b2001-09-22 04:28:19 +0000147 ])
148
Fred Drake2d879012001-09-24 18:44:11 +0000149 def test_exception_in_except_clause(self):
150 def f(p):
151 1/0
152 def g(p):
153 try:
154 f(p)
155 except:
156 try: f(p)
157 except: pass
158 f_ident = ident(f)
159 g_ident = ident(g)
Fred Drakecc91ac02001-09-25 20:48:14 +0000160 self.check_events(g, [(1, 'call', g_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000161 (2, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000162 (2, 'return', f_ident),
Fred Drakecc91ac02001-09-25 20:48:14 +0000163 (3, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000164 (3, 'return', f_ident),
Fred Drakecc91ac02001-09-25 20:48:14 +0000165 (1, 'return', g_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000166 ])
167
168 def test_exception_propogation(self):
169 def f(p):
170 1/0
171 def g(p):
172 try: f(p)
173 finally: p.add_event("falling through")
Fred Drake2d879012001-09-24 18:44:11 +0000174 f_ident = ident(f)
175 g_ident = ident(g)
Fred Drakecc91ac02001-09-25 20:48:14 +0000176 self.check_events(g, [(1, 'call', g_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000177 (2, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000178 (2, 'return', f_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000179 (1, 'falling through', g_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000180 (1, 'return', g_ident),
Fred Drake2d879012001-09-24 18:44:11 +0000181 ])
Fred Drake3208d4b2001-09-22 04:28:19 +0000182
Fred Drakecc91ac02001-09-25 20:48:14 +0000183 def test_raise_twice(self):
184 def f(p):
185 try: 1/0
186 except: 1/0
187 f_ident = ident(f)
188 self.check_events(f, [(1, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000189 (1, 'return', f_ident),
Fred Drakecc91ac02001-09-25 20:48:14 +0000190 ])
191
192 def test_raise_reraise(self):
193 def f(p):
194 try: 1/0
195 except: raise
196 f_ident = ident(f)
197 self.check_events(f, [(1, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000198 (1, 'return', f_ident),
Fred Drakecc91ac02001-09-25 20:48:14 +0000199 ])
200
201 def test_raise(self):
202 def f(p):
203 raise Exception()
204 f_ident = ident(f)
205 self.check_events(f, [(1, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000206 (1, 'return', f_ident),
Fred Drakecc91ac02001-09-25 20:48:14 +0000207 ])
208
Fred Drake0099ba72001-10-03 21:15:32 +0000209 def test_distant_exception(self):
210 def f():
211 1/0
212 def g():
213 f()
214 def h():
215 g()
216 def i():
217 h()
218 def j(p):
219 i()
220 f_ident = ident(f)
221 g_ident = ident(g)
222 h_ident = ident(h)
223 i_ident = ident(i)
224 j_ident = ident(j)
225 self.check_events(j, [(1, 'call', j_ident),
226 (2, 'call', i_ident),
227 (3, 'call', h_ident),
228 (4, 'call', g_ident),
229 (5, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000230 (5, 'return', f_ident),
231 (4, 'return', g_ident),
232 (3, 'return', h_ident),
233 (2, 'return', i_ident),
234 (1, 'return', j_ident),
Fred Drake0099ba72001-10-03 21:15:32 +0000235 ])
236
237 def test_generator(self):
238 def f():
239 for i in range(2):
240 yield i
241 def g(p):
242 for i in f():
243 pass
244 f_ident = ident(f)
245 g_ident = ident(g)
246 self.check_events(g, [(1, 'call', g_ident),
247 # call the iterator twice to generate values
248 (2, 'call', f_ident),
249 (2, 'return', f_ident),
250 (2, 'call', f_ident),
251 (2, 'return', f_ident),
252 # once more; returns end-of-iteration with
253 # actually raising an exception
254 (2, 'call', f_ident),
255 (2, 'return', f_ident),
256 (1, 'return', g_ident),
257 ])
258
259 def test_stop_iteration(self):
260 def f():
261 for i in range(2):
262 yield i
263 raise StopIteration
264 def g(p):
265 for i in f():
266 pass
267 f_ident = ident(f)
268 g_ident = ident(g)
269 self.check_events(g, [(1, 'call', g_ident),
270 # call the iterator twice to generate values
271 (2, 'call', f_ident),
272 (2, 'return', f_ident),
273 (2, 'call', f_ident),
274 (2, 'return', f_ident),
275 # once more to hit the raise:
276 (2, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000277 (2, 'return', f_ident),
Fred Drake0099ba72001-10-03 21:15:32 +0000278 (1, 'return', g_ident),
279 ])
280
Fred Drakecc91ac02001-09-25 20:48:14 +0000281
Fred Drake39cd6032001-09-26 21:00:33 +0000282class ProfileSimulatorTestCase(TestCaseBase):
283 def new_watcher(self):
Fred Drake7c0a93d2001-10-04 14:49:46 +0000284 return ProfileSimulator(self)
Fred Drake39cd6032001-09-26 21:00:33 +0000285
286 def test_simple(self):
287 def f(p):
288 pass
289 f_ident = ident(f)
290 self.check_events(f, [(1, 'call', f_ident),
291 (1, 'return', f_ident),
292 ])
293
294 def test_basic_exception(self):
295 def f(p):
296 1/0
297 f_ident = ident(f)
298 self.check_events(f, [(1, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000299 (1, 'return', f_ident),
Fred Drake39cd6032001-09-26 21:00:33 +0000300 ])
301
Fred Drake0099ba72001-10-03 21:15:32 +0000302 def test_caught_exception(self):
303 def f(p):
304 try: 1/0
305 except: pass
306 f_ident = ident(f)
307 self.check_events(f, [(1, 'call', f_ident),
Fred Drake0099ba72001-10-03 21:15:32 +0000308 (1, 'return', f_ident),
309 ])
310
311 def test_distant_exception(self):
312 def f():
313 1/0
314 def g():
315 f()
316 def h():
317 g()
318 def i():
319 h()
320 def j(p):
321 i()
322 f_ident = ident(f)
323 g_ident = ident(g)
324 h_ident = ident(h)
325 i_ident = ident(i)
326 j_ident = ident(j)
327 self.check_events(j, [(1, 'call', j_ident),
328 (2, 'call', i_ident),
329 (3, 'call', h_ident),
330 (4, 'call', g_ident),
331 (5, 'call', f_ident),
Fred Drake7c0a93d2001-10-04 14:49:46 +0000332 (5, 'return', f_ident),
333 (4, 'return', g_ident),
334 (3, 'return', h_ident),
335 (2, 'return', i_ident),
336 (1, 'return', j_ident),
Fred Drake0099ba72001-10-03 21:15:32 +0000337 ])
338
Fred Drake39cd6032001-09-26 21:00:33 +0000339
Fred Drake3208d4b2001-09-22 04:28:19 +0000340def ident(function):
341 if hasattr(function, "f_code"):
342 code = function.f_code
343 else:
Neal Norwitz221085d2007-02-25 20:55:47 +0000344 code = function.__code__
Fred Drake3208d4b2001-09-22 04:28:19 +0000345 return code.co_firstlineno, code.co_name
346
347
Fred Drakecc91ac02001-09-25 20:48:14 +0000348def protect(f, p):
349 try: f(p)
350 except: pass
351
352protect_ident = ident(protect)
353
354
Fred Drake39cd6032001-09-26 21:00:33 +0000355def capture_events(callable, p=None):
356 if p is None:
357 p = HookWatcher()
Benjamin Petersoncb170942010-10-17 01:29:11 +0000358 # Disable the garbage collector. This prevents __del__s from showing up in
359 # traces.
360 old_gc = gc.isenabled()
361 gc.disable()
362 try:
363 sys.setprofile(p.callback)
364 protect(callable, p)
365 sys.setprofile(None)
366 finally:
367 if old_gc:
368 gc.enable()
Fred Drakecc91ac02001-09-25 20:48:14 +0000369 return p.get_events()[1:-1]
Fred Drake3208d4b2001-09-22 04:28:19 +0000370
371
372def show_events(callable):
373 import pprint
374 pprint.pprint(capture_events(callable))
375
376
377def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000378 support.run_unittest(
Christian Heimes9bd667a2008-01-20 15:14:11 +0000379 TestGetProfile,
Walter Dörwald21d3a322003-05-01 17:45:56 +0000380 ProfileHookTestCase,
381 ProfileSimulatorTestCase
382 )
Fred Drake3208d4b2001-09-22 04:28:19 +0000383
384
385if __name__ == "__main__":
386 test_main()