/Lib/test/test_profilehooks.py

http://unladen-swallow.googlecode.com/ · Python · 385 lines · 316 code · 58 blank · 11 comment · 30 complexity · a8899b00a17870b62ccb017f02e084ac MD5 · raw file

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