Back to index

python3.2  3.2.2
test_sys_setprofile.py
Go to the documentation of this file.
00001 import gc
00002 import pprint
00003 import sys
00004 import unittest
00005 
00006 from test import support
00007 
00008 class TestGetProfile(unittest.TestCase):
00009     def setUp(self):
00010         sys.setprofile(None)
00011 
00012     def tearDown(self):
00013         sys.setprofile(None)
00014 
00015     def test_empty(self):
00016         self.assertIsNone(sys.getprofile())
00017 
00018     def test_setget(self):
00019         def fn(*args):
00020             pass
00021 
00022         sys.setprofile(fn)
00023         self.assertIs(sys.getprofile(), fn)
00024 
00025 class HookWatcher:
00026     def __init__(self):
00027         self.frames = []
00028         self.events = []
00029 
00030     def callback(self, frame, event, arg):
00031         if (event == "call"
00032             or event == "return"
00033             or event == "exception"):
00034             self.add_event(event, frame)
00035 
00036     def add_event(self, event, frame=None):
00037         """Add an event to the log."""
00038         if frame is None:
00039             frame = sys._getframe(1)
00040 
00041         try:
00042             frameno = self.frames.index(frame)
00043         except ValueError:
00044             frameno = len(self.frames)
00045             self.frames.append(frame)
00046 
00047         self.events.append((frameno, event, ident(frame)))
00048 
00049     def get_events(self):
00050         """Remove calls to add_event()."""
00051         disallowed = [ident(self.add_event.__func__), ident(ident)]
00052         self.frames = None
00053 
00054         return [item for item in self.events if item[2] not in disallowed]
00055 
00056 
00057 class ProfileSimulator(HookWatcher):
00058     def __init__(self, testcase):
00059         self.testcase = testcase
00060         self.stack = []
00061         HookWatcher.__init__(self)
00062 
00063     def callback(self, frame, event, arg):
00064         # Callback registered with sys.setprofile()/sys.settrace()
00065         self.dispatch[event](self, frame)
00066 
00067     def trace_call(self, frame):
00068         self.add_event('call', frame)
00069         self.stack.append(frame)
00070 
00071     def trace_return(self, frame):
00072         self.add_event('return', frame)
00073         self.stack.pop()
00074 
00075     def trace_exception(self, frame):
00076         self.testcase.fail(
00077             "the profiler should never receive exception events")
00078 
00079     def trace_pass(self, frame):
00080         pass
00081 
00082     dispatch = {
00083         'call': trace_call,
00084         'exception': trace_exception,
00085         'return': trace_return,
00086         'c_call': trace_pass,
00087         'c_return': trace_pass,
00088         'c_exception': trace_pass,
00089         }
00090 
00091 
00092 class TestCaseBase(unittest.TestCase):
00093     def check_events(self, callable, expected):
00094         events = capture_events(callable, self.new_watcher())
00095         if events != expected:
00096             self.fail("Expected events:\n%s\nReceived events:\n%s"
00097                       % (pprint.pformat(expected), pprint.pformat(events)))
00098 
00099 
00100 class ProfileHookTestCase(TestCaseBase):
00101     def new_watcher(self):
00102         return HookWatcher()
00103 
00104     def test_simple(self):
00105         def f(p):
00106             pass
00107         f_ident = ident(f)
00108         self.check_events(f, [(1, 'call', f_ident),
00109                               (1, 'return', f_ident),
00110                               ])
00111 
00112     def test_exception(self):
00113         def f(p):
00114             1/0
00115         f_ident = ident(f)
00116         self.check_events(f, [(1, 'call', f_ident),
00117                               (1, 'return', f_ident),
00118                               ])
00119 
00120     def test_caught_exception(self):
00121         def f(p):
00122             try: 1/0
00123             except: pass
00124         f_ident = ident(f)
00125         self.check_events(f, [(1, 'call', f_ident),
00126                               (1, 'return', f_ident),
00127                               ])
00128 
00129     def test_caught_nested_exception(self):
00130         def f(p):
00131             try: 1/0
00132             except: pass
00133         f_ident = ident(f)
00134         self.check_events(f, [(1, 'call', f_ident),
00135                               (1, 'return', f_ident),
00136                               ])
00137 
00138     def test_nested_exception(self):
00139         def f(p):
00140             1/0
00141         f_ident = ident(f)
00142         self.check_events(f, [(1, 'call', f_ident),
00143                               # This isn't what I expected:
00144                               # (0, 'exception', protect_ident),
00145                               # I expected this again:
00146                               (1, 'return', f_ident),
00147                               ])
00148 
00149     def test_exception_in_except_clause(self):
00150         def f(p):
00151             1/0
00152         def g(p):
00153             try:
00154                 f(p)
00155             except:
00156                 try: f(p)
00157                 except: pass
00158         f_ident = ident(f)
00159         g_ident = ident(g)
00160         self.check_events(g, [(1, 'call', g_ident),
00161                               (2, 'call', f_ident),
00162                               (2, 'return', f_ident),
00163                               (3, 'call', f_ident),
00164                               (3, 'return', f_ident),
00165                               (1, 'return', g_ident),
00166                               ])
00167 
00168     def test_exception_propogation(self):
00169         def f(p):
00170             1/0
00171         def g(p):
00172             try: f(p)
00173             finally: p.add_event("falling through")
00174         f_ident = ident(f)
00175         g_ident = ident(g)
00176         self.check_events(g, [(1, 'call', g_ident),
00177                               (2, 'call', f_ident),
00178                               (2, 'return', f_ident),
00179                               (1, 'falling through', g_ident),
00180                               (1, 'return', g_ident),
00181                               ])
00182 
00183     def test_raise_twice(self):
00184         def f(p):
00185             try: 1/0
00186             except: 1/0
00187         f_ident = ident(f)
00188         self.check_events(f, [(1, 'call', f_ident),
00189                               (1, 'return', f_ident),
00190                               ])
00191 
00192     def test_raise_reraise(self):
00193         def f(p):
00194             try: 1/0
00195             except: raise
00196         f_ident = ident(f)
00197         self.check_events(f, [(1, 'call', f_ident),
00198                               (1, 'return', f_ident),
00199                               ])
00200 
00201     def test_raise(self):
00202         def f(p):
00203             raise Exception()
00204         f_ident = ident(f)
00205         self.check_events(f, [(1, 'call', f_ident),
00206                               (1, 'return', f_ident),
00207                               ])
00208 
00209     def test_distant_exception(self):
00210         def f():
00211             1/0
00212         def g():
00213             f()
00214         def h():
00215             g()
00216         def i():
00217             h()
00218         def j(p):
00219             i()
00220         f_ident = ident(f)
00221         g_ident = ident(g)
00222         h_ident = ident(h)
00223         i_ident = ident(i)
00224         j_ident = ident(j)
00225         self.check_events(j, [(1, 'call', j_ident),
00226                               (2, 'call', i_ident),
00227                               (3, 'call', h_ident),
00228                               (4, 'call', g_ident),
00229                               (5, 'call', f_ident),
00230                               (5, 'return', f_ident),
00231                               (4, 'return', g_ident),
00232                               (3, 'return', h_ident),
00233                               (2, 'return', i_ident),
00234                               (1, 'return', j_ident),
00235                               ])
00236 
00237     def test_generator(self):
00238         def f():
00239             for i in range(2):
00240                 yield i
00241         def g(p):
00242             for i in f():
00243                 pass
00244         f_ident = ident(f)
00245         g_ident = ident(g)
00246         self.check_events(g, [(1, 'call', g_ident),
00247                               # call the iterator twice to generate values
00248                               (2, 'call', f_ident),
00249                               (2, 'return', f_ident),
00250                               (2, 'call', f_ident),
00251                               (2, 'return', f_ident),
00252                               # once more; returns end-of-iteration with
00253                               # actually raising an exception
00254                               (2, 'call', f_ident),
00255                               (2, 'return', f_ident),
00256                               (1, 'return', g_ident),
00257                               ])
00258 
00259     def test_stop_iteration(self):
00260         def f():
00261             for i in range(2):
00262                 yield i
00263             raise StopIteration
00264         def g(p):
00265             for i in f():
00266                 pass
00267         f_ident = ident(f)
00268         g_ident = ident(g)
00269         self.check_events(g, [(1, 'call', g_ident),
00270                               # call the iterator twice to generate values
00271                               (2, 'call', f_ident),
00272                               (2, 'return', f_ident),
00273                               (2, 'call', f_ident),
00274                               (2, 'return', f_ident),
00275                               # once more to hit the raise:
00276                               (2, 'call', f_ident),
00277                               (2, 'return', f_ident),
00278                               (1, 'return', g_ident),
00279                               ])
00280 
00281 
00282 class ProfileSimulatorTestCase(TestCaseBase):
00283     def new_watcher(self):
00284         return ProfileSimulator(self)
00285 
00286     def test_simple(self):
00287         def f(p):
00288             pass
00289         f_ident = ident(f)
00290         self.check_events(f, [(1, 'call', f_ident),
00291                               (1, 'return', f_ident),
00292                               ])
00293 
00294     def test_basic_exception(self):
00295         def f(p):
00296             1/0
00297         f_ident = ident(f)
00298         self.check_events(f, [(1, 'call', f_ident),
00299                               (1, 'return', f_ident),
00300                               ])
00301 
00302     def test_caught_exception(self):
00303         def f(p):
00304             try: 1/0
00305             except: pass
00306         f_ident = ident(f)
00307         self.check_events(f, [(1, 'call', f_ident),
00308                               (1, 'return', f_ident),
00309                               ])
00310 
00311     def test_distant_exception(self):
00312         def f():
00313             1/0
00314         def g():
00315             f()
00316         def h():
00317             g()
00318         def i():
00319             h()
00320         def j(p):
00321             i()
00322         f_ident = ident(f)
00323         g_ident = ident(g)
00324         h_ident = ident(h)
00325         i_ident = ident(i)
00326         j_ident = ident(j)
00327         self.check_events(j, [(1, 'call', j_ident),
00328                               (2, 'call', i_ident),
00329                               (3, 'call', h_ident),
00330                               (4, 'call', g_ident),
00331                               (5, 'call', f_ident),
00332                               (5, 'return', f_ident),
00333                               (4, 'return', g_ident),
00334                               (3, 'return', h_ident),
00335                               (2, 'return', i_ident),
00336                               (1, 'return', j_ident),
00337                               ])
00338 
00339 
00340 def ident(function):
00341     if hasattr(function, "f_code"):
00342         code = function.f_code
00343     else:
00344         code = function.__code__
00345     return code.co_firstlineno, code.co_name
00346 
00347 
00348 def protect(f, p):
00349     try: f(p)
00350     except: pass
00351 
00352 protect_ident = ident(protect)
00353 
00354 
00355 def capture_events(callable, p=None):
00356     if p is None:
00357         p = HookWatcher()
00358     # Disable the garbage collector. This prevents __del__s from showing up in
00359     # traces.
00360     old_gc = gc.isenabled()
00361     gc.disable()
00362     try:
00363         sys.setprofile(p.callback)
00364         protect(callable, p)
00365         sys.setprofile(None)
00366     finally:
00367         if old_gc:
00368             gc.enable()
00369     return p.get_events()[1:-1]
00370 
00371 
00372 def show_events(callable):
00373     import pprint
00374     pprint.pprint(capture_events(callable))
00375 
00376 
00377 def test_main():
00378     support.run_unittest(
00379         TestGetProfile,
00380         ProfileHookTestCase,
00381         ProfileSimulatorTestCase
00382     )
00383 
00384 
00385 if __name__ == "__main__":
00386     test_main()