Maintain backwards compatibility with python < 2.3 by dynamically
[python/dscho.git] / Lib / test / test_profilehooks.py
blobac8ebd8a1898af41c9a6a3b2f6443348bd220e98
1 import pprint
2 import sys
3 import unittest
5 from test import test_support
8 class HookWatcher:
9 def __init__(self):
10 self.frames = []
11 self.events = []
13 def callback(self, frame, event, arg):
14 self.add_event(event, frame)
16 def add_event(self, event, frame=None):
17 """Add an event to the log."""
18 if frame is None:
19 frame = sys._getframe(1)
21 try:
22 frameno = self.frames.index(frame)
23 except ValueError:
24 frameno = len(self.frames)
25 self.frames.append(frame)
27 self.events.append((frameno, event, ident(frame)))
29 def get_events(self):
30 """Remove calls to add_event()."""
31 disallowed = [ident(self.add_event.im_func), ident(ident)]
32 self.frames = None
34 return [item for item in self.events if item[2] not in disallowed]
37 class ProfileSimulator(HookWatcher):
38 def __init__(self, testcase):
39 self.testcase = testcase
40 self.stack = []
41 HookWatcher.__init__(self)
43 def callback(self, frame, event, arg):
44 # Callback registered with sys.setprofile()/sys.settrace()
45 self.dispatch[event](self, frame)
47 def trace_call(self, frame):
48 self.add_event('call', frame)
49 self.stack.append(frame)
51 def trace_return(self, frame):
52 self.add_event('return', frame)
53 self.stack.pop()
55 def trace_exception(self, frame):
56 self.testcase.fail(
57 "the profiler should never receive exception events")
59 dispatch = {
60 'call': trace_call,
61 'exception': trace_exception,
62 'return': trace_return,
66 class TestCaseBase(unittest.TestCase):
67 def check_events(self, callable, expected):
68 events = capture_events(callable, self.new_watcher())
69 if events != expected:
70 self.fail("Expected events:\n%s\nReceived events:\n%s"
71 % (pprint.pformat(expected), pprint.pformat(events)))
74 class ProfileHookTestCase(TestCaseBase):
75 def new_watcher(self):
76 return HookWatcher()
78 def test_simple(self):
79 def f(p):
80 pass
81 f_ident = ident(f)
82 self.check_events(f, [(1, 'call', f_ident),
83 (1, 'return', f_ident),
86 def test_exception(self):
87 def f(p):
88 1/0
89 f_ident = ident(f)
90 self.check_events(f, [(1, 'call', f_ident),
91 (1, 'return', f_ident),
94 def test_caught_exception(self):
95 def f(p):
96 try: 1/0
97 except: pass
98 f_ident = ident(f)
99 self.check_events(f, [(1, 'call', f_ident),
100 (1, 'return', f_ident),
103 def test_caught_nested_exception(self):
104 def f(p):
105 try: 1/0
106 except: pass
107 f_ident = ident(f)
108 self.check_events(f, [(1, 'call', f_ident),
109 (1, 'return', f_ident),
112 def test_nested_exception(self):
113 def f(p):
115 f_ident = ident(f)
116 self.check_events(f, [(1, 'call', f_ident),
117 # This isn't what I expected:
118 # (0, 'exception', protect_ident),
119 # I expected this again:
120 (1, 'return', f_ident),
123 def test_exception_in_except_clause(self):
124 def f(p):
126 def g(p):
127 try:
128 f(p)
129 except:
130 try: f(p)
131 except: pass
132 f_ident = ident(f)
133 g_ident = ident(g)
134 self.check_events(g, [(1, 'call', g_ident),
135 (2, 'call', f_ident),
136 (2, 'return', f_ident),
137 (3, 'call', f_ident),
138 (3, 'return', f_ident),
139 (1, 'return', g_ident),
142 def test_exception_propogation(self):
143 def f(p):
145 def g(p):
146 try: f(p)
147 finally: p.add_event("falling through")
148 f_ident = ident(f)
149 g_ident = ident(g)
150 self.check_events(g, [(1, 'call', g_ident),
151 (2, 'call', f_ident),
152 (2, 'return', f_ident),
153 (1, 'falling through', g_ident),
154 (1, 'return', g_ident),
157 def test_raise_twice(self):
158 def f(p):
159 try: 1/0
160 except: 1/0
161 f_ident = ident(f)
162 self.check_events(f, [(1, 'call', f_ident),
163 (1, 'return', f_ident),
166 def test_raise_reraise(self):
167 def f(p):
168 try: 1/0
169 except: raise
170 f_ident = ident(f)
171 self.check_events(f, [(1, 'call', f_ident),
172 (1, 'return', f_ident),
175 def test_raise(self):
176 def f(p):
177 raise Exception()
178 f_ident = ident(f)
179 self.check_events(f, [(1, 'call', f_ident),
180 (1, 'return', f_ident),
183 def test_distant_exception(self):
184 def f():
186 def g():
188 def h():
190 def i():
192 def j(p):
194 f_ident = ident(f)
195 g_ident = ident(g)
196 h_ident = ident(h)
197 i_ident = ident(i)
198 j_ident = ident(j)
199 self.check_events(j, [(1, 'call', j_ident),
200 (2, 'call', i_ident),
201 (3, 'call', h_ident),
202 (4, 'call', g_ident),
203 (5, 'call', f_ident),
204 (5, 'return', f_ident),
205 (4, 'return', g_ident),
206 (3, 'return', h_ident),
207 (2, 'return', i_ident),
208 (1, 'return', j_ident),
211 def test_generator(self):
212 def f():
213 for i in range(2):
214 yield i
215 def g(p):
216 for i in f():
217 pass
218 f_ident = ident(f)
219 g_ident = ident(g)
220 self.check_events(g, [(1, 'call', g_ident),
221 # call the iterator twice to generate values
222 (2, 'call', f_ident),
223 (2, 'return', f_ident),
224 (2, 'call', f_ident),
225 (2, 'return', f_ident),
226 # once more; returns end-of-iteration with
227 # actually raising an exception
228 (2, 'call', f_ident),
229 (2, 'return', f_ident),
230 (1, 'return', g_ident),
233 def test_stop_iteration(self):
234 def f():
235 for i in range(2):
236 yield i
237 raise StopIteration
238 def g(p):
239 for i in f():
240 pass
241 f_ident = ident(f)
242 g_ident = ident(g)
243 self.check_events(g, [(1, 'call', g_ident),
244 # call the iterator twice to generate values
245 (2, 'call', f_ident),
246 (2, 'return', f_ident),
247 (2, 'call', f_ident),
248 (2, 'return', f_ident),
249 # once more to hit the raise:
250 (2, 'call', f_ident),
251 (2, 'return', f_ident),
252 (1, 'return', g_ident),
256 class ProfileSimulatorTestCase(TestCaseBase):
257 def new_watcher(self):
258 return ProfileSimulator(self)
260 def test_simple(self):
261 def f(p):
262 pass
263 f_ident = ident(f)
264 self.check_events(f, [(1, 'call', f_ident),
265 (1, 'return', f_ident),
268 def test_basic_exception(self):
269 def f(p):
271 f_ident = ident(f)
272 self.check_events(f, [(1, 'call', f_ident),
273 (1, 'return', f_ident),
276 def test_caught_exception(self):
277 def f(p):
278 try: 1/0
279 except: pass
280 f_ident = ident(f)
281 self.check_events(f, [(1, 'call', f_ident),
282 (1, 'return', f_ident),
285 def test_distant_exception(self):
286 def f():
288 def g():
290 def h():
292 def i():
294 def j(p):
296 f_ident = ident(f)
297 g_ident = ident(g)
298 h_ident = ident(h)
299 i_ident = ident(i)
300 j_ident = ident(j)
301 self.check_events(j, [(1, 'call', j_ident),
302 (2, 'call', i_ident),
303 (3, 'call', h_ident),
304 (4, 'call', g_ident),
305 (5, 'call', f_ident),
306 (5, 'return', f_ident),
307 (4, 'return', g_ident),
308 (3, 'return', h_ident),
309 (2, 'return', i_ident),
310 (1, 'return', j_ident),
314 def ident(function):
315 if hasattr(function, "f_code"):
316 code = function.f_code
317 else:
318 code = function.func_code
319 return code.co_firstlineno, code.co_name
322 def protect(f, p):
323 try: f(p)
324 except: pass
326 protect_ident = ident(protect)
329 def capture_events(callable, p=None):
330 try:
331 sys.setprofile()
332 except TypeError:
333 pass
334 else:
335 raise test_support.TestFailed(
336 'sys.setprofile() did not raise TypeError')
338 if p is None:
339 p = HookWatcher()
340 sys.setprofile(p.callback)
341 protect(callable, p)
342 sys.setprofile(None)
343 return p.get_events()[1:-1]
346 def show_events(callable):
347 import pprint
348 pprint.pprint(capture_events(callable))
351 def test_main():
352 test_support.run_unittest(
353 ProfileHookTestCase,
354 ProfileSimulatorTestCase
358 if __name__ == "__main__":
359 test_main()