test_tracing.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. import sys
  2. import unittest
  3. import greenlet
  4. class SomeError(Exception):
  5. pass
  6. class GreenletTracer(object):
  7. oldtrace = None
  8. def __init__(self, error_on_trace=False):
  9. self.actions = []
  10. self.error_on_trace = error_on_trace
  11. def __call__(self, *args):
  12. self.actions.append(args)
  13. if self.error_on_trace:
  14. raise SomeError
  15. def __enter__(self):
  16. self.oldtrace = greenlet.settrace(self)
  17. return self.actions
  18. def __exit__(self, *args):
  19. greenlet.settrace(self.oldtrace)
  20. class TestGreenletTracing(unittest.TestCase):
  21. """
  22. Tests of ``greenlet.settrace()``
  23. """
  24. def test_greenlet_tracing(self):
  25. main = greenlet.getcurrent()
  26. def dummy():
  27. pass
  28. def dummyexc():
  29. raise SomeError()
  30. with GreenletTracer() as actions:
  31. g1 = greenlet.greenlet(dummy)
  32. g1.switch()
  33. g2 = greenlet.greenlet(dummyexc)
  34. self.assertRaises(SomeError, g2.switch)
  35. self.assertEqual(actions, [
  36. ('switch', (main, g1)),
  37. ('switch', (g1, main)),
  38. ('switch', (main, g2)),
  39. ('throw', (g2, main)),
  40. ])
  41. def test_exception_disables_tracing(self):
  42. main = greenlet.getcurrent()
  43. def dummy():
  44. main.switch()
  45. g = greenlet.greenlet(dummy)
  46. g.switch()
  47. with GreenletTracer(error_on_trace=True) as actions:
  48. self.assertRaises(SomeError, g.switch)
  49. self.assertEqual(greenlet.gettrace(), None)
  50. self.assertEqual(actions, [
  51. ('switch', (main, g)),
  52. ])
  53. class PythonTracer(object):
  54. oldtrace = None
  55. def __init__(self):
  56. self.actions = []
  57. def __call__(self, frame, event, arg):
  58. # Record the co_name so we have an idea what function we're in.
  59. self.actions.append((event, frame.f_code.co_name))
  60. def __enter__(self):
  61. self.oldtrace = sys.setprofile(self)
  62. return self.actions
  63. def __exit__(self, *args):
  64. sys.setprofile(self.oldtrace)
  65. def tpt_callback():
  66. return 42
  67. class TestPythonTracing(unittest.TestCase):
  68. """
  69. Tests of the interaction of ``sys.settrace()``
  70. with greenlet facilities.
  71. NOTE: Most of this is probably CPython specific.
  72. """
  73. maxDiff = None
  74. def test_trace_events_trivial(self):
  75. with PythonTracer() as actions:
  76. tpt_callback()
  77. # If we use the sys.settrace instead of setprofile, we get
  78. # this:
  79. # self.assertEqual(actions, [
  80. # ('call', 'tpt_callback'),
  81. # ('call', '__exit__'),
  82. # ])
  83. self.assertEqual(actions, [
  84. ('return', '__enter__'),
  85. ('call', 'tpt_callback'),
  86. ('return', 'tpt_callback'),
  87. ('call', '__exit__'),
  88. ('c_call', '__exit__'),
  89. ])
  90. def _trace_switch(self, glet):
  91. with PythonTracer() as actions:
  92. glet.switch()
  93. return actions
  94. def _check_trace_events_func_already_set(self, glet):
  95. actions = self._trace_switch(glet)
  96. self.assertEqual(actions, [
  97. ('return', '__enter__'),
  98. ('c_call', '_trace_switch'),
  99. ('call', 'run'),
  100. ('call', 'tpt_callback'),
  101. ('return', 'tpt_callback'),
  102. ('return', 'run'),
  103. ('c_return', '_trace_switch'),
  104. ('call', '__exit__'),
  105. ('c_call', '__exit__'),
  106. ])
  107. def test_trace_events_into_greenlet_func_already_set(self):
  108. def run():
  109. return tpt_callback()
  110. self._check_trace_events_func_already_set(greenlet.greenlet(run))
  111. def test_trace_events_into_greenlet_subclass_already_set(self):
  112. class X(greenlet.greenlet):
  113. def run(self):
  114. return tpt_callback()
  115. self._check_trace_events_func_already_set(X())
  116. def _check_trace_events_from_greenlet_sets_profiler(self, g, tracer):
  117. g.switch()
  118. tpt_callback()
  119. tracer.__exit__()
  120. self.assertEqual(tracer.actions, [
  121. ('return', '__enter__'),
  122. ('call', 'tpt_callback'),
  123. ('return', 'tpt_callback'),
  124. ('return', 'run'),
  125. ('call', 'tpt_callback'),
  126. ('return', 'tpt_callback'),
  127. ('call', '__exit__'),
  128. ('c_call', '__exit__'),
  129. ])
  130. def test_trace_events_from_greenlet_func_sets_profiler(self):
  131. tracer = PythonTracer()
  132. def run():
  133. tracer.__enter__()
  134. return tpt_callback()
  135. self._check_trace_events_from_greenlet_sets_profiler(greenlet.greenlet(run),
  136. tracer)
  137. def test_trace_events_from_greenlet_subclass_sets_profiler(self):
  138. tracer = PythonTracer()
  139. class X(greenlet.greenlet):
  140. def run(self):
  141. tracer.__enter__()
  142. return tpt_callback()
  143. self._check_trace_events_from_greenlet_sets_profiler(X(), tracer)
  144. def test_trace_events_multiple_greenlets_switching(self):
  145. tracer = PythonTracer()
  146. g1 = None
  147. g2 = None
  148. def g1_run():
  149. tracer.__enter__()
  150. tpt_callback()
  151. g2.switch()
  152. tpt_callback()
  153. return 42
  154. def g2_run():
  155. tpt_callback()
  156. tracer.__exit__()
  157. tpt_callback()
  158. g1.switch()
  159. g1 = greenlet.greenlet(g1_run)
  160. g2 = greenlet.greenlet(g2_run)
  161. x = g1.switch()
  162. self.assertEqual(x, 42)
  163. tpt_callback() # ensure not in the trace
  164. self.assertEqual(tracer.actions, [
  165. ('return', '__enter__'),
  166. ('call', 'tpt_callback'),
  167. ('return', 'tpt_callback'),
  168. ('c_call', 'g1_run'),
  169. ('call', 'g2_run'),
  170. ('call', 'tpt_callback'),
  171. ('return', 'tpt_callback'),
  172. ('call', '__exit__'),
  173. ('c_call', '__exit__'),
  174. ])
  175. def test_trace_events_multiple_greenlets_switching_siblings(self):
  176. # Like the first version, but get both greenlets running first
  177. # as "siblings" and then establish the tracing.
  178. tracer = PythonTracer()
  179. g1 = None
  180. g2 = None
  181. def g1_run():
  182. greenlet.getcurrent().parent.switch()
  183. tracer.__enter__()
  184. tpt_callback()
  185. g2.switch()
  186. tpt_callback()
  187. return 42
  188. def g2_run():
  189. greenlet.getcurrent().parent.switch()
  190. tpt_callback()
  191. tracer.__exit__()
  192. tpt_callback()
  193. g1.switch()
  194. g1 = greenlet.greenlet(g1_run)
  195. g2 = greenlet.greenlet(g2_run)
  196. # Start g1
  197. g1.switch()
  198. # And it immediately returns control to us.
  199. # Start g2
  200. g2.switch()
  201. # Which also returns. Now kick of the real part of the
  202. # test.
  203. x = g1.switch()
  204. self.assertEqual(x, 42)
  205. tpt_callback() # ensure not in the trace
  206. self.assertEqual(tracer.actions, [
  207. ('return', '__enter__'),
  208. ('call', 'tpt_callback'),
  209. ('return', 'tpt_callback'),
  210. ('c_call', 'g1_run'),
  211. ('call', 'tpt_callback'),
  212. ('return', 'tpt_callback'),
  213. ('call', '__exit__'),
  214. ('c_call', '__exit__'),
  215. ])