00001 from __future__ import absolute_import, division, print_function, with_statement
00002
00003 import contextlib
00004 import datetime
00005 import functools
00006 import sys
00007 import textwrap
00008 import time
00009 import platform
00010 import weakref
00011
00012 from tornado.concurrent import return_future, Future
00013 from tornado.escape import url_escape
00014 from tornado.httpclient import AsyncHTTPClient
00015 from tornado.ioloop import IOLoop
00016 from tornado.log import app_log
00017 from tornado import stack_context
00018 from tornado.testing import AsyncHTTPTestCase, AsyncTestCase, ExpectLog, gen_test
00019 from tornado.test.util import unittest, skipOnTravis
00020 from tornado.web import Application, RequestHandler, asynchronous, HTTPError
00021
00022 from tornado import gen
00023
00024 try:
00025 from concurrent import futures
00026 except ImportError:
00027 futures = None
00028
00029 skipBefore33 = unittest.skipIf(sys.version_info < (3, 3), 'PEP 380 not available')
00030 skipNotCPython = unittest.skipIf(platform.python_implementation() != 'CPython',
00031 'Not CPython implementation')
00032
00033
00034 class GenEngineTest(AsyncTestCase):
00035 def setUp(self):
00036 super(GenEngineTest, self).setUp()
00037 self.named_contexts = []
00038
00039 def named_context(self, name):
00040 @contextlib.contextmanager
00041 def context():
00042 self.named_contexts.append(name)
00043 try:
00044 yield
00045 finally:
00046 self.assertEqual(self.named_contexts.pop(), name)
00047 return context
00048
00049 def run_gen(self, f):
00050 f()
00051 return self.wait()
00052
00053 def delay_callback(self, iterations, callback, arg):
00054 """Runs callback(arg) after a number of IOLoop iterations."""
00055 if iterations == 0:
00056 callback(arg)
00057 else:
00058 self.io_loop.add_callback(functools.partial(
00059 self.delay_callback, iterations - 1, callback, arg))
00060
00061 @return_future
00062 def async_future(self, result, callback):
00063 self.io_loop.add_callback(callback, result)
00064
00065 def test_no_yield(self):
00066 @gen.engine
00067 def f():
00068 self.stop()
00069 self.run_gen(f)
00070
00071 def test_inline_cb(self):
00072 @gen.engine
00073 def f():
00074 (yield gen.Callback("k1"))()
00075 res = yield gen.Wait("k1")
00076 self.assertTrue(res is None)
00077 self.stop()
00078 self.run_gen(f)
00079
00080 def test_ioloop_cb(self):
00081 @gen.engine
00082 def f():
00083 self.io_loop.add_callback((yield gen.Callback("k1")))
00084 yield gen.Wait("k1")
00085 self.stop()
00086 self.run_gen(f)
00087
00088 def test_exception_phase1(self):
00089 @gen.engine
00090 def f():
00091 1 / 0
00092 self.assertRaises(ZeroDivisionError, self.run_gen, f)
00093
00094 def test_exception_phase2(self):
00095 @gen.engine
00096 def f():
00097 self.io_loop.add_callback((yield gen.Callback("k1")))
00098 yield gen.Wait("k1")
00099 1 / 0
00100 self.assertRaises(ZeroDivisionError, self.run_gen, f)
00101
00102 def test_exception_in_task_phase1(self):
00103 def fail_task(callback):
00104 1 / 0
00105
00106 @gen.engine
00107 def f():
00108 try:
00109 yield gen.Task(fail_task)
00110 raise Exception("did not get expected exception")
00111 except ZeroDivisionError:
00112 self.stop()
00113 self.run_gen(f)
00114
00115 def test_exception_in_task_phase2(self):
00116
00117 def fail_task(callback):
00118 self.io_loop.add_callback(lambda: 1 / 0)
00119
00120 @gen.engine
00121 def f():
00122 try:
00123 yield gen.Task(fail_task)
00124 raise Exception("did not get expected exception")
00125 except ZeroDivisionError:
00126 self.stop()
00127 self.run_gen(f)
00128
00129 def test_with_arg(self):
00130 @gen.engine
00131 def f():
00132 (yield gen.Callback("k1"))(42)
00133 res = yield gen.Wait("k1")
00134 self.assertEqual(42, res)
00135 self.stop()
00136 self.run_gen(f)
00137
00138 def test_with_arg_tuple(self):
00139 @gen.engine
00140 def f():
00141 (yield gen.Callback((1, 2)))((3, 4))
00142 res = yield gen.Wait((1, 2))
00143 self.assertEqual((3, 4), res)
00144 self.stop()
00145 self.run_gen(f)
00146
00147 def test_key_reuse(self):
00148 @gen.engine
00149 def f():
00150 yield gen.Callback("k1")
00151 yield gen.Callback("k1")
00152 self.stop()
00153 self.assertRaises(gen.KeyReuseError, self.run_gen, f)
00154
00155 def test_key_reuse_tuple(self):
00156 @gen.engine
00157 def f():
00158 yield gen.Callback((1, 2))
00159 yield gen.Callback((1, 2))
00160 self.stop()
00161 self.assertRaises(gen.KeyReuseError, self.run_gen, f)
00162
00163 def test_key_mismatch(self):
00164 @gen.engine
00165 def f():
00166 yield gen.Callback("k1")
00167 yield gen.Wait("k2")
00168 self.stop()
00169 self.assertRaises(gen.UnknownKeyError, self.run_gen, f)
00170
00171 def test_key_mismatch_tuple(self):
00172 @gen.engine
00173 def f():
00174 yield gen.Callback((1, 2))
00175 yield gen.Wait((2, 3))
00176 self.stop()
00177 self.assertRaises(gen.UnknownKeyError, self.run_gen, f)
00178
00179 def test_leaked_callback(self):
00180 @gen.engine
00181 def f():
00182 yield gen.Callback("k1")
00183 self.stop()
00184 self.assertRaises(gen.LeakedCallbackError, self.run_gen, f)
00185
00186 def test_leaked_callback_tuple(self):
00187 @gen.engine
00188 def f():
00189 yield gen.Callback((1, 2))
00190 self.stop()
00191 self.assertRaises(gen.LeakedCallbackError, self.run_gen, f)
00192
00193 def test_parallel_callback(self):
00194 @gen.engine
00195 def f():
00196 for k in range(3):
00197 self.io_loop.add_callback((yield gen.Callback(k)))
00198 yield gen.Wait(1)
00199 self.io_loop.add_callback((yield gen.Callback(3)))
00200 yield gen.Wait(0)
00201 yield gen.Wait(3)
00202 yield gen.Wait(2)
00203 self.stop()
00204 self.run_gen(f)
00205
00206 def test_bogus_yield(self):
00207 @gen.engine
00208 def f():
00209 yield 42
00210 self.assertRaises(gen.BadYieldError, self.run_gen, f)
00211
00212 def test_bogus_yield_tuple(self):
00213 @gen.engine
00214 def f():
00215 yield (1, 2)
00216 self.assertRaises(gen.BadYieldError, self.run_gen, f)
00217
00218 def test_reuse(self):
00219 @gen.engine
00220 def f():
00221 self.io_loop.add_callback((yield gen.Callback(0)))
00222 yield gen.Wait(0)
00223 self.stop()
00224 self.run_gen(f)
00225 self.run_gen(f)
00226
00227 def test_task(self):
00228 @gen.engine
00229 def f():
00230 yield gen.Task(self.io_loop.add_callback)
00231 self.stop()
00232 self.run_gen(f)
00233
00234 def test_wait_all(self):
00235 @gen.engine
00236 def f():
00237 (yield gen.Callback("k1"))("v1")
00238 (yield gen.Callback("k2"))("v2")
00239 results = yield gen.WaitAll(["k1", "k2"])
00240 self.assertEqual(results, ["v1", "v2"])
00241 self.stop()
00242 self.run_gen(f)
00243
00244 def test_exception_in_yield(self):
00245 @gen.engine
00246 def f():
00247 try:
00248 yield gen.Wait("k1")
00249 raise Exception("did not get expected exception")
00250 except gen.UnknownKeyError:
00251 pass
00252 self.stop()
00253 self.run_gen(f)
00254
00255 def test_resume_after_exception_in_yield(self):
00256 @gen.engine
00257 def f():
00258 try:
00259 yield gen.Wait("k1")
00260 raise Exception("did not get expected exception")
00261 except gen.UnknownKeyError:
00262 pass
00263 (yield gen.Callback("k2"))("v2")
00264 self.assertEqual((yield gen.Wait("k2")), "v2")
00265 self.stop()
00266 self.run_gen(f)
00267
00268 def test_orphaned_callback(self):
00269 @gen.engine
00270 def f():
00271 self.orphaned_callback = yield gen.Callback(1)
00272 try:
00273 self.run_gen(f)
00274 raise Exception("did not get expected exception")
00275 except gen.LeakedCallbackError:
00276 pass
00277 self.orphaned_callback()
00278
00279 def test_multi(self):
00280 @gen.engine
00281 def f():
00282 (yield gen.Callback("k1"))("v1")
00283 (yield gen.Callback("k2"))("v2")
00284 results = yield [gen.Wait("k1"), gen.Wait("k2")]
00285 self.assertEqual(results, ["v1", "v2"])
00286 self.stop()
00287 self.run_gen(f)
00288
00289 def test_multi_dict(self):
00290 @gen.engine
00291 def f():
00292 (yield gen.Callback("k1"))("v1")
00293 (yield gen.Callback("k2"))("v2")
00294 results = yield dict(foo=gen.Wait("k1"), bar=gen.Wait("k2"))
00295 self.assertEqual(results, dict(foo="v1", bar="v2"))
00296 self.stop()
00297 self.run_gen(f)
00298
00299
00300
00301
00302 def test_multi_yieldpoint_delayed(self):
00303 @gen.engine
00304 def f():
00305
00306 responses = yield gen.Multi([
00307 gen.Task(self.delay_callback, 3, arg="v1"),
00308 gen.Task(self.delay_callback, 1, arg="v2"),
00309 ])
00310 self.assertEqual(responses, ["v1", "v2"])
00311 self.stop()
00312 self.run_gen(f)
00313
00314 def test_multi_yieldpoint_dict_delayed(self):
00315 @gen.engine
00316 def f():
00317
00318 responses = yield gen.Multi(dict(
00319 foo=gen.Task(self.delay_callback, 3, arg="v1"),
00320 bar=gen.Task(self.delay_callback, 1, arg="v2"),
00321 ))
00322 self.assertEqual(responses, dict(foo="v1", bar="v2"))
00323 self.stop()
00324 self.run_gen(f)
00325
00326 def test_multi_future_delayed(self):
00327 @gen.engine
00328 def f():
00329
00330 responses = yield gen.multi_future([
00331 gen.Task(self.delay_callback, 3, arg="v1"),
00332 gen.Task(self.delay_callback, 1, arg="v2"),
00333 ])
00334 self.assertEqual(responses, ["v1", "v2"])
00335 self.stop()
00336 self.run_gen(f)
00337
00338 def test_multi_future_dict_delayed(self):
00339 @gen.engine
00340 def f():
00341
00342 responses = yield gen.multi_future(dict(
00343 foo=gen.Task(self.delay_callback, 3, arg="v1"),
00344 bar=gen.Task(self.delay_callback, 1, arg="v2"),
00345 ))
00346 self.assertEqual(responses, dict(foo="v1", bar="v2"))
00347 self.stop()
00348 self.run_gen(f)
00349
00350 @skipOnTravis
00351 @gen_test
00352 def test_multi_performance(self):
00353
00354
00355
00356 start = time.time()
00357 yield [gen.Task(self.io_loop.add_callback) for i in range(2000)]
00358 end = time.time()
00359 self.assertLess(end - start, 1.0)
00360
00361 @gen_test
00362 def test_multi_empty(self):
00363
00364 x = yield []
00365 self.assertTrue(isinstance(x, list))
00366 y = yield {}
00367 self.assertTrue(isinstance(y, dict))
00368
00369 @gen_test
00370 def test_multi_mixed_types(self):
00371
00372
00373 (yield gen.Callback("k1"))("v1")
00374 responses = yield [gen.Wait("k1"),
00375 gen.Task(self.delay_callback, 3, arg="v2")]
00376 self.assertEqual(responses, ["v1", "v2"])
00377
00378 @gen_test
00379 def test_future(self):
00380 result = yield self.async_future(1)
00381 self.assertEqual(result, 1)
00382
00383 @gen_test
00384 def test_multi_future(self):
00385 results = yield [self.async_future(1), self.async_future(2)]
00386 self.assertEqual(results, [1, 2])
00387
00388 @gen_test
00389 def test_multi_dict_future(self):
00390 results = yield dict(foo=self.async_future(1), bar=self.async_future(2))
00391 self.assertEqual(results, dict(foo=1, bar=2))
00392
00393 def test_arguments(self):
00394 @gen.engine
00395 def f():
00396 (yield gen.Callback("noargs"))()
00397 self.assertEqual((yield gen.Wait("noargs")), None)
00398 (yield gen.Callback("1arg"))(42)
00399 self.assertEqual((yield gen.Wait("1arg")), 42)
00400
00401 (yield gen.Callback("kwargs"))(value=42)
00402 result = yield gen.Wait("kwargs")
00403 self.assertTrue(isinstance(result, gen.Arguments))
00404 self.assertEqual(((), dict(value=42)), result)
00405 self.assertEqual(dict(value=42), result.kwargs)
00406
00407 (yield gen.Callback("2args"))(42, 43)
00408 result = yield gen.Wait("2args")
00409 self.assertTrue(isinstance(result, gen.Arguments))
00410 self.assertEqual(((42, 43), {}), result)
00411 self.assertEqual((42, 43), result.args)
00412
00413 def task_func(callback):
00414 callback(None, error="foo")
00415 result = yield gen.Task(task_func)
00416 self.assertTrue(isinstance(result, gen.Arguments))
00417 self.assertEqual(((None,), dict(error="foo")), result)
00418
00419 self.stop()
00420 self.run_gen(f)
00421
00422 def test_stack_context_leak(self):
00423
00424
00425 def _stack_depth():
00426 head = stack_context._state.contexts[1]
00427 length = 0
00428
00429 while head is not None:
00430 length += 1
00431 head = head.old_contexts[1]
00432
00433 return length
00434
00435 @gen.engine
00436 def inner(callback):
00437 yield gen.Task(self.io_loop.add_callback)
00438 callback()
00439
00440 @gen.engine
00441 def outer():
00442 for i in range(10):
00443 yield gen.Task(inner)
00444
00445 stack_increase = _stack_depth() - initial_stack_depth
00446 self.assertTrue(stack_increase <= 2)
00447 self.stop()
00448 initial_stack_depth = _stack_depth()
00449 self.run_gen(outer)
00450
00451 def test_stack_context_leak_exception(self):
00452
00453 @gen.engine
00454 def inner(callback):
00455 yield gen.Task(self.io_loop.add_callback)
00456 1 / 0
00457
00458 @gen.engine
00459 def outer():
00460 for i in range(10):
00461 try:
00462 yield gen.Task(inner)
00463 except ZeroDivisionError:
00464 pass
00465 stack_increase = len(stack_context._state.contexts) - initial_stack_depth
00466 self.assertTrue(stack_increase <= 2)
00467 self.stop()
00468 initial_stack_depth = len(stack_context._state.contexts)
00469 self.run_gen(outer)
00470
00471 def function_with_stack_context(self, callback):
00472
00473
00474
00475 def step2():
00476 self.assertEqual(self.named_contexts, ['a'])
00477 self.io_loop.add_callback(callback)
00478
00479 with stack_context.StackContext(self.named_context('a')):
00480 self.io_loop.add_callback(step2)
00481
00482 @gen_test
00483 def test_wait_transfer_stack_context(self):
00484
00485
00486 cb = yield gen.Callback('k1')
00487 self.function_with_stack_context(cb)
00488 self.assertEqual(self.named_contexts, [])
00489 yield gen.Wait('k1')
00490 self.assertEqual(self.named_contexts, [])
00491
00492 @gen_test
00493 def test_task_transfer_stack_context(self):
00494 yield gen.Task(self.function_with_stack_context)
00495 self.assertEqual(self.named_contexts, [])
00496
00497 def test_raise_after_stop(self):
00498
00499
00500 @gen.engine
00501 def f():
00502 self.stop()
00503 1 / 0
00504
00505 with self.assertRaises(ZeroDivisionError):
00506 self.run_gen(f)
00507
00508 def test_sync_raise_return(self):
00509
00510
00511 @gen.engine
00512 def f():
00513 self.stop(42)
00514 raise gen.Return()
00515
00516 result = self.run_gen(f)
00517 self.assertEqual(result, 42)
00518
00519 def test_async_raise_return(self):
00520 @gen.engine
00521 def f():
00522 yield gen.Task(self.io_loop.add_callback)
00523 self.stop(42)
00524 raise gen.Return()
00525
00526 result = self.run_gen(f)
00527 self.assertEqual(result, 42)
00528
00529 def test_sync_raise_return_value(self):
00530 @gen.engine
00531 def f():
00532 raise gen.Return(42)
00533
00534 with self.assertRaises(gen.ReturnValueIgnoredError):
00535 self.run_gen(f)
00536
00537 def test_sync_raise_return_value_tuple(self):
00538 @gen.engine
00539 def f():
00540 raise gen.Return((1, 2))
00541
00542 with self.assertRaises(gen.ReturnValueIgnoredError):
00543 self.run_gen(f)
00544
00545 def test_async_raise_return_value(self):
00546 @gen.engine
00547 def f():
00548 yield gen.Task(self.io_loop.add_callback)
00549 raise gen.Return(42)
00550
00551 with self.assertRaises(gen.ReturnValueIgnoredError):
00552 self.run_gen(f)
00553
00554 def test_async_raise_return_value_tuple(self):
00555 @gen.engine
00556 def f():
00557 yield gen.Task(self.io_loop.add_callback)
00558 raise gen.Return((1, 2))
00559
00560 with self.assertRaises(gen.ReturnValueIgnoredError):
00561 self.run_gen(f)
00562
00563 def test_return_value(self):
00564
00565
00566 @gen.engine
00567 def f():
00568 return 42
00569
00570 with self.assertRaises(gen.ReturnValueIgnoredError):
00571 self.run_gen(f)
00572
00573 def test_return_value_tuple(self):
00574
00575
00576 @gen.engine
00577 def f():
00578 return (1, 2)
00579
00580 with self.assertRaises(gen.ReturnValueIgnoredError):
00581 self.run_gen(f)
00582
00583 @skipNotCPython
00584 def test_task_refcounting(self):
00585
00586
00587 @gen.engine
00588 def f():
00589 class Foo(object):
00590 pass
00591 arg = Foo()
00592 self.arg_ref = weakref.ref(arg)
00593 task = gen.Task(self.io_loop.add_callback, arg=arg)
00594 self.task_ref = weakref.ref(task)
00595 yield task
00596 self.stop()
00597
00598 self.run_gen(f)
00599 self.assertIs(self.arg_ref(), None)
00600 self.assertIs(self.task_ref(), None)
00601
00602
00603 class GenCoroutineTest(AsyncTestCase):
00604 def setUp(self):
00605
00606
00607
00608 self.finished = False
00609 super(GenCoroutineTest, self).setUp()
00610
00611 def tearDown(self):
00612 super(GenCoroutineTest, self).tearDown()
00613 assert self.finished
00614
00615 @gen_test
00616 def test_sync_gen_return(self):
00617 @gen.coroutine
00618 def f():
00619 raise gen.Return(42)
00620 result = yield f()
00621 self.assertEqual(result, 42)
00622 self.finished = True
00623
00624 @gen_test
00625 def test_async_gen_return(self):
00626 @gen.coroutine
00627 def f():
00628 yield gen.Task(self.io_loop.add_callback)
00629 raise gen.Return(42)
00630 result = yield f()
00631 self.assertEqual(result, 42)
00632 self.finished = True
00633
00634 @gen_test
00635 def test_sync_return(self):
00636 @gen.coroutine
00637 def f():
00638 return 42
00639 result = yield f()
00640 self.assertEqual(result, 42)
00641 self.finished = True
00642
00643 @skipBefore33
00644 @gen_test
00645 def test_async_return(self):
00646
00647
00648
00649
00650 global_namespace = dict(globals(), **locals())
00651 local_namespace = {}
00652 exec(textwrap.dedent("""
00653 @gen.coroutine
00654 def f():
00655 yield gen.Task(self.io_loop.add_callback)
00656 return 42
00657 """), global_namespace, local_namespace)
00658 result = yield local_namespace['f']()
00659 self.assertEqual(result, 42)
00660 self.finished = True
00661
00662 @skipBefore33
00663 @gen_test
00664 def test_async_early_return(self):
00665
00666
00667
00668 global_namespace = dict(globals(), **locals())
00669 local_namespace = {}
00670 exec(textwrap.dedent("""
00671 @gen.coroutine
00672 def f():
00673 if True:
00674 return 42
00675 yield gen.Task(self.io_loop.add_callback)
00676 """), global_namespace, local_namespace)
00677 result = yield local_namespace['f']()
00678 self.assertEqual(result, 42)
00679 self.finished = True
00680
00681 @gen_test
00682 def test_sync_return_no_value(self):
00683 @gen.coroutine
00684 def f():
00685 return
00686 result = yield f()
00687 self.assertEqual(result, None)
00688 self.finished = True
00689
00690 @gen_test
00691 def test_async_return_no_value(self):
00692
00693 @gen.coroutine
00694 def f():
00695 yield gen.Task(self.io_loop.add_callback)
00696 return
00697 result = yield f()
00698 self.assertEqual(result, None)
00699 self.finished = True
00700
00701 @gen_test
00702 def test_sync_raise(self):
00703 @gen.coroutine
00704 def f():
00705 1 / 0
00706
00707
00708
00709 future = f()
00710 with self.assertRaises(ZeroDivisionError):
00711 yield future
00712 self.finished = True
00713
00714 @gen_test
00715 def test_async_raise(self):
00716 @gen.coroutine
00717 def f():
00718 yield gen.Task(self.io_loop.add_callback)
00719 1 / 0
00720 future = f()
00721 with self.assertRaises(ZeroDivisionError):
00722 yield future
00723 self.finished = True
00724
00725 @gen_test
00726 def test_pass_callback(self):
00727 @gen.coroutine
00728 def f():
00729 raise gen.Return(42)
00730 result = yield gen.Task(f)
00731 self.assertEqual(result, 42)
00732 self.finished = True
00733
00734 @gen_test
00735 def test_replace_yieldpoint_exception(self):
00736
00737
00738 @gen.coroutine
00739 def f1():
00740 1 / 0
00741
00742 @gen.coroutine
00743 def f2():
00744 try:
00745 yield f1()
00746 except ZeroDivisionError:
00747 raise KeyError()
00748
00749 future = f2()
00750 with self.assertRaises(KeyError):
00751 yield future
00752 self.finished = True
00753
00754 @gen_test
00755 def test_swallow_yieldpoint_exception(self):
00756
00757
00758 @gen.coroutine
00759 def f1():
00760 1 / 0
00761
00762 @gen.coroutine
00763 def f2():
00764 try:
00765 yield f1()
00766 except ZeroDivisionError:
00767 raise gen.Return(42)
00768
00769 result = yield f2()
00770 self.assertEqual(result, 42)
00771 self.finished = True
00772
00773 @gen_test
00774 def test_replace_context_exception(self):
00775
00776
00777
00778
00779
00780
00781 @gen.coroutine
00782 def f2():
00783 (yield gen.Callback(1))()
00784 yield gen.Wait(1)
00785 self.io_loop.add_callback(lambda: 1 / 0)
00786 try:
00787 yield gen.Task(self.io_loop.add_timeout,
00788 self.io_loop.time() + 10)
00789 except ZeroDivisionError:
00790 raise KeyError()
00791
00792 future = f2()
00793 with self.assertRaises(KeyError):
00794 yield future
00795 self.finished = True
00796
00797 @gen_test
00798 def test_swallow_context_exception(self):
00799
00800
00801 @gen.coroutine
00802 def f2():
00803 (yield gen.Callback(1))()
00804 yield gen.Wait(1)
00805 self.io_loop.add_callback(lambda: 1 / 0)
00806 try:
00807 yield gen.Task(self.io_loop.add_timeout,
00808 self.io_loop.time() + 10)
00809 except ZeroDivisionError:
00810 raise gen.Return(42)
00811
00812 result = yield f2()
00813 self.assertEqual(result, 42)
00814 self.finished = True
00815
00816 @gen_test
00817 def test_moment(self):
00818 calls = []
00819 @gen.coroutine
00820 def f(name, yieldable):
00821 for i in range(5):
00822 calls.append(name)
00823 yield yieldable
00824
00825
00826 immediate = Future()
00827 immediate.set_result(None)
00828 yield [f('a', immediate), f('b', immediate)]
00829 self.assertEqual(''.join(calls), 'aaaaabbbbb')
00830
00831
00832 calls = []
00833 yield [f('a', gen.moment), f('b', gen.moment)]
00834 self.assertEqual(''.join(calls), 'ababababab')
00835 self.finished = True
00836
00837 calls = []
00838 yield [f('a', gen.moment), f('b', immediate)]
00839 self.assertEqual(''.join(calls), 'abbbbbaaaa')
00840
00841
00842 class GenSequenceHandler(RequestHandler):
00843 @asynchronous
00844 @gen.engine
00845 def get(self):
00846 self.io_loop = self.request.connection.stream.io_loop
00847 self.io_loop.add_callback((yield gen.Callback("k1")))
00848 yield gen.Wait("k1")
00849 self.write("1")
00850 self.io_loop.add_callback((yield gen.Callback("k2")))
00851 yield gen.Wait("k2")
00852 self.write("2")
00853
00854 self.io_loop.add_callback((yield gen.Callback("k1")))
00855 yield gen.Wait("k1")
00856 self.finish("3")
00857
00858
00859 class GenCoroutineSequenceHandler(RequestHandler):
00860 @gen.coroutine
00861 def get(self):
00862 self.io_loop = self.request.connection.stream.io_loop
00863 self.io_loop.add_callback((yield gen.Callback("k1")))
00864 yield gen.Wait("k1")
00865 self.write("1")
00866 self.io_loop.add_callback((yield gen.Callback("k2")))
00867 yield gen.Wait("k2")
00868 self.write("2")
00869
00870 self.io_loop.add_callback((yield gen.Callback("k1")))
00871 yield gen.Wait("k1")
00872 self.finish("3")
00873
00874
00875 class GenCoroutineUnfinishedSequenceHandler(RequestHandler):
00876 @asynchronous
00877 @gen.coroutine
00878 def get(self):
00879 self.io_loop = self.request.connection.stream.io_loop
00880 self.io_loop.add_callback((yield gen.Callback("k1")))
00881 yield gen.Wait("k1")
00882 self.write("1")
00883 self.io_loop.add_callback((yield gen.Callback("k2")))
00884 yield gen.Wait("k2")
00885 self.write("2")
00886
00887 self.io_loop.add_callback((yield gen.Callback("k1")))
00888 yield gen.Wait("k1")
00889
00890 self.write("3")
00891
00892
00893 class GenTaskHandler(RequestHandler):
00894 @asynchronous
00895 @gen.engine
00896 def get(self):
00897 io_loop = self.request.connection.stream.io_loop
00898 client = AsyncHTTPClient(io_loop=io_loop)
00899 response = yield gen.Task(client.fetch, self.get_argument('url'))
00900 response.rethrow()
00901 self.finish(b"got response: " + response.body)
00902
00903
00904 class GenExceptionHandler(RequestHandler):
00905 @asynchronous
00906 @gen.engine
00907 def get(self):
00908
00909 io_loop = self.request.connection.stream.io_loop
00910 yield gen.Task(io_loop.add_callback)
00911 raise Exception("oops")
00912
00913
00914 class GenCoroutineExceptionHandler(RequestHandler):
00915 @gen.coroutine
00916 def get(self):
00917
00918 io_loop = self.request.connection.stream.io_loop
00919 yield gen.Task(io_loop.add_callback)
00920 raise Exception("oops")
00921
00922
00923 class GenYieldExceptionHandler(RequestHandler):
00924 @asynchronous
00925 @gen.engine
00926 def get(self):
00927 io_loop = self.request.connection.stream.io_loop
00928
00929
00930 def fail_task(callback):
00931 io_loop.add_callback(lambda: 1 / 0)
00932 try:
00933 yield gen.Task(fail_task)
00934 raise Exception("did not get expected exception")
00935 except ZeroDivisionError:
00936 self.finish('ok')
00937
00938
00939 class UndecoratedCoroutinesHandler(RequestHandler):
00940 @gen.coroutine
00941 def prepare(self):
00942 self.chunks = []
00943 yield gen.Task(IOLoop.current().add_callback)
00944 self.chunks.append('1')
00945
00946 @gen.coroutine
00947 def get(self):
00948 self.chunks.append('2')
00949 yield gen.Task(IOLoop.current().add_callback)
00950 self.chunks.append('3')
00951 yield gen.Task(IOLoop.current().add_callback)
00952 self.write(''.join(self.chunks))
00953
00954
00955 class AsyncPrepareErrorHandler(RequestHandler):
00956 @gen.coroutine
00957 def prepare(self):
00958 yield gen.Task(IOLoop.current().add_callback)
00959 raise HTTPError(403)
00960
00961 def get(self):
00962 self.finish('ok')
00963
00964
00965 class GenWebTest(AsyncHTTPTestCase):
00966 def get_app(self):
00967 return Application([
00968 ('/sequence', GenSequenceHandler),
00969 ('/coroutine_sequence', GenCoroutineSequenceHandler),
00970 ('/coroutine_unfinished_sequence',
00971 GenCoroutineUnfinishedSequenceHandler),
00972 ('/task', GenTaskHandler),
00973 ('/exception', GenExceptionHandler),
00974 ('/coroutine_exception', GenCoroutineExceptionHandler),
00975 ('/yield_exception', GenYieldExceptionHandler),
00976 ('/undecorated_coroutine', UndecoratedCoroutinesHandler),
00977 ('/async_prepare_error', AsyncPrepareErrorHandler),
00978 ])
00979
00980 def test_sequence_handler(self):
00981 response = self.fetch('/sequence')
00982 self.assertEqual(response.body, b"123")
00983
00984 def test_coroutine_sequence_handler(self):
00985 response = self.fetch('/coroutine_sequence')
00986 self.assertEqual(response.body, b"123")
00987
00988 def test_coroutine_unfinished_sequence_handler(self):
00989 response = self.fetch('/coroutine_unfinished_sequence')
00990 self.assertEqual(response.body, b"123")
00991
00992 def test_task_handler(self):
00993 response = self.fetch('/task?url=%s' % url_escape(self.get_url('/sequence')))
00994 self.assertEqual(response.body, b"got response: 123")
00995
00996 def test_exception_handler(self):
00997
00998 with ExpectLog(app_log, "Uncaught exception GET /exception"):
00999 response = self.fetch('/exception')
01000 self.assertEqual(500, response.code)
01001
01002 def test_coroutine_exception_handler(self):
01003
01004 with ExpectLog(app_log, "Uncaught exception GET /coroutine_exception"):
01005 response = self.fetch('/coroutine_exception')
01006 self.assertEqual(500, response.code)
01007
01008 def test_yield_exception_handler(self):
01009 response = self.fetch('/yield_exception')
01010 self.assertEqual(response.body, b'ok')
01011
01012 def test_undecorated_coroutines(self):
01013 response = self.fetch('/undecorated_coroutine')
01014 self.assertEqual(response.body, b'123')
01015
01016 def test_async_prepare_error_handler(self):
01017 response = self.fetch('/async_prepare_error')
01018 self.assertEqual(response.code, 403)
01019
01020
01021 class WithTimeoutTest(AsyncTestCase):
01022 @gen_test
01023 def test_timeout(self):
01024 with self.assertRaises(gen.TimeoutError):
01025 yield gen.with_timeout(datetime.timedelta(seconds=0.1),
01026 Future())
01027
01028 @gen_test
01029 def test_completes_before_timeout(self):
01030 future = Future()
01031 self.io_loop.add_timeout(datetime.timedelta(seconds=0.1),
01032 lambda: future.set_result('asdf'))
01033 result = yield gen.with_timeout(datetime.timedelta(seconds=3600),
01034 future)
01035 self.assertEqual(result, 'asdf')
01036
01037 @gen_test
01038 def test_fails_before_timeout(self):
01039 future = Future()
01040 self.io_loop.add_timeout(
01041 datetime.timedelta(seconds=0.1),
01042 lambda: future.set_exception(ZeroDivisionError))
01043 with self.assertRaises(ZeroDivisionError):
01044 yield gen.with_timeout(datetime.timedelta(seconds=3600), future)
01045
01046 @gen_test
01047 def test_already_resolved(self):
01048 future = Future()
01049 future.set_result('asdf')
01050 result = yield gen.with_timeout(datetime.timedelta(seconds=3600),
01051 future)
01052 self.assertEqual(result, 'asdf')
01053
01054 @unittest.skipIf(futures is None, 'futures module not present')
01055 @gen_test
01056 def test_timeout_concurrent_future(self):
01057 with futures.ThreadPoolExecutor(1) as executor:
01058 with self.assertRaises(gen.TimeoutError):
01059 yield gen.with_timeout(self.io_loop.time(),
01060 executor.submit(time.sleep, 0.1))
01061
01062 @unittest.skipIf(futures is None, 'futures module not present')
01063 @gen_test
01064 def test_completed_concurrent_future(self):
01065 with futures.ThreadPoolExecutor(1) as executor:
01066 yield gen.with_timeout(datetime.timedelta(seconds=3600),
01067 executor.submit(lambda: None))
01068
01069
01070 if __name__ == '__main__':
01071 unittest.main()