From: Ben Darnell Date: Mon, 23 Jan 2012 17:45:29 +0000 (-0800) Subject: Fix gen.engine docs on decorator order. X-Git-Tag: v2.2.0~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7fd178840b45a5d949f1dafd2346fbc89b35eec7;p=thirdparty%2Ftornado.git Fix gen.engine docs on decorator order. Add some more test cases that use gen.engine and web.asynchronous together. --- diff --git a/tornado/gen.py b/tornado/gen.py index 74c3bf9d1..51be53760 100644 --- a/tornado/gen.py +++ b/tornado/gen.py @@ -82,10 +82,12 @@ def engine(func): Any generator that yields objects from this module must be wrapped in this decorator. The decorator only works on functions that are already asynchronous. For `~tornado.web.RequestHandler` - ``get``/``post``/etc methods, this means that both the `tornado.gen.engine` - and `tornado.web.asynchronous` decorators must be used (in either order). - In most other cases, it means that it doesn't make sense to use - ``gen.engine`` on functions that don't already take a callback argument. + ``get``/``post``/etc methods, this means that both the + `tornado.web.asynchronous` and `tornado.gen.engine` decorators + must be used (for proper exception handling, ``asynchronous`` + should come before ``gen.engine``). In most other cases, it means + that it doesn't make sense to use ``gen.engine`` on functions that + don't already take a callback argument. """ @functools.wraps(func) def wrapper(*args, **kwargs): diff --git a/tornado/test/gen_test.py b/tornado/test/gen_test.py index 15c30ab6d..935b40948 100644 --- a/tornado/test/gen_test.py +++ b/tornado/test/gen_test.py @@ -71,6 +71,7 @@ class GenTest(AsyncTestCase): self.run_gen(f) def test_exception_in_task_phase2(self): + # This is the case that requires the use of stack_context in gen.engine def fail_task(callback): self.io_loop.add_callback(lambda: 1/0) @@ -273,11 +274,36 @@ class GenTaskHandler(RequestHandler): response.rethrow() self.finish(b("got response: ") + response.body) +class GenExceptionHandler(RequestHandler): + @asynchronous + @gen.engine + def get(self): + # This test depends on the order of the two decorators. + io_loop = self.request.connection.stream.io_loop + yield gen.Task(io_loop.add_callback) + raise Exception("oops") + +class GenYieldExceptionHandler(RequestHandler): + @asynchronous + @gen.engine + def get(self): + io_loop = self.request.connection.stream.io_loop + # Test the interaction of the two stack_contexts. + def fail_task(callback): + io_loop.add_callback(lambda: 1/0) + try: + yield gen.Task(fail_task) + raise Exception("did not get expected exception") + except ZeroDivisionError: + self.finish('ok') + class GenWebTest(AsyncHTTPTestCase, LogTrapTestCase): def get_app(self): return Application([ ('/sequence', GenSequenceHandler), ('/task', GenTaskHandler), + ('/exception', GenExceptionHandler), + ('/yield_exception', GenYieldExceptionHandler), ]) def test_sequence_handler(self): @@ -287,3 +313,12 @@ class GenWebTest(AsyncHTTPTestCase, LogTrapTestCase): def test_task_handler(self): response = self.fetch('/task?url=%s' % url_escape(self.get_url('/sequence'))) self.assertEqual(response.body, b("got response: 123")) + + def test_exception_handler(self): + # Make sure we get an error and not a timeout + response = self.fetch('/exception') + self.assertEqual(500, response.code) + + def test_yield_exception_handler(self): + response = self.fetch('/yield_exception') + self.assertEqual(response.body, b('ok'))