test_sys_setprofile.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. import gc
  2. import pprint
  3. import sys
  4. import unittest
  5. from test import support
  6. class TestGetProfile(unittest.TestCase):
  7. def setUp(self):
  8. sys.setprofile(None)
  9. def tearDown(self):
  10. sys.setprofile(None)
  11. def test_empty(self):
  12. self.assertIsNone(sys.getprofile())
  13. def test_setget(self):
  14. def fn(*args):
  15. pass
  16. sys.setprofile(fn)
  17. self.assertIs(sys.getprofile(), fn)
  18. class HookWatcher:
  19. def __init__(self):
  20. self.frames = []
  21. self.events = []
  22. def callback(self, frame, event, arg):
  23. if (event == "call"
  24. or event == "return"
  25. or event == "exception"):
  26. self.add_event(event, frame)
  27. def add_event(self, event, frame=None):
  28. """Add an event to the log."""
  29. if frame is None:
  30. frame = sys._getframe(1)
  31. try:
  32. frameno = self.frames.index(frame)
  33. except ValueError:
  34. frameno = len(self.frames)
  35. self.frames.append(frame)
  36. self.events.append((frameno, event, ident(frame)))
  37. def get_events(self):
  38. """Remove calls to add_event()."""
  39. disallowed = [ident(self.add_event.__func__), ident(ident)]
  40. self.frames = None
  41. return [item for item in self.events if item[2] not in disallowed]
  42. class ProfileSimulator(HookWatcher):
  43. def __init__(self, testcase):
  44. self.testcase = testcase
  45. self.stack = []
  46. HookWatcher.__init__(self)
  47. def callback(self, frame, event, arg):
  48. # Callback registered with sys.setprofile()/sys.settrace()
  49. self.dispatch[event](self, frame)
  50. def trace_call(self, frame):
  51. self.add_event('call', frame)
  52. self.stack.append(frame)
  53. def trace_return(self, frame):
  54. self.add_event('return', frame)
  55. self.stack.pop()
  56. def trace_exception(self, frame):
  57. self.testcase.fail(
  58. "the profiler should never receive exception events")
  59. def trace_pass(self, frame):
  60. pass
  61. dispatch = {
  62. 'call': trace_call,
  63. 'exception': trace_exception,
  64. 'return': trace_return,
  65. 'c_call': trace_pass,
  66. 'c_return': trace_pass,
  67. 'c_exception': trace_pass,
  68. }
  69. class TestCaseBase(unittest.TestCase):
  70. def check_events(self, callable, expected):
  71. events = capture_events(callable, self.new_watcher())
  72. if events != expected:
  73. self.fail("Expected events:\n%s\nReceived events:\n%s"
  74. % (pprint.pformat(expected), pprint.pformat(events)))
  75. class ProfileHookTestCase(TestCaseBase):
  76. def new_watcher(self):
  77. 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),
  84. ])
  85. def test_exception(self):
  86. def f(p):
  87. 1/0
  88. f_ident = ident(f)
  89. self.check_events(f, [(1, 'call', f_ident),
  90. (1, 'return', f_ident),
  91. ])
  92. def test_caught_exception(self):
  93. def f(p):
  94. try: 1/0
  95. except: pass
  96. f_ident = ident(f)
  97. self.check_events(f, [(1, 'call', f_ident),
  98. (1, 'return', f_ident),
  99. ])
  100. def test_caught_nested_exception(self):
  101. def f(p):
  102. try: 1/0
  103. except: pass
  104. f_ident = ident(f)
  105. self.check_events(f, [(1, 'call', f_ident),
  106. (1, 'return', f_ident),
  107. ])
  108. def test_nested_exception(self):
  109. def f(p):
  110. 1/0
  111. f_ident = ident(f)
  112. self.check_events(f, [(1, 'call', f_ident),
  113. # This isn't what I expected:
  114. # (0, 'exception', protect_ident),
  115. # I expected this again:
  116. (1, 'return', f_ident),
  117. ])
  118. def test_exception_in_except_clause(self):
  119. def f(p):
  120. 1/0
  121. def g(p):
  122. try:
  123. f(p)
  124. except:
  125. try: f(p)
  126. except: pass
  127. f_ident = ident(f)
  128. g_ident = ident(g)
  129. self.check_events(g, [(1, 'call', g_ident),
  130. (2, 'call', f_ident),
  131. (2, 'return', f_ident),
  132. (3, 'call', f_ident),
  133. (3, 'return', f_ident),
  134. (1, 'return', g_ident),
  135. ])
  136. def test_exception_propagation(self):
  137. def f(p):
  138. 1/0
  139. def g(p):
  140. try: f(p)
  141. finally: p.add_event("falling through")
  142. f_ident = ident(f)
  143. g_ident = ident(g)
  144. self.check_events(g, [(1, 'call', g_ident),
  145. (2, 'call', f_ident),
  146. (2, 'return', f_ident),
  147. (1, 'falling through', g_ident),
  148. (1, 'return', g_ident),
  149. ])
  150. def test_raise_twice(self):
  151. def f(p):
  152. try: 1/0
  153. except: 1/0
  154. f_ident = ident(f)
  155. self.check_events(f, [(1, 'call', f_ident),
  156. (1, 'return', f_ident),
  157. ])
  158. def test_raise_reraise(self):
  159. def f(p):
  160. try: 1/0
  161. except: raise
  162. f_ident = ident(f)
  163. self.check_events(f, [(1, 'call', f_ident),
  164. (1, 'return', f_ident),
  165. ])
  166. def test_raise(self):
  167. def f(p):
  168. raise Exception()
  169. f_ident = ident(f)
  170. self.check_events(f, [(1, 'call', f_ident),
  171. (1, 'return', f_ident),
  172. ])
  173. def test_distant_exception(self):
  174. def f():
  175. 1/0
  176. def g():
  177. f()
  178. def h():
  179. g()
  180. def i():
  181. h()
  182. def j(p):
  183. i()
  184. f_ident = ident(f)
  185. g_ident = ident(g)
  186. h_ident = ident(h)
  187. i_ident = ident(i)
  188. j_ident = ident(j)
  189. self.check_events(j, [(1, 'call', j_ident),
  190. (2, 'call', i_ident),
  191. (3, 'call', h_ident),
  192. (4, 'call', g_ident),
  193. (5, 'call', f_ident),
  194. (5, 'return', f_ident),
  195. (4, 'return', g_ident),
  196. (3, 'return', h_ident),
  197. (2, 'return', i_ident),
  198. (1, 'return', j_ident),
  199. ])
  200. def test_generator(self):
  201. def f():
  202. for i in range(2):
  203. yield i
  204. def g(p):
  205. for i in f():
  206. pass
  207. f_ident = ident(f)
  208. g_ident = ident(g)
  209. self.check_events(g, [(1, 'call', g_ident),
  210. # call the iterator twice to generate values
  211. (2, 'call', f_ident),
  212. (2, 'return', f_ident),
  213. (2, 'call', f_ident),
  214. (2, 'return', f_ident),
  215. # once more; returns end-of-iteration with
  216. # actually raising an exception
  217. (2, 'call', f_ident),
  218. (2, 'return', f_ident),
  219. (1, 'return', g_ident),
  220. ])
  221. def test_stop_iteration(self):
  222. def f():
  223. for i in range(2):
  224. yield i
  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. # bpo-34125: profiling method_descriptor with **kwargs
  294. def test_unbound_method(self):
  295. kwargs = {}
  296. def f(p):
  297. dict.get({}, 42, **kwargs)
  298. f_ident = ident(f)
  299. self.check_events(f, [(1, 'call', f_ident),
  300. (1, 'return', f_ident)])
  301. # Test an invalid call (bpo-34126)
  302. def test_unbound_method_no_args(self):
  303. def f(p):
  304. dict.get()
  305. f_ident = ident(f)
  306. self.check_events(f, [(1, 'call', f_ident),
  307. (1, 'return', f_ident)])
  308. # Test an invalid call (bpo-34126)
  309. def test_unbound_method_invalid_args(self):
  310. def f(p):
  311. dict.get(print, 42)
  312. f_ident = ident(f)
  313. self.check_events(f, [(1, 'call', f_ident),
  314. (1, 'return', f_ident)])
  315. # Test an invalid call (bpo-34125)
  316. def test_unbound_method_no_keyword_args(self):
  317. kwargs = {}
  318. def f(p):
  319. dict.get(**kwargs)
  320. f_ident = ident(f)
  321. self.check_events(f, [(1, 'call', f_ident),
  322. (1, 'return', f_ident)])
  323. # Test an invalid call (bpo-34125)
  324. def test_unbound_method_invalid_keyword_args(self):
  325. kwargs = {}
  326. def f(p):
  327. dict.get(print, 42, **kwargs)
  328. f_ident = ident(f)
  329. self.check_events(f, [(1, 'call', f_ident),
  330. (1, 'return', f_ident)])
  331. def ident(function):
  332. if hasattr(function, "f_code"):
  333. code = function.f_code
  334. else:
  335. code = function.__code__
  336. return code.co_firstlineno, code.co_name
  337. def protect(f, p):
  338. try: f(p)
  339. except: pass
  340. protect_ident = ident(protect)
  341. def capture_events(callable, p=None):
  342. if p is None:
  343. p = HookWatcher()
  344. # Disable the garbage collector. This prevents __del__s from showing up in
  345. # traces.
  346. old_gc = gc.isenabled()
  347. gc.disable()
  348. try:
  349. sys.setprofile(p.callback)
  350. protect(callable, p)
  351. sys.setprofile(None)
  352. finally:
  353. if old_gc:
  354. gc.enable()
  355. return p.get_events()[1:-1]
  356. def show_events(callable):
  357. import pprint
  358. pprint.pprint(capture_events(callable))
  359. class TestEdgeCases(unittest.TestCase):
  360. def setUp(self):
  361. self.addCleanup(sys.setprofile, sys.getprofile())
  362. sys.setprofile(None)
  363. def test_reentrancy(self):
  364. def foo(*args):
  365. ...
  366. def bar(*args):
  367. ...
  368. class A:
  369. def __call__(self, *args):
  370. pass
  371. def __del__(self):
  372. sys.setprofile(bar)
  373. sys.setprofile(A())
  374. with support.catch_unraisable_exception() as cm:
  375. sys.setprofile(foo)
  376. self.assertEqual(cm.unraisable.object, A.__del__)
  377. self.assertIsInstance(cm.unraisable.exc_value, RuntimeError)
  378. self.assertEqual(sys.getprofile(), foo)
  379. def test_same_object(self):
  380. def foo(*args):
  381. ...
  382. sys.setprofile(foo)
  383. del foo
  384. sys.setprofile(sys.getprofile())
  385. if __name__ == "__main__":
  386. unittest.main()