gen_test.py 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862
  1. from __future__ import absolute_import, division, print_function
  2. import gc
  3. import contextlib
  4. import datetime
  5. import functools
  6. import platform
  7. import sys
  8. import textwrap
  9. import time
  10. import weakref
  11. import warnings
  12. from tornado.concurrent import return_future, Future
  13. from tornado.escape import url_escape
  14. from tornado.httpclient import AsyncHTTPClient
  15. from tornado.ioloop import IOLoop
  16. from tornado.log import app_log
  17. from tornado import stack_context
  18. from tornado.testing import AsyncHTTPTestCase, AsyncTestCase, ExpectLog, gen_test
  19. from tornado.test.util import unittest, skipOnTravis, skipBefore33, skipBefore35, skipNotCPython, exec_test, ignore_deprecation # noqa: E501
  20. from tornado.web import Application, RequestHandler, asynchronous, HTTPError
  21. from tornado import gen
  22. try:
  23. from concurrent import futures
  24. except ImportError:
  25. futures = None
  26. try:
  27. import asyncio
  28. except ImportError:
  29. asyncio = None
  30. class GenEngineTest(AsyncTestCase):
  31. def setUp(self):
  32. self.warning_catcher = warnings.catch_warnings()
  33. self.warning_catcher.__enter__()
  34. warnings.simplefilter('ignore', DeprecationWarning)
  35. super(GenEngineTest, self).setUp()
  36. self.named_contexts = []
  37. def tearDown(self):
  38. super(GenEngineTest, self).tearDown()
  39. self.warning_catcher.__exit__(None, None, None)
  40. def named_context(self, name):
  41. @contextlib.contextmanager
  42. def context():
  43. self.named_contexts.append(name)
  44. try:
  45. yield
  46. finally:
  47. self.assertEqual(self.named_contexts.pop(), name)
  48. return context
  49. def run_gen(self, f):
  50. f()
  51. return self.wait()
  52. def delay_callback(self, iterations, callback, arg):
  53. """Runs callback(arg) after a number of IOLoop iterations."""
  54. if iterations == 0:
  55. callback(arg)
  56. else:
  57. self.io_loop.add_callback(functools.partial(
  58. self.delay_callback, iterations - 1, callback, arg))
  59. with ignore_deprecation():
  60. @return_future
  61. def async_future(self, result, callback):
  62. self.io_loop.add_callback(callback, result)
  63. @gen.coroutine
  64. def async_exception(self, e):
  65. yield gen.moment
  66. raise e
  67. def test_no_yield(self):
  68. @gen.engine
  69. def f():
  70. self.stop()
  71. self.run_gen(f)
  72. def test_inline_cb(self):
  73. @gen.engine
  74. def f():
  75. (yield gen.Callback("k1"))()
  76. res = yield gen.Wait("k1")
  77. self.assertTrue(res is None)
  78. self.stop()
  79. self.run_gen(f)
  80. def test_ioloop_cb(self):
  81. @gen.engine
  82. def f():
  83. self.io_loop.add_callback((yield gen.Callback("k1")))
  84. yield gen.Wait("k1")
  85. self.stop()
  86. self.run_gen(f)
  87. def test_exception_phase1(self):
  88. @gen.engine
  89. def f():
  90. 1 / 0
  91. self.assertRaises(ZeroDivisionError, self.run_gen, f)
  92. def test_exception_phase2(self):
  93. @gen.engine
  94. def f():
  95. self.io_loop.add_callback((yield gen.Callback("k1")))
  96. yield gen.Wait("k1")
  97. 1 / 0
  98. self.assertRaises(ZeroDivisionError, self.run_gen, f)
  99. def test_exception_in_task_phase1(self):
  100. def fail_task(callback):
  101. 1 / 0
  102. @gen.engine
  103. def f():
  104. try:
  105. yield gen.Task(fail_task)
  106. raise Exception("did not get expected exception")
  107. except ZeroDivisionError:
  108. self.stop()
  109. self.run_gen(f)
  110. def test_exception_in_task_phase2(self):
  111. # This is the case that requires the use of stack_context in gen.engine
  112. def fail_task(callback):
  113. self.io_loop.add_callback(lambda: 1 / 0)
  114. @gen.engine
  115. def f():
  116. try:
  117. yield gen.Task(fail_task)
  118. raise Exception("did not get expected exception")
  119. except ZeroDivisionError:
  120. self.stop()
  121. self.run_gen(f)
  122. def test_with_arg(self):
  123. @gen.engine
  124. def f():
  125. (yield gen.Callback("k1"))(42)
  126. res = yield gen.Wait("k1")
  127. self.assertEqual(42, res)
  128. self.stop()
  129. self.run_gen(f)
  130. def test_with_arg_tuple(self):
  131. @gen.engine
  132. def f():
  133. (yield gen.Callback((1, 2)))((3, 4))
  134. res = yield gen.Wait((1, 2))
  135. self.assertEqual((3, 4), res)
  136. self.stop()
  137. self.run_gen(f)
  138. def test_key_reuse(self):
  139. @gen.engine
  140. def f():
  141. yield gen.Callback("k1")
  142. yield gen.Callback("k1")
  143. self.stop()
  144. self.assertRaises(gen.KeyReuseError, self.run_gen, f)
  145. def test_key_reuse_tuple(self):
  146. @gen.engine
  147. def f():
  148. yield gen.Callback((1, 2))
  149. yield gen.Callback((1, 2))
  150. self.stop()
  151. self.assertRaises(gen.KeyReuseError, self.run_gen, f)
  152. def test_key_mismatch(self):
  153. @gen.engine
  154. def f():
  155. yield gen.Callback("k1")
  156. yield gen.Wait("k2")
  157. self.stop()
  158. self.assertRaises(gen.UnknownKeyError, self.run_gen, f)
  159. def test_key_mismatch_tuple(self):
  160. @gen.engine
  161. def f():
  162. yield gen.Callback((1, 2))
  163. yield gen.Wait((2, 3))
  164. self.stop()
  165. self.assertRaises(gen.UnknownKeyError, self.run_gen, f)
  166. def test_leaked_callback(self):
  167. @gen.engine
  168. def f():
  169. yield gen.Callback("k1")
  170. self.stop()
  171. self.assertRaises(gen.LeakedCallbackError, self.run_gen, f)
  172. def test_leaked_callback_tuple(self):
  173. @gen.engine
  174. def f():
  175. yield gen.Callback((1, 2))
  176. self.stop()
  177. self.assertRaises(gen.LeakedCallbackError, self.run_gen, f)
  178. def test_parallel_callback(self):
  179. @gen.engine
  180. def f():
  181. for k in range(3):
  182. self.io_loop.add_callback((yield gen.Callback(k)))
  183. yield gen.Wait(1)
  184. self.io_loop.add_callback((yield gen.Callback(3)))
  185. yield gen.Wait(0)
  186. yield gen.Wait(3)
  187. yield gen.Wait(2)
  188. self.stop()
  189. self.run_gen(f)
  190. def test_bogus_yield(self):
  191. @gen.engine
  192. def f():
  193. yield 42
  194. self.assertRaises(gen.BadYieldError, self.run_gen, f)
  195. def test_bogus_yield_tuple(self):
  196. @gen.engine
  197. def f():
  198. yield (1, 2)
  199. self.assertRaises(gen.BadYieldError, self.run_gen, f)
  200. def test_reuse(self):
  201. @gen.engine
  202. def f():
  203. self.io_loop.add_callback((yield gen.Callback(0)))
  204. yield gen.Wait(0)
  205. self.stop()
  206. self.run_gen(f)
  207. self.run_gen(f)
  208. def test_task(self):
  209. @gen.engine
  210. def f():
  211. yield gen.Task(self.io_loop.add_callback)
  212. self.stop()
  213. self.run_gen(f)
  214. def test_wait_all(self):
  215. @gen.engine
  216. def f():
  217. (yield gen.Callback("k1"))("v1")
  218. (yield gen.Callback("k2"))("v2")
  219. results = yield gen.WaitAll(["k1", "k2"])
  220. self.assertEqual(results, ["v1", "v2"])
  221. self.stop()
  222. self.run_gen(f)
  223. def test_exception_in_yield(self):
  224. @gen.engine
  225. def f():
  226. try:
  227. yield gen.Wait("k1")
  228. raise Exception("did not get expected exception")
  229. except gen.UnknownKeyError:
  230. pass
  231. self.stop()
  232. self.run_gen(f)
  233. def test_resume_after_exception_in_yield(self):
  234. @gen.engine
  235. def f():
  236. try:
  237. yield gen.Wait("k1")
  238. raise Exception("did not get expected exception")
  239. except gen.UnknownKeyError:
  240. pass
  241. (yield gen.Callback("k2"))("v2")
  242. self.assertEqual((yield gen.Wait("k2")), "v2")
  243. self.stop()
  244. self.run_gen(f)
  245. def test_orphaned_callback(self):
  246. @gen.engine
  247. def f():
  248. self.orphaned_callback = yield gen.Callback(1)
  249. try:
  250. self.run_gen(f)
  251. raise Exception("did not get expected exception")
  252. except gen.LeakedCallbackError:
  253. pass
  254. self.orphaned_callback()
  255. def test_none(self):
  256. @gen.engine
  257. def f():
  258. yield None
  259. self.stop()
  260. self.run_gen(f)
  261. def test_multi(self):
  262. @gen.engine
  263. def f():
  264. (yield gen.Callback("k1"))("v1")
  265. (yield gen.Callback("k2"))("v2")
  266. results = yield [gen.Wait("k1"), gen.Wait("k2")]
  267. self.assertEqual(results, ["v1", "v2"])
  268. self.stop()
  269. self.run_gen(f)
  270. def test_multi_dict(self):
  271. @gen.engine
  272. def f():
  273. (yield gen.Callback("k1"))("v1")
  274. (yield gen.Callback("k2"))("v2")
  275. results = yield dict(foo=gen.Wait("k1"), bar=gen.Wait("k2"))
  276. self.assertEqual(results, dict(foo="v1", bar="v2"))
  277. self.stop()
  278. self.run_gen(f)
  279. # The following tests explicitly run with both gen.Multi
  280. # and gen.multi_future (Task returns a Future, so it can be used
  281. # with either).
  282. def test_multi_yieldpoint_delayed(self):
  283. @gen.engine
  284. def f():
  285. # callbacks run at different times
  286. responses = yield gen.Multi([
  287. gen.Task(self.delay_callback, 3, arg="v1"),
  288. gen.Task(self.delay_callback, 1, arg="v2"),
  289. ])
  290. self.assertEqual(responses, ["v1", "v2"])
  291. self.stop()
  292. self.run_gen(f)
  293. def test_multi_yieldpoint_dict_delayed(self):
  294. @gen.engine
  295. def f():
  296. # callbacks run at different times
  297. responses = yield gen.Multi(dict(
  298. foo=gen.Task(self.delay_callback, 3, arg="v1"),
  299. bar=gen.Task(self.delay_callback, 1, arg="v2"),
  300. ))
  301. self.assertEqual(responses, dict(foo="v1", bar="v2"))
  302. self.stop()
  303. self.run_gen(f)
  304. def test_multi_future_delayed(self):
  305. @gen.engine
  306. def f():
  307. # callbacks run at different times
  308. responses = yield gen.multi_future([
  309. gen.Task(self.delay_callback, 3, arg="v1"),
  310. gen.Task(self.delay_callback, 1, arg="v2"),
  311. ])
  312. self.assertEqual(responses, ["v1", "v2"])
  313. self.stop()
  314. self.run_gen(f)
  315. def test_multi_future_dict_delayed(self):
  316. @gen.engine
  317. def f():
  318. # callbacks run at different times
  319. responses = yield gen.multi_future(dict(
  320. foo=gen.Task(self.delay_callback, 3, arg="v1"),
  321. bar=gen.Task(self.delay_callback, 1, arg="v2"),
  322. ))
  323. self.assertEqual(responses, dict(foo="v1", bar="v2"))
  324. self.stop()
  325. self.run_gen(f)
  326. @skipOnTravis
  327. @gen_test
  328. def test_multi_performance(self):
  329. # Yielding a list used to have quadratic performance; make
  330. # sure a large list stays reasonable. On my laptop a list of
  331. # 2000 used to take 1.8s, now it takes 0.12.
  332. start = time.time()
  333. yield [gen.Task(self.io_loop.add_callback) for i in range(2000)]
  334. end = time.time()
  335. self.assertLess(end - start, 1.0)
  336. @gen_test
  337. def test_multi_empty(self):
  338. # Empty lists or dicts should return the same type.
  339. x = yield []
  340. self.assertTrue(isinstance(x, list))
  341. y = yield {}
  342. self.assertTrue(isinstance(y, dict))
  343. @gen_test
  344. def test_multi_mixed_types(self):
  345. # A YieldPoint (Wait) and Future (Task) can be combined
  346. # (and use the YieldPoint codepath)
  347. (yield gen.Callback("k1"))("v1")
  348. responses = yield [gen.Wait("k1"),
  349. gen.Task(self.delay_callback, 3, arg="v2")]
  350. self.assertEqual(responses, ["v1", "v2"])
  351. @gen_test
  352. def test_future(self):
  353. result = yield self.async_future(1)
  354. self.assertEqual(result, 1)
  355. @gen_test
  356. def test_multi_future(self):
  357. results = yield [self.async_future(1), self.async_future(2)]
  358. self.assertEqual(results, [1, 2])
  359. @gen_test
  360. def test_multi_future_duplicate(self):
  361. f = self.async_future(2)
  362. results = yield [self.async_future(1), f, self.async_future(3), f]
  363. self.assertEqual(results, [1, 2, 3, 2])
  364. @gen_test
  365. def test_multi_dict_future(self):
  366. results = yield dict(foo=self.async_future(1), bar=self.async_future(2))
  367. self.assertEqual(results, dict(foo=1, bar=2))
  368. @gen_test
  369. def test_multi_exceptions(self):
  370. with ExpectLog(app_log, "Multiple exceptions in yield list"):
  371. with self.assertRaises(RuntimeError) as cm:
  372. yield gen.Multi([self.async_exception(RuntimeError("error 1")),
  373. self.async_exception(RuntimeError("error 2"))])
  374. self.assertEqual(str(cm.exception), "error 1")
  375. # With only one exception, no error is logged.
  376. with self.assertRaises(RuntimeError):
  377. yield gen.Multi([self.async_exception(RuntimeError("error 1")),
  378. self.async_future(2)])
  379. # Exception logging may be explicitly quieted.
  380. with self.assertRaises(RuntimeError):
  381. yield gen.Multi([self.async_exception(RuntimeError("error 1")),
  382. self.async_exception(RuntimeError("error 2"))],
  383. quiet_exceptions=RuntimeError)
  384. @gen_test
  385. def test_multi_future_exceptions(self):
  386. with ExpectLog(app_log, "Multiple exceptions in yield list"):
  387. with self.assertRaises(RuntimeError) as cm:
  388. yield [self.async_exception(RuntimeError("error 1")),
  389. self.async_exception(RuntimeError("error 2"))]
  390. self.assertEqual(str(cm.exception), "error 1")
  391. # With only one exception, no error is logged.
  392. with self.assertRaises(RuntimeError):
  393. yield [self.async_exception(RuntimeError("error 1")),
  394. self.async_future(2)]
  395. # Exception logging may be explicitly quieted.
  396. with self.assertRaises(RuntimeError):
  397. yield gen.multi_future(
  398. [self.async_exception(RuntimeError("error 1")),
  399. self.async_exception(RuntimeError("error 2"))],
  400. quiet_exceptions=RuntimeError)
  401. def test_arguments(self):
  402. @gen.engine
  403. def f():
  404. (yield gen.Callback("noargs"))()
  405. self.assertEqual((yield gen.Wait("noargs")), None)
  406. (yield gen.Callback("1arg"))(42)
  407. self.assertEqual((yield gen.Wait("1arg")), 42)
  408. (yield gen.Callback("kwargs"))(value=42)
  409. result = yield gen.Wait("kwargs")
  410. self.assertTrue(isinstance(result, gen.Arguments))
  411. self.assertEqual(((), dict(value=42)), result)
  412. self.assertEqual(dict(value=42), result.kwargs)
  413. (yield gen.Callback("2args"))(42, 43)
  414. result = yield gen.Wait("2args")
  415. self.assertTrue(isinstance(result, gen.Arguments))
  416. self.assertEqual(((42, 43), {}), result)
  417. self.assertEqual((42, 43), result.args)
  418. def task_func(callback):
  419. callback(None, error="foo")
  420. result = yield gen.Task(task_func)
  421. self.assertTrue(isinstance(result, gen.Arguments))
  422. self.assertEqual(((None,), dict(error="foo")), result)
  423. self.stop()
  424. self.run_gen(f)
  425. def test_stack_context_leak(self):
  426. # regression test: repeated invocations of a gen-based
  427. # function should not result in accumulated stack_contexts
  428. def _stack_depth():
  429. head = stack_context._state.contexts[1]
  430. length = 0
  431. while head is not None:
  432. length += 1
  433. head = head.old_contexts[1]
  434. return length
  435. @gen.engine
  436. def inner(callback):
  437. yield gen.Task(self.io_loop.add_callback)
  438. callback()
  439. @gen.engine
  440. def outer():
  441. for i in range(10):
  442. yield gen.Task(inner)
  443. stack_increase = _stack_depth() - initial_stack_depth
  444. self.assertTrue(stack_increase <= 2)
  445. self.stop()
  446. initial_stack_depth = _stack_depth()
  447. self.run_gen(outer)
  448. def test_stack_context_leak_exception(self):
  449. # same as previous, but with a function that exits with an exception
  450. @gen.engine
  451. def inner(callback):
  452. yield gen.Task(self.io_loop.add_callback)
  453. 1 / 0
  454. @gen.engine
  455. def outer():
  456. for i in range(10):
  457. try:
  458. yield gen.Task(inner)
  459. except ZeroDivisionError:
  460. pass
  461. stack_increase = len(stack_context._state.contexts) - initial_stack_depth
  462. self.assertTrue(stack_increase <= 2)
  463. self.stop()
  464. initial_stack_depth = len(stack_context._state.contexts)
  465. self.run_gen(outer)
  466. def function_with_stack_context(self, callback):
  467. # Technically this function should stack_context.wrap its callback
  468. # upon entry. However, it is very common for this step to be
  469. # omitted.
  470. def step2():
  471. self.assertEqual(self.named_contexts, ['a'])
  472. self.io_loop.add_callback(callback)
  473. with stack_context.StackContext(self.named_context('a')):
  474. self.io_loop.add_callback(step2)
  475. @gen_test
  476. def test_wait_transfer_stack_context(self):
  477. # Wait should not pick up contexts from where callback was invoked,
  478. # even if that function improperly fails to wrap its callback.
  479. cb = yield gen.Callback('k1')
  480. self.function_with_stack_context(cb)
  481. self.assertEqual(self.named_contexts, [])
  482. yield gen.Wait('k1')
  483. self.assertEqual(self.named_contexts, [])
  484. @gen_test
  485. def test_task_transfer_stack_context(self):
  486. yield gen.Task(self.function_with_stack_context)
  487. self.assertEqual(self.named_contexts, [])
  488. def test_raise_after_stop(self):
  489. # This pattern will be used in the following tests so make sure
  490. # the exception propagates as expected.
  491. @gen.engine
  492. def f():
  493. self.stop()
  494. 1 / 0
  495. with self.assertRaises(ZeroDivisionError):
  496. self.run_gen(f)
  497. def test_sync_raise_return(self):
  498. # gen.Return is allowed in @gen.engine, but it may not be used
  499. # to return a value.
  500. @gen.engine
  501. def f():
  502. self.stop(42)
  503. raise gen.Return()
  504. result = self.run_gen(f)
  505. self.assertEqual(result, 42)
  506. def test_async_raise_return(self):
  507. @gen.engine
  508. def f():
  509. yield gen.Task(self.io_loop.add_callback)
  510. self.stop(42)
  511. raise gen.Return()
  512. result = self.run_gen(f)
  513. self.assertEqual(result, 42)
  514. def test_sync_raise_return_value(self):
  515. @gen.engine
  516. def f():
  517. raise gen.Return(42)
  518. with self.assertRaises(gen.ReturnValueIgnoredError):
  519. self.run_gen(f)
  520. def test_sync_raise_return_value_tuple(self):
  521. @gen.engine
  522. def f():
  523. raise gen.Return((1, 2))
  524. with self.assertRaises(gen.ReturnValueIgnoredError):
  525. self.run_gen(f)
  526. def test_async_raise_return_value(self):
  527. @gen.engine
  528. def f():
  529. yield gen.Task(self.io_loop.add_callback)
  530. raise gen.Return(42)
  531. with self.assertRaises(gen.ReturnValueIgnoredError):
  532. self.run_gen(f)
  533. def test_async_raise_return_value_tuple(self):
  534. @gen.engine
  535. def f():
  536. yield gen.Task(self.io_loop.add_callback)
  537. raise gen.Return((1, 2))
  538. with self.assertRaises(gen.ReturnValueIgnoredError):
  539. self.run_gen(f)
  540. def test_return_value(self):
  541. # It is an error to apply @gen.engine to a function that returns
  542. # a value.
  543. @gen.engine
  544. def f():
  545. return 42
  546. with self.assertRaises(gen.ReturnValueIgnoredError):
  547. self.run_gen(f)
  548. def test_return_value_tuple(self):
  549. # It is an error to apply @gen.engine to a function that returns
  550. # a value.
  551. @gen.engine
  552. def f():
  553. return (1, 2)
  554. with self.assertRaises(gen.ReturnValueIgnoredError):
  555. self.run_gen(f)
  556. @skipNotCPython
  557. def test_task_refcounting(self):
  558. # On CPython, tasks and their arguments should be released immediately
  559. # without waiting for garbage collection.
  560. @gen.engine
  561. def f():
  562. class Foo(object):
  563. pass
  564. arg = Foo()
  565. self.arg_ref = weakref.ref(arg)
  566. task = gen.Task(self.io_loop.add_callback, arg=arg)
  567. self.task_ref = weakref.ref(task)
  568. yield task
  569. self.stop()
  570. self.run_gen(f)
  571. self.assertIs(self.arg_ref(), None)
  572. self.assertIs(self.task_ref(), None)
  573. # GenBasicTest duplicates the non-deprecated portions of GenEngineTest
  574. # with gen.coroutine to ensure we don't lose coverage when gen.engine
  575. # goes away.
  576. class GenBasicTest(AsyncTestCase):
  577. @gen.coroutine
  578. def delay(self, iterations, arg):
  579. """Returns arg after a number of IOLoop iterations."""
  580. for i in range(iterations):
  581. yield gen.moment
  582. raise gen.Return(arg)
  583. with ignore_deprecation():
  584. @return_future
  585. def async_future(self, result, callback):
  586. self.io_loop.add_callback(callback, result)
  587. @gen.coroutine
  588. def async_exception(self, e):
  589. yield gen.moment
  590. raise e
  591. @gen.coroutine
  592. def add_one_async(self, x):
  593. yield gen.moment
  594. raise gen.Return(x + 1)
  595. def test_no_yield(self):
  596. @gen.coroutine
  597. def f():
  598. pass
  599. self.io_loop.run_sync(f)
  600. def test_exception_phase1(self):
  601. @gen.coroutine
  602. def f():
  603. 1 / 0
  604. self.assertRaises(ZeroDivisionError, self.io_loop.run_sync, f)
  605. def test_exception_phase2(self):
  606. @gen.coroutine
  607. def f():
  608. yield gen.moment
  609. 1 / 0
  610. self.assertRaises(ZeroDivisionError, self.io_loop.run_sync, f)
  611. def test_bogus_yield(self):
  612. @gen.coroutine
  613. def f():
  614. yield 42
  615. self.assertRaises(gen.BadYieldError, self.io_loop.run_sync, f)
  616. def test_bogus_yield_tuple(self):
  617. @gen.coroutine
  618. def f():
  619. yield (1, 2)
  620. self.assertRaises(gen.BadYieldError, self.io_loop.run_sync, f)
  621. def test_reuse(self):
  622. @gen.coroutine
  623. def f():
  624. yield gen.moment
  625. self.io_loop.run_sync(f)
  626. self.io_loop.run_sync(f)
  627. def test_none(self):
  628. @gen.coroutine
  629. def f():
  630. yield None
  631. self.io_loop.run_sync(f)
  632. def test_multi(self):
  633. @gen.coroutine
  634. def f():
  635. results = yield [self.add_one_async(1), self.add_one_async(2)]
  636. self.assertEqual(results, [2, 3])
  637. self.io_loop.run_sync(f)
  638. def test_multi_dict(self):
  639. @gen.coroutine
  640. def f():
  641. results = yield dict(foo=self.add_one_async(1), bar=self.add_one_async(2))
  642. self.assertEqual(results, dict(foo=2, bar=3))
  643. self.io_loop.run_sync(f)
  644. def test_multi_delayed(self):
  645. @gen.coroutine
  646. def f():
  647. # callbacks run at different times
  648. responses = yield gen.multi_future([
  649. self.delay(3, "v1"),
  650. self.delay(1, "v2"),
  651. ])
  652. self.assertEqual(responses, ["v1", "v2"])
  653. self.io_loop.run_sync(f)
  654. def test_multi_dict_delayed(self):
  655. @gen.coroutine
  656. def f():
  657. # callbacks run at different times
  658. responses = yield gen.multi_future(dict(
  659. foo=self.delay(3, "v1"),
  660. bar=self.delay(1, "v2"),
  661. ))
  662. self.assertEqual(responses, dict(foo="v1", bar="v2"))
  663. self.io_loop.run_sync(f)
  664. @skipOnTravis
  665. @gen_test
  666. def test_multi_performance(self):
  667. # Yielding a list used to have quadratic performance; make
  668. # sure a large list stays reasonable. On my laptop a list of
  669. # 2000 used to take 1.8s, now it takes 0.12.
  670. start = time.time()
  671. yield [gen.moment for i in range(2000)]
  672. end = time.time()
  673. self.assertLess(end - start, 1.0)
  674. @gen_test
  675. def test_multi_empty(self):
  676. # Empty lists or dicts should return the same type.
  677. x = yield []
  678. self.assertTrue(isinstance(x, list))
  679. y = yield {}
  680. self.assertTrue(isinstance(y, dict))
  681. @gen_test
  682. def test_future(self):
  683. result = yield self.async_future(1)
  684. self.assertEqual(result, 1)
  685. @gen_test
  686. def test_multi_future(self):
  687. results = yield [self.async_future(1), self.async_future(2)]
  688. self.assertEqual(results, [1, 2])
  689. @gen_test
  690. def test_multi_future_duplicate(self):
  691. f = self.async_future(2)
  692. results = yield [self.async_future(1), f, self.async_future(3), f]
  693. self.assertEqual(results, [1, 2, 3, 2])
  694. @gen_test
  695. def test_multi_dict_future(self):
  696. results = yield dict(foo=self.async_future(1), bar=self.async_future(2))
  697. self.assertEqual(results, dict(foo=1, bar=2))
  698. @gen_test
  699. def test_multi_exceptions(self):
  700. with ExpectLog(app_log, "Multiple exceptions in yield list"):
  701. with self.assertRaises(RuntimeError) as cm:
  702. yield gen.Multi([self.async_exception(RuntimeError("error 1")),
  703. self.async_exception(RuntimeError("error 2"))])
  704. self.assertEqual(str(cm.exception), "error 1")
  705. # With only one exception, no error is logged.
  706. with self.assertRaises(RuntimeError):
  707. yield gen.Multi([self.async_exception(RuntimeError("error 1")),
  708. self.async_future(2)])
  709. # Exception logging may be explicitly quieted.
  710. with self.assertRaises(RuntimeError):
  711. yield gen.Multi([self.async_exception(RuntimeError("error 1")),
  712. self.async_exception(RuntimeError("error 2"))],
  713. quiet_exceptions=RuntimeError)
  714. @gen_test
  715. def test_multi_future_exceptions(self):
  716. with ExpectLog(app_log, "Multiple exceptions in yield list"):
  717. with self.assertRaises(RuntimeError) as cm:
  718. yield [self.async_exception(RuntimeError("error 1")),
  719. self.async_exception(RuntimeError("error 2"))]
  720. self.assertEqual(str(cm.exception), "error 1")
  721. # With only one exception, no error is logged.
  722. with self.assertRaises(RuntimeError):
  723. yield [self.async_exception(RuntimeError("error 1")),
  724. self.async_future(2)]
  725. # Exception logging may be explicitly quieted.
  726. with self.assertRaises(RuntimeError):
  727. yield gen.multi_future(
  728. [self.async_exception(RuntimeError("error 1")),
  729. self.async_exception(RuntimeError("error 2"))],
  730. quiet_exceptions=RuntimeError)
  731. def test_sync_raise_return(self):
  732. @gen.coroutine
  733. def f():
  734. raise gen.Return()
  735. self.io_loop.run_sync(f)
  736. def test_async_raise_return(self):
  737. @gen.coroutine
  738. def f():
  739. yield gen.moment
  740. raise gen.Return()
  741. self.io_loop.run_sync(f)
  742. def test_sync_raise_return_value(self):
  743. @gen.coroutine
  744. def f():
  745. raise gen.Return(42)
  746. self.assertEqual(42, self.io_loop.run_sync(f))
  747. def test_sync_raise_return_value_tuple(self):
  748. @gen.coroutine
  749. def f():
  750. raise gen.Return((1, 2))
  751. self.assertEqual((1, 2), self.io_loop.run_sync(f))
  752. def test_async_raise_return_value(self):
  753. @gen.coroutine
  754. def f():
  755. yield gen.moment
  756. raise gen.Return(42)
  757. self.assertEqual(42, self.io_loop.run_sync(f))
  758. def test_async_raise_return_value_tuple(self):
  759. @gen.coroutine
  760. def f():
  761. yield gen.moment
  762. raise gen.Return((1, 2))
  763. self.assertEqual((1, 2), self.io_loop.run_sync(f))
  764. class GenCoroutineTest(AsyncTestCase):
  765. def setUp(self):
  766. # Stray StopIteration exceptions can lead to tests exiting prematurely,
  767. # so we need explicit checks here to make sure the tests run all
  768. # the way through.
  769. self.finished = False
  770. super(GenCoroutineTest, self).setUp()
  771. def tearDown(self):
  772. super(GenCoroutineTest, self).tearDown()
  773. assert self.finished
  774. def test_attributes(self):
  775. self.finished = True
  776. def f():
  777. yield gen.moment
  778. coro = gen.coroutine(f)
  779. self.assertEqual(coro.__name__, f.__name__)
  780. self.assertEqual(coro.__module__, f.__module__)
  781. self.assertIs(coro.__wrapped__, f)
  782. def test_is_coroutine_function(self):
  783. self.finished = True
  784. def f():
  785. yield gen.moment
  786. coro = gen.coroutine(f)
  787. self.assertFalse(gen.is_coroutine_function(f))
  788. self.assertTrue(gen.is_coroutine_function(coro))
  789. self.assertFalse(gen.is_coroutine_function(coro()))
  790. @gen_test
  791. def test_sync_gen_return(self):
  792. @gen.coroutine
  793. def f():
  794. raise gen.Return(42)
  795. result = yield f()
  796. self.assertEqual(result, 42)
  797. self.finished = True
  798. @gen_test
  799. def test_async_gen_return(self):
  800. @gen.coroutine
  801. def f():
  802. yield gen.moment
  803. raise gen.Return(42)
  804. result = yield f()
  805. self.assertEqual(result, 42)
  806. self.finished = True
  807. @gen_test
  808. def test_sync_return(self):
  809. @gen.coroutine
  810. def f():
  811. return 42
  812. result = yield f()
  813. self.assertEqual(result, 42)
  814. self.finished = True
  815. @skipBefore33
  816. @gen_test
  817. def test_async_return(self):
  818. namespace = exec_test(globals(), locals(), """
  819. @gen.coroutine
  820. def f():
  821. yield gen.moment
  822. return 42
  823. """)
  824. result = yield namespace['f']()
  825. self.assertEqual(result, 42)
  826. self.finished = True
  827. @skipBefore33
  828. @gen_test
  829. def test_async_early_return(self):
  830. # A yield statement exists but is not executed, which means
  831. # this function "returns" via an exception. This exception
  832. # doesn't happen before the exception handling is set up.
  833. namespace = exec_test(globals(), locals(), """
  834. @gen.coroutine
  835. def f():
  836. if True:
  837. return 42
  838. yield gen.Task(self.io_loop.add_callback)
  839. """)
  840. result = yield namespace['f']()
  841. self.assertEqual(result, 42)
  842. self.finished = True
  843. @skipBefore35
  844. @gen_test
  845. def test_async_await(self):
  846. @gen.coroutine
  847. def f1():
  848. yield gen.moment
  849. raise gen.Return(42)
  850. # This test verifies that an async function can await a
  851. # yield-based gen.coroutine, and that a gen.coroutine
  852. # (the test method itself) can yield an async function.
  853. namespace = exec_test(globals(), locals(), """
  854. async def f2():
  855. result = await f1()
  856. return result
  857. """)
  858. result = yield namespace['f2']()
  859. self.assertEqual(result, 42)
  860. self.finished = True
  861. @skipBefore35
  862. @gen_test
  863. def test_asyncio_sleep_zero(self):
  864. # asyncio.sleep(0) turns into a special case (equivalent to
  865. # `yield None`)
  866. namespace = exec_test(globals(), locals(), """
  867. async def f():
  868. import asyncio
  869. await asyncio.sleep(0)
  870. return 42
  871. """)
  872. result = yield namespace['f']()
  873. self.assertEqual(result, 42)
  874. self.finished = True
  875. @skipBefore35
  876. @gen_test
  877. def test_async_await_mixed_multi_native_future(self):
  878. @gen.coroutine
  879. def f1():
  880. yield gen.moment
  881. namespace = exec_test(globals(), locals(), """
  882. async def f2():
  883. await f1()
  884. return 42
  885. """)
  886. @gen.coroutine
  887. def f3():
  888. yield gen.moment
  889. raise gen.Return(43)
  890. results = yield [namespace['f2'](), f3()]
  891. self.assertEqual(results, [42, 43])
  892. self.finished = True
  893. @skipBefore35
  894. @gen_test
  895. def test_async_await_mixed_multi_native_yieldpoint(self):
  896. namespace = exec_test(globals(), locals(), """
  897. async def f1():
  898. await gen.Task(self.io_loop.add_callback)
  899. return 42
  900. """)
  901. @gen.coroutine
  902. def f2():
  903. yield gen.Task(self.io_loop.add_callback)
  904. raise gen.Return(43)
  905. with ignore_deprecation():
  906. f2(callback=(yield gen.Callback('cb')))
  907. results = yield [namespace['f1'](), gen.Wait('cb')]
  908. self.assertEqual(results, [42, 43])
  909. self.finished = True
  910. @skipBefore35
  911. @gen_test
  912. def test_async_with_timeout(self):
  913. namespace = exec_test(globals(), locals(), """
  914. async def f1():
  915. return 42
  916. """)
  917. result = yield gen.with_timeout(datetime.timedelta(hours=1),
  918. namespace['f1']())
  919. self.assertEqual(result, 42)
  920. self.finished = True
  921. @gen_test
  922. def test_sync_return_no_value(self):
  923. @gen.coroutine
  924. def f():
  925. return
  926. result = yield f()
  927. self.assertEqual(result, None)
  928. self.finished = True
  929. @gen_test
  930. def test_async_return_no_value(self):
  931. # Without a return value we don't need python 3.3.
  932. @gen.coroutine
  933. def f():
  934. yield gen.moment
  935. return
  936. result = yield f()
  937. self.assertEqual(result, None)
  938. self.finished = True
  939. @gen_test
  940. def test_sync_raise(self):
  941. @gen.coroutine
  942. def f():
  943. 1 / 0
  944. # The exception is raised when the future is yielded
  945. # (or equivalently when its result method is called),
  946. # not when the function itself is called).
  947. future = f()
  948. with self.assertRaises(ZeroDivisionError):
  949. yield future
  950. self.finished = True
  951. @gen_test
  952. def test_async_raise(self):
  953. @gen.coroutine
  954. def f():
  955. yield gen.moment
  956. 1 / 0
  957. future = f()
  958. with self.assertRaises(ZeroDivisionError):
  959. yield future
  960. self.finished = True
  961. @gen_test
  962. def test_pass_callback(self):
  963. with ignore_deprecation():
  964. @gen.coroutine
  965. def f():
  966. raise gen.Return(42)
  967. result = yield gen.Task(f)
  968. self.assertEqual(result, 42)
  969. self.finished = True
  970. @gen_test
  971. def test_replace_yieldpoint_exception(self):
  972. # Test exception handling: a coroutine can catch one exception
  973. # raised by a yield point and raise a different one.
  974. @gen.coroutine
  975. def f1():
  976. 1 / 0
  977. @gen.coroutine
  978. def f2():
  979. try:
  980. yield f1()
  981. except ZeroDivisionError:
  982. raise KeyError()
  983. future = f2()
  984. with self.assertRaises(KeyError):
  985. yield future
  986. self.finished = True
  987. @gen_test
  988. def test_swallow_yieldpoint_exception(self):
  989. # Test exception handling: a coroutine can catch an exception
  990. # raised by a yield point and not raise a different one.
  991. @gen.coroutine
  992. def f1():
  993. 1 / 0
  994. @gen.coroutine
  995. def f2():
  996. try:
  997. yield f1()
  998. except ZeroDivisionError:
  999. raise gen.Return(42)
  1000. result = yield f2()
  1001. self.assertEqual(result, 42)
  1002. self.finished = True
  1003. @gen_test
  1004. def test_replace_context_exception(self):
  1005. with ignore_deprecation():
  1006. # Test exception handling: exceptions thrown into the stack context
  1007. # can be caught and replaced.
  1008. # Note that this test and the following are for behavior that is
  1009. # not really supported any more: coroutines no longer create a
  1010. # stack context automatically; but one is created after the first
  1011. # YieldPoint (i.e. not a Future).
  1012. @gen.coroutine
  1013. def f2():
  1014. (yield gen.Callback(1))()
  1015. yield gen.Wait(1)
  1016. self.io_loop.add_callback(lambda: 1 / 0)
  1017. try:
  1018. yield gen.Task(self.io_loop.add_timeout,
  1019. self.io_loop.time() + 10)
  1020. except ZeroDivisionError:
  1021. raise KeyError()
  1022. future = f2()
  1023. with self.assertRaises(KeyError):
  1024. yield future
  1025. self.finished = True
  1026. @gen_test
  1027. def test_swallow_context_exception(self):
  1028. with ignore_deprecation():
  1029. # Test exception handling: exceptions thrown into the stack context
  1030. # can be caught and ignored.
  1031. @gen.coroutine
  1032. def f2():
  1033. (yield gen.Callback(1))()
  1034. yield gen.Wait(1)
  1035. self.io_loop.add_callback(lambda: 1 / 0)
  1036. try:
  1037. yield gen.Task(self.io_loop.add_timeout,
  1038. self.io_loop.time() + 10)
  1039. except ZeroDivisionError:
  1040. raise gen.Return(42)
  1041. result = yield f2()
  1042. self.assertEqual(result, 42)
  1043. self.finished = True
  1044. @gen_test
  1045. def test_moment(self):
  1046. calls = []
  1047. @gen.coroutine
  1048. def f(name, yieldable):
  1049. for i in range(5):
  1050. calls.append(name)
  1051. yield yieldable
  1052. # First, confirm the behavior without moment: each coroutine
  1053. # monopolizes the event loop until it finishes.
  1054. immediate = Future()
  1055. immediate.set_result(None)
  1056. yield [f('a', immediate), f('b', immediate)]
  1057. self.assertEqual(''.join(calls), 'aaaaabbbbb')
  1058. # With moment, they take turns.
  1059. calls = []
  1060. yield [f('a', gen.moment), f('b', gen.moment)]
  1061. self.assertEqual(''.join(calls), 'ababababab')
  1062. self.finished = True
  1063. calls = []
  1064. yield [f('a', gen.moment), f('b', immediate)]
  1065. self.assertEqual(''.join(calls), 'abbbbbaaaa')
  1066. @gen_test
  1067. def test_sleep(self):
  1068. yield gen.sleep(0.01)
  1069. self.finished = True
  1070. @skipBefore33
  1071. @gen_test
  1072. def test_py3_leak_exception_context(self):
  1073. class LeakedException(Exception):
  1074. pass
  1075. @gen.coroutine
  1076. def inner(iteration):
  1077. raise LeakedException(iteration)
  1078. try:
  1079. yield inner(1)
  1080. except LeakedException as e:
  1081. self.assertEqual(str(e), "1")
  1082. self.assertIsNone(e.__context__)
  1083. try:
  1084. yield inner(2)
  1085. except LeakedException as e:
  1086. self.assertEqual(str(e), "2")
  1087. self.assertIsNone(e.__context__)
  1088. self.finished = True
  1089. @skipNotCPython
  1090. @unittest.skipIf((3,) < sys.version_info < (3, 6),
  1091. "asyncio.Future has reference cycles")
  1092. def test_coroutine_refcounting(self):
  1093. # On CPython, tasks and their arguments should be released immediately
  1094. # without waiting for garbage collection.
  1095. @gen.coroutine
  1096. def inner():
  1097. class Foo(object):
  1098. pass
  1099. local_var = Foo()
  1100. self.local_ref = weakref.ref(local_var)
  1101. yield gen.coroutine(lambda: None)()
  1102. raise ValueError('Some error')
  1103. @gen.coroutine
  1104. def inner2():
  1105. try:
  1106. yield inner()
  1107. except ValueError:
  1108. pass
  1109. self.io_loop.run_sync(inner2, timeout=3)
  1110. self.assertIs(self.local_ref(), None)
  1111. self.finished = True
  1112. @unittest.skipIf(sys.version_info < (3,),
  1113. "test only relevant with asyncio Futures")
  1114. def test_asyncio_future_debug_info(self):
  1115. self.finished = True
  1116. # Enable debug mode
  1117. asyncio_loop = asyncio.get_event_loop()
  1118. self.addCleanup(asyncio_loop.set_debug, asyncio_loop.get_debug())
  1119. asyncio_loop.set_debug(True)
  1120. def f():
  1121. yield gen.moment
  1122. coro = gen.coroutine(f)()
  1123. self.assertIsInstance(coro, asyncio.Future)
  1124. # We expect the coroutine repr() to show the place where
  1125. # it was instantiated
  1126. expected = ("created at %s:%d"
  1127. % (__file__, f.__code__.co_firstlineno + 3))
  1128. actual = repr(coro)
  1129. self.assertIn(expected, actual)
  1130. @unittest.skipIf(asyncio is None, "asyncio module not present")
  1131. @gen_test
  1132. def test_asyncio_gather(self):
  1133. # This demonstrates that tornado coroutines can be understood
  1134. # by asyncio (This failed prior to Tornado 5.0).
  1135. @gen.coroutine
  1136. def f():
  1137. yield gen.moment
  1138. raise gen.Return(1)
  1139. ret = yield asyncio.gather(f(), f())
  1140. self.assertEqual(ret, [1, 1])
  1141. self.finished = True
  1142. class GenSequenceHandler(RequestHandler):
  1143. with ignore_deprecation():
  1144. @asynchronous
  1145. @gen.engine
  1146. def get(self):
  1147. # The outer ignore_deprecation applies at definition time.
  1148. # We need another for serving time.
  1149. with ignore_deprecation():
  1150. self.io_loop = self.request.connection.stream.io_loop
  1151. self.io_loop.add_callback((yield gen.Callback("k1")))
  1152. yield gen.Wait("k1")
  1153. self.write("1")
  1154. self.io_loop.add_callback((yield gen.Callback("k2")))
  1155. yield gen.Wait("k2")
  1156. self.write("2")
  1157. # reuse an old key
  1158. self.io_loop.add_callback((yield gen.Callback("k1")))
  1159. yield gen.Wait("k1")
  1160. self.finish("3")
  1161. class GenCoroutineSequenceHandler(RequestHandler):
  1162. @gen.coroutine
  1163. def get(self):
  1164. yield gen.moment
  1165. self.write("1")
  1166. yield gen.moment
  1167. self.write("2")
  1168. yield gen.moment
  1169. self.finish("3")
  1170. class GenCoroutineUnfinishedSequenceHandler(RequestHandler):
  1171. @gen.coroutine
  1172. def get(self):
  1173. yield gen.moment
  1174. self.write("1")
  1175. yield gen.moment
  1176. self.write("2")
  1177. yield gen.moment
  1178. # just write, don't finish
  1179. self.write("3")
  1180. class GenTaskHandler(RequestHandler):
  1181. @gen.coroutine
  1182. def get(self):
  1183. client = AsyncHTTPClient()
  1184. with ignore_deprecation():
  1185. response = yield gen.Task(client.fetch, self.get_argument('url'))
  1186. response.rethrow()
  1187. self.finish(b"got response: " + response.body)
  1188. class GenExceptionHandler(RequestHandler):
  1189. with ignore_deprecation():
  1190. @asynchronous
  1191. @gen.engine
  1192. def get(self):
  1193. # This test depends on the order of the two decorators.
  1194. io_loop = self.request.connection.stream.io_loop
  1195. yield gen.Task(io_loop.add_callback)
  1196. raise Exception("oops")
  1197. class GenCoroutineExceptionHandler(RequestHandler):
  1198. @gen.coroutine
  1199. def get(self):
  1200. # This test depends on the order of the two decorators.
  1201. io_loop = self.request.connection.stream.io_loop
  1202. yield gen.Task(io_loop.add_callback)
  1203. raise Exception("oops")
  1204. class GenYieldExceptionHandler(RequestHandler):
  1205. @gen.coroutine
  1206. def get(self):
  1207. io_loop = self.request.connection.stream.io_loop
  1208. # Test the interaction of the two stack_contexts.
  1209. with ignore_deprecation():
  1210. def fail_task(callback):
  1211. io_loop.add_callback(lambda: 1 / 0)
  1212. try:
  1213. yield gen.Task(fail_task)
  1214. raise Exception("did not get expected exception")
  1215. except ZeroDivisionError:
  1216. self.finish('ok')
  1217. # "Undecorated" here refers to the absence of @asynchronous.
  1218. class UndecoratedCoroutinesHandler(RequestHandler):
  1219. @gen.coroutine
  1220. def prepare(self):
  1221. self.chunks = []
  1222. yield gen.moment
  1223. self.chunks.append('1')
  1224. @gen.coroutine
  1225. def get(self):
  1226. self.chunks.append('2')
  1227. yield gen.moment
  1228. self.chunks.append('3')
  1229. yield gen.moment
  1230. self.write(''.join(self.chunks))
  1231. class AsyncPrepareErrorHandler(RequestHandler):
  1232. @gen.coroutine
  1233. def prepare(self):
  1234. yield gen.moment
  1235. raise HTTPError(403)
  1236. def get(self):
  1237. self.finish('ok')
  1238. class NativeCoroutineHandler(RequestHandler):
  1239. if sys.version_info > (3, 5):
  1240. exec(textwrap.dedent("""
  1241. async def get(self):
  1242. import asyncio
  1243. await asyncio.sleep(0)
  1244. self.write("ok")
  1245. """))
  1246. class GenWebTest(AsyncHTTPTestCase):
  1247. def get_app(self):
  1248. return Application([
  1249. ('/sequence', GenSequenceHandler),
  1250. ('/coroutine_sequence', GenCoroutineSequenceHandler),
  1251. ('/coroutine_unfinished_sequence',
  1252. GenCoroutineUnfinishedSequenceHandler),
  1253. ('/task', GenTaskHandler),
  1254. ('/exception', GenExceptionHandler),
  1255. ('/coroutine_exception', GenCoroutineExceptionHandler),
  1256. ('/yield_exception', GenYieldExceptionHandler),
  1257. ('/undecorated_coroutine', UndecoratedCoroutinesHandler),
  1258. ('/async_prepare_error', AsyncPrepareErrorHandler),
  1259. ('/native_coroutine', NativeCoroutineHandler),
  1260. ])
  1261. def test_sequence_handler(self):
  1262. response = self.fetch('/sequence')
  1263. self.assertEqual(response.body, b"123")
  1264. def test_coroutine_sequence_handler(self):
  1265. response = self.fetch('/coroutine_sequence')
  1266. self.assertEqual(response.body, b"123")
  1267. def test_coroutine_unfinished_sequence_handler(self):
  1268. response = self.fetch('/coroutine_unfinished_sequence')
  1269. self.assertEqual(response.body, b"123")
  1270. def test_task_handler(self):
  1271. response = self.fetch('/task?url=%s' % url_escape(self.get_url('/sequence')))
  1272. self.assertEqual(response.body, b"got response: 123")
  1273. def test_exception_handler(self):
  1274. # Make sure we get an error and not a timeout
  1275. with ExpectLog(app_log, "Uncaught exception GET /exception"):
  1276. response = self.fetch('/exception')
  1277. self.assertEqual(500, response.code)
  1278. def test_coroutine_exception_handler(self):
  1279. # Make sure we get an error and not a timeout
  1280. with ExpectLog(app_log, "Uncaught exception GET /coroutine_exception"):
  1281. response = self.fetch('/coroutine_exception')
  1282. self.assertEqual(500, response.code)
  1283. def test_yield_exception_handler(self):
  1284. response = self.fetch('/yield_exception')
  1285. self.assertEqual(response.body, b'ok')
  1286. def test_undecorated_coroutines(self):
  1287. response = self.fetch('/undecorated_coroutine')
  1288. self.assertEqual(response.body, b'123')
  1289. def test_async_prepare_error_handler(self):
  1290. response = self.fetch('/async_prepare_error')
  1291. self.assertEqual(response.code, 403)
  1292. @skipBefore35
  1293. def test_native_coroutine_handler(self):
  1294. response = self.fetch('/native_coroutine')
  1295. self.assertEqual(response.code, 200)
  1296. self.assertEqual(response.body, b'ok')
  1297. class WithTimeoutTest(AsyncTestCase):
  1298. @gen_test
  1299. def test_timeout(self):
  1300. with self.assertRaises(gen.TimeoutError):
  1301. yield gen.with_timeout(datetime.timedelta(seconds=0.1),
  1302. Future())
  1303. @gen_test
  1304. def test_completes_before_timeout(self):
  1305. future = Future()
  1306. self.io_loop.add_timeout(datetime.timedelta(seconds=0.1),
  1307. lambda: future.set_result('asdf'))
  1308. result = yield gen.with_timeout(datetime.timedelta(seconds=3600),
  1309. future)
  1310. self.assertEqual(result, 'asdf')
  1311. @gen_test
  1312. def test_fails_before_timeout(self):
  1313. future = Future()
  1314. self.io_loop.add_timeout(
  1315. datetime.timedelta(seconds=0.1),
  1316. lambda: future.set_exception(ZeroDivisionError()))
  1317. with self.assertRaises(ZeroDivisionError):
  1318. yield gen.with_timeout(datetime.timedelta(seconds=3600),
  1319. future)
  1320. @gen_test
  1321. def test_already_resolved(self):
  1322. future = Future()
  1323. future.set_result('asdf')
  1324. result = yield gen.with_timeout(datetime.timedelta(seconds=3600),
  1325. future)
  1326. self.assertEqual(result, 'asdf')
  1327. @unittest.skipIf(futures is None, 'futures module not present')
  1328. @gen_test
  1329. def test_timeout_concurrent_future(self):
  1330. # A concurrent future that does not resolve before the timeout.
  1331. with futures.ThreadPoolExecutor(1) as executor:
  1332. with self.assertRaises(gen.TimeoutError):
  1333. yield gen.with_timeout(self.io_loop.time(),
  1334. executor.submit(time.sleep, 0.1))
  1335. @unittest.skipIf(futures is None, 'futures module not present')
  1336. @gen_test
  1337. def test_completed_concurrent_future(self):
  1338. # A concurrent future that is resolved before we even submit it
  1339. # to with_timeout.
  1340. with futures.ThreadPoolExecutor(1) as executor:
  1341. f = executor.submit(lambda: None)
  1342. f.result() # wait for completion
  1343. yield gen.with_timeout(datetime.timedelta(seconds=3600), f)
  1344. @unittest.skipIf(futures is None, 'futures module not present')
  1345. @gen_test
  1346. def test_normal_concurrent_future(self):
  1347. # A conccurrent future that resolves while waiting for the timeout.
  1348. with futures.ThreadPoolExecutor(1) as executor:
  1349. yield gen.with_timeout(datetime.timedelta(seconds=3600),
  1350. executor.submit(lambda: time.sleep(0.01)))
  1351. class WaitIteratorTest(AsyncTestCase):
  1352. @gen_test
  1353. def test_empty_iterator(self):
  1354. g = gen.WaitIterator()
  1355. self.assertTrue(g.done(), 'empty generator iterated')
  1356. with self.assertRaises(ValueError):
  1357. g = gen.WaitIterator(False, bar=False)
  1358. self.assertEqual(g.current_index, None, "bad nil current index")
  1359. self.assertEqual(g.current_future, None, "bad nil current future")
  1360. @gen_test
  1361. def test_already_done(self):
  1362. f1 = Future()
  1363. f2 = Future()
  1364. f3 = Future()
  1365. f1.set_result(24)
  1366. f2.set_result(42)
  1367. f3.set_result(84)
  1368. g = gen.WaitIterator(f1, f2, f3)
  1369. i = 0
  1370. while not g.done():
  1371. r = yield g.next()
  1372. # Order is not guaranteed, but the current implementation
  1373. # preserves ordering of already-done Futures.
  1374. if i == 0:
  1375. self.assertEqual(g.current_index, 0)
  1376. self.assertIs(g.current_future, f1)
  1377. self.assertEqual(r, 24)
  1378. elif i == 1:
  1379. self.assertEqual(g.current_index, 1)
  1380. self.assertIs(g.current_future, f2)
  1381. self.assertEqual(r, 42)
  1382. elif i == 2:
  1383. self.assertEqual(g.current_index, 2)
  1384. self.assertIs(g.current_future, f3)
  1385. self.assertEqual(r, 84)
  1386. i += 1
  1387. self.assertEqual(g.current_index, None, "bad nil current index")
  1388. self.assertEqual(g.current_future, None, "bad nil current future")
  1389. dg = gen.WaitIterator(f1=f1, f2=f2)
  1390. while not dg.done():
  1391. dr = yield dg.next()
  1392. if dg.current_index == "f1":
  1393. self.assertTrue(dg.current_future == f1 and dr == 24,
  1394. "WaitIterator dict status incorrect")
  1395. elif dg.current_index == "f2":
  1396. self.assertTrue(dg.current_future == f2 and dr == 42,
  1397. "WaitIterator dict status incorrect")
  1398. else:
  1399. self.fail("got bad WaitIterator index {}".format(
  1400. dg.current_index))
  1401. i += 1
  1402. self.assertEqual(dg.current_index, None, "bad nil current index")
  1403. self.assertEqual(dg.current_future, None, "bad nil current future")
  1404. def finish_coroutines(self, iteration, futures):
  1405. if iteration == 3:
  1406. futures[2].set_result(24)
  1407. elif iteration == 5:
  1408. futures[0].set_exception(ZeroDivisionError())
  1409. elif iteration == 8:
  1410. futures[1].set_result(42)
  1411. futures[3].set_result(84)
  1412. if iteration < 8:
  1413. self.io_loop.add_callback(self.finish_coroutines, iteration + 1, futures)
  1414. @gen_test
  1415. def test_iterator(self):
  1416. futures = [Future(), Future(), Future(), Future()]
  1417. self.finish_coroutines(0, futures)
  1418. g = gen.WaitIterator(*futures)
  1419. i = 0
  1420. while not g.done():
  1421. try:
  1422. r = yield g.next()
  1423. except ZeroDivisionError:
  1424. self.assertIs(g.current_future, futures[0],
  1425. 'exception future invalid')
  1426. else:
  1427. if i == 0:
  1428. self.assertEqual(r, 24, 'iterator value incorrect')
  1429. self.assertEqual(g.current_index, 2, 'wrong index')
  1430. elif i == 2:
  1431. self.assertEqual(r, 42, 'iterator value incorrect')
  1432. self.assertEqual(g.current_index, 1, 'wrong index')
  1433. elif i == 3:
  1434. self.assertEqual(r, 84, 'iterator value incorrect')
  1435. self.assertEqual(g.current_index, 3, 'wrong index')
  1436. i += 1
  1437. @skipBefore35
  1438. @gen_test
  1439. def test_iterator_async_await(self):
  1440. # Recreate the previous test with py35 syntax. It's a little clunky
  1441. # because of the way the previous test handles an exception on
  1442. # a single iteration.
  1443. futures = [Future(), Future(), Future(), Future()]
  1444. self.finish_coroutines(0, futures)
  1445. self.finished = False
  1446. namespace = exec_test(globals(), locals(), """
  1447. async def f():
  1448. i = 0
  1449. g = gen.WaitIterator(*futures)
  1450. try:
  1451. async for r in g:
  1452. if i == 0:
  1453. self.assertEqual(r, 24, 'iterator value incorrect')
  1454. self.assertEqual(g.current_index, 2, 'wrong index')
  1455. else:
  1456. raise Exception("expected exception on iteration 1")
  1457. i += 1
  1458. except ZeroDivisionError:
  1459. i += 1
  1460. async for r in g:
  1461. if i == 2:
  1462. self.assertEqual(r, 42, 'iterator value incorrect')
  1463. self.assertEqual(g.current_index, 1, 'wrong index')
  1464. elif i == 3:
  1465. self.assertEqual(r, 84, 'iterator value incorrect')
  1466. self.assertEqual(g.current_index, 3, 'wrong index')
  1467. else:
  1468. raise Exception("didn't expect iteration %d" % i)
  1469. i += 1
  1470. self.finished = True
  1471. """)
  1472. yield namespace['f']()
  1473. self.assertTrue(self.finished)
  1474. @gen_test
  1475. def test_no_ref(self):
  1476. # In this usage, there is no direct hard reference to the
  1477. # WaitIterator itself, only the Future it returns. Since
  1478. # WaitIterator uses weak references internally to improve GC
  1479. # performance, this used to cause problems.
  1480. yield gen.with_timeout(datetime.timedelta(seconds=0.1),
  1481. gen.WaitIterator(gen.sleep(0)).next())
  1482. class RunnerGCTest(AsyncTestCase):
  1483. def is_pypy3(self):
  1484. return (platform.python_implementation() == 'PyPy' and
  1485. sys.version_info > (3,))
  1486. @gen_test
  1487. def test_gc(self):
  1488. # Github issue 1769: Runner objects can get GCed unexpectedly
  1489. # while their future is alive.
  1490. weakref_scope = [None]
  1491. def callback():
  1492. gc.collect(2)
  1493. weakref_scope[0]().set_result(123)
  1494. @gen.coroutine
  1495. def tester():
  1496. fut = Future()
  1497. weakref_scope[0] = weakref.ref(fut)
  1498. self.io_loop.add_callback(callback)
  1499. yield fut
  1500. yield gen.with_timeout(
  1501. datetime.timedelta(seconds=0.2),
  1502. tester()
  1503. )
  1504. def test_gc_infinite_coro(self):
  1505. # Github issue 2229: suspended coroutines should be GCed when
  1506. # their loop is closed, even if they're involved in a reference
  1507. # cycle.
  1508. if IOLoop.configured_class().__name__.endswith('TwistedIOLoop'):
  1509. raise unittest.SkipTest("Test may fail on TwistedIOLoop")
  1510. loop = self.get_new_ioloop()
  1511. result = []
  1512. wfut = []
  1513. @gen.coroutine
  1514. def infinite_coro():
  1515. try:
  1516. while True:
  1517. yield gen.sleep(1e-3)
  1518. result.append(True)
  1519. finally:
  1520. # coroutine finalizer
  1521. result.append(None)
  1522. @gen.coroutine
  1523. def do_something():
  1524. fut = infinite_coro()
  1525. fut._refcycle = fut
  1526. wfut.append(weakref.ref(fut))
  1527. yield gen.sleep(0.2)
  1528. loop.run_sync(do_something)
  1529. loop.close()
  1530. gc.collect()
  1531. # Future was collected
  1532. self.assertIs(wfut[0](), None)
  1533. # At least one wakeup
  1534. self.assertGreaterEqual(len(result), 2)
  1535. if not self.is_pypy3():
  1536. # coroutine finalizer was called (not on PyPy3 apparently)
  1537. self.assertIs(result[-1], None)
  1538. @skipBefore35
  1539. def test_gc_infinite_async_await(self):
  1540. # Same as test_gc_infinite_coro, but with a `async def` function
  1541. import asyncio
  1542. namespace = exec_test(globals(), locals(), """
  1543. async def infinite_coro(result):
  1544. try:
  1545. while True:
  1546. await gen.sleep(1e-3)
  1547. result.append(True)
  1548. finally:
  1549. # coroutine finalizer
  1550. result.append(None)
  1551. """)
  1552. infinite_coro = namespace['infinite_coro']
  1553. loop = self.get_new_ioloop()
  1554. result = []
  1555. wfut = []
  1556. @gen.coroutine
  1557. def do_something():
  1558. fut = asyncio.get_event_loop().create_task(infinite_coro(result))
  1559. fut._refcycle = fut
  1560. wfut.append(weakref.ref(fut))
  1561. yield gen.sleep(0.2)
  1562. loop.run_sync(do_something)
  1563. with ExpectLog('asyncio', "Task was destroyed but it is pending"):
  1564. loop.close()
  1565. gc.collect()
  1566. # Future was collected
  1567. self.assertIs(wfut[0](), None)
  1568. # At least one wakeup and one finally
  1569. self.assertGreaterEqual(len(result), 2)
  1570. if not self.is_pypy3():
  1571. # coroutine finalizer was called (not on PyPy3 apparently)
  1572. self.assertIs(result[-1], None)
  1573. def test_multi_moment(self):
  1574. # Test gen.multi with moment
  1575. # now that it's not a real Future
  1576. @gen.coroutine
  1577. def wait_a_moment():
  1578. result = yield gen.multi([gen.moment, gen.moment])
  1579. raise gen.Return(result)
  1580. loop = self.get_new_ioloop()
  1581. result = loop.run_sync(wait_a_moment)
  1582. self.assertEqual(result, [None, None])
  1583. if __name__ == '__main__':
  1584. unittest.main()