PageRenderTime 42ms CodeModel.GetById 2ms app.highlight 34ms RepoModel.GetById 2ms app.codeStats 0ms

/Lib/test/test_profilehooks.py

http://unladen-swallow.googlecode.com/
Python | 385 lines | 316 code | 58 blank | 11 comment | 21 complexity | a8899b00a17870b62ccb017f02e084ac MD5 | raw file
  1import pprint
  2import sys
  3import unittest
  4
  5from test import test_support
  6
  7class TestGetProfile(unittest.TestCase):
  8    def setUp(self):
  9        sys.setprofile(None)
 10
 11    def tearDown(self):
 12        sys.setprofile(None)
 13
 14    def test_empty(self):
 15        assert sys.getprofile() == None
 16
 17    def test_setget(self):
 18        def fn(*args):
 19            pass
 20
 21        sys.setprofile(fn)
 22        assert sys.getprofile() == fn
 23
 24class HookWatcher:
 25    def __init__(self):
 26        self.frames = []
 27        self.events = []
 28
 29    def callback(self, frame, event, arg):
 30        if (event == "call"
 31            or event == "return"
 32            or event == "exception"):
 33            self.add_event(event, frame)
 34
 35    def add_event(self, event, frame=None):
 36        """Add an event to the log."""
 37        if frame is None:
 38            frame = sys._getframe(1)
 39
 40        try:
 41            frameno = self.frames.index(frame)
 42        except ValueError:
 43            frameno = len(self.frames)
 44            self.frames.append(frame)
 45
 46        self.events.append((frameno, event, ident(frame)))
 47
 48    def get_events(self):
 49        """Remove calls to add_event()."""
 50        disallowed = [ident(self.add_event.im_func), ident(ident)]
 51        self.frames = None
 52
 53        return [item for item in self.events if item[2] not in disallowed]
 54
 55
 56class ProfileSimulator(HookWatcher):
 57    def __init__(self, testcase):
 58        self.testcase = testcase
 59        self.stack = []
 60        HookWatcher.__init__(self)
 61
 62    def callback(self, frame, event, arg):
 63        # Callback registered with sys.setprofile()/sys.settrace()
 64        self.dispatch[event](self, frame)
 65
 66    def trace_call(self, frame):
 67        self.add_event('call', frame)
 68        self.stack.append(frame)
 69
 70    def trace_return(self, frame):
 71        self.add_event('return', frame)
 72        self.stack.pop()
 73
 74    def trace_exception(self, frame):
 75        self.testcase.fail(
 76            "the profiler should never receive exception events")
 77
 78    def trace_pass(self, frame):
 79        pass
 80
 81    dispatch = {
 82        'call': trace_call,
 83        'exception': trace_exception,
 84        'return': trace_return,
 85        'c_call': trace_pass,
 86        'c_return': trace_pass,
 87        'c_exception': trace_pass,
 88        }
 89
 90
 91class TestCaseBase(unittest.TestCase):
 92    def check_events(self, callable, expected):
 93        events = capture_events(callable, self.new_watcher())
 94        if events != expected:
 95            self.fail("Expected events:\n%s\nReceived events:\n%s"
 96                      % (pprint.pformat(expected), pprint.pformat(events)))
 97
 98
 99class ProfileHookTestCase(TestCaseBase):
100    def new_watcher(self):
101        return HookWatcher()
102
103    def test_simple(self):
104        def f(p):
105            pass
106        f_ident = ident(f)
107        self.check_events(f, [(1, 'call', f_ident),
108                              (1, 'return', f_ident),
109                              ])
110
111    def test_exception(self):
112        def f(p):
113            1/0
114        f_ident = ident(f)
115        self.check_events(f, [(1, 'call', f_ident),
116                              (1, 'return', f_ident),
117                              ])
118
119    def test_caught_exception(self):
120        def f(p):
121            try: 1/0
122            except: pass
123        f_ident = ident(f)
124        self.check_events(f, [(1, 'call', f_ident),
125                              (1, 'return', f_ident),
126                              ])
127
128    def test_caught_nested_exception(self):
129        def f(p):
130            try: 1/0
131            except: pass
132        f_ident = ident(f)
133        self.check_events(f, [(1, 'call', f_ident),
134                              (1, 'return', f_ident),
135                              ])
136
137    def test_nested_exception(self):
138        def f(p):
139            1/0
140        f_ident = ident(f)
141        self.check_events(f, [(1, 'call', f_ident),
142                              # This isn't what I expected:
143                              # (0, 'exception', protect_ident),
144                              # I expected this again:
145                              (1, 'return', f_ident),
146                              ])
147
148    def test_exception_in_except_clause(self):
149        def f(p):
150            1/0
151        def g(p):
152            try:
153                f(p)
154            except:
155                try: f(p)
156                except: pass
157        f_ident = ident(f)
158        g_ident = ident(g)
159        self.check_events(g, [(1, 'call', g_ident),
160                              (2, 'call', f_ident),
161                              (2, 'return', f_ident),
162                              (3, 'call', f_ident),
163                              (3, 'return', f_ident),
164                              (1, 'return', g_ident),
165                              ])
166
167    def test_exception_propogation(self):
168        def f(p):
169            1/0
170        def g(p):
171            try: f(p)
172            finally: p.add_event("falling through")
173        f_ident = ident(f)
174        g_ident = ident(g)
175        self.check_events(g, [(1, 'call', g_ident),
176                              (2, 'call', f_ident),
177                              (2, 'return', f_ident),
178                              (1, 'falling through', g_ident),
179                              (1, 'return', g_ident),
180                              ])
181
182    def test_raise_twice(self):
183        def f(p):
184            try: 1/0
185            except: 1/0
186        f_ident = ident(f)
187        self.check_events(f, [(1, 'call', f_ident),
188                              (1, 'return', f_ident),
189                              ])
190
191    def test_raise_reraise(self):
192        def f(p):
193            try: 1/0
194            except: raise
195        f_ident = ident(f)
196        self.check_events(f, [(1, 'call', f_ident),
197                              (1, 'return', f_ident),
198                              ])
199
200    def test_raise(self):
201        def f(p):
202            raise Exception()
203        f_ident = ident(f)
204        self.check_events(f, [(1, 'call', f_ident),
205                              (1, 'return', f_ident),
206                              ])
207
208    def test_distant_exception(self):
209        def f():
210            1/0
211        def g():
212            f()
213        def h():
214            g()
215        def i():
216            h()
217        def j(p):
218            i()
219        f_ident = ident(f)
220        g_ident = ident(g)
221        h_ident = ident(h)
222        i_ident = ident(i)
223        j_ident = ident(j)
224        self.check_events(j, [(1, 'call', j_ident),
225                              (2, 'call', i_ident),
226                              (3, 'call', h_ident),
227                              (4, 'call', g_ident),
228                              (5, 'call', f_ident),
229                              (5, 'return', f_ident),
230                              (4, 'return', g_ident),
231                              (3, 'return', h_ident),
232                              (2, 'return', i_ident),
233                              (1, 'return', j_ident),
234                              ])
235
236    def test_generator(self):
237        def f():
238            for i in range(2):
239                yield i
240        def g(p):
241            for i in f():
242                pass
243        f_ident = ident(f)
244        g_ident = ident(g)
245        self.check_events(g, [(1, 'call', g_ident),
246                              # call the iterator twice to generate values
247                              (2, 'call', f_ident),
248                              (2, 'return', f_ident),
249                              (2, 'call', f_ident),
250                              (2, 'return', f_ident),
251                              # once more; returns end-of-iteration with
252                              # actually raising an exception
253                              (2, 'call', f_ident),
254                              (2, 'return', f_ident),
255                              (1, 'return', g_ident),
256                              ])
257
258    def test_stop_iteration(self):
259        def f():
260            for i in range(2):
261                yield i
262            raise StopIteration
263        def g(p):
264            for i in f():
265                pass
266        f_ident = ident(f)
267        g_ident = ident(g)
268        self.check_events(g, [(1, 'call', g_ident),
269                              # call the iterator twice to generate values
270                              (2, 'call', f_ident),
271                              (2, 'return', f_ident),
272                              (2, 'call', f_ident),
273                              (2, 'return', f_ident),
274                              # once more to hit the raise:
275                              (2, 'call', f_ident),
276                              (2, 'return', f_ident),
277                              (1, 'return', g_ident),
278                              ])
279
280
281class ProfileSimulatorTestCase(TestCaseBase):
282    def new_watcher(self):
283        return ProfileSimulator(self)
284
285    def test_simple(self):
286        def f(p):
287            pass
288        f_ident = ident(f)
289        self.check_events(f, [(1, 'call', f_ident),
290                              (1, 'return', f_ident),
291                              ])
292
293    def test_basic_exception(self):
294        def f(p):
295            1/0
296        f_ident = ident(f)
297        self.check_events(f, [(1, 'call', f_ident),
298                              (1, 'return', f_ident),
299                              ])
300
301    def test_caught_exception(self):
302        def f(p):
303            try: 1/0
304            except: pass
305        f_ident = ident(f)
306        self.check_events(f, [(1, 'call', f_ident),
307                              (1, 'return', f_ident),
308                              ])
309
310    def test_distant_exception(self):
311        def f():
312            1/0
313        def g():
314            f()
315        def h():
316            g()
317        def i():
318            h()
319        def j(p):
320            i()
321        f_ident = ident(f)
322        g_ident = ident(g)
323        h_ident = ident(h)
324        i_ident = ident(i)
325        j_ident = ident(j)
326        self.check_events(j, [(1, 'call', j_ident),
327                              (2, 'call', i_ident),
328                              (3, 'call', h_ident),
329                              (4, 'call', g_ident),
330                              (5, 'call', f_ident),
331                              (5, 'return', f_ident),
332                              (4, 'return', g_ident),
333                              (3, 'return', h_ident),
334                              (2, 'return', i_ident),
335                              (1, 'return', j_ident),
336                              ])
337
338
339def ident(function):
340    if hasattr(function, "f_code"):
341        code = function.f_code
342    else:
343        code = function.func_code
344    return code.co_firstlineno, code.co_name
345
346
347def protect(f, p):
348    try: f(p)
349    except: pass
350
351protect_ident = ident(protect)
352
353
354def capture_events(callable, p=None):
355    try:
356        sys.setprofile()
357    except TypeError:
358        pass
359    else:
360        raise test_support.TestFailed(
361            'sys.setprofile() did not raise TypeError')
362
363    if p is None:
364        p = HookWatcher()
365    sys.setprofile(p.callback)
366    protect(callable, p)
367    sys.setprofile(None)
368    return p.get_events()[1:-1]
369
370
371def show_events(callable):
372    import pprint
373    pprint.pprint(capture_events(callable))
374
375
376def test_main():
377    test_support.run_unittest(
378        TestGetProfile,
379        ProfileHookTestCase,
380        ProfileSimulatorTestCase
381    )
382
383
384if __name__ == "__main__":
385    test_main()