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):
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)
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):
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'))