]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Fix gen.engine docs on decorator order.
authorBen Darnell <ben@bendarnell.com>
Mon, 23 Jan 2012 17:45:29 +0000 (09:45 -0800)
committerBen Darnell <ben@bendarnell.com>
Mon, 23 Jan 2012 17:45:29 +0000 (09:45 -0800)
Add some more test cases that use gen.engine and web.asynchronous together.

tornado/gen.py
tornado/test/gen_test.py

index 74c3bf9d18e3b2556f001c24daf7c25b6033c147..51be53760187084ade525a799973098a9e8b6af9 100644 (file)
@@ -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):
index 15c30ab6d71397e99a8c110729f0307280dfd493..935b409482cff079458d87a5f3f64555e3f5ef10 100644 (file)
@@ -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'))