]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Recognize other yieldables in IOLoop._run_callback.
authorBen Darnell <ben@bendarnell.com>
Sun, 4 Oct 2015 00:42:48 +0000 (20:42 -0400)
committerBen Darnell <ben@bendarnell.com>
Sun, 4 Oct 2015 02:28:14 +0000 (22:28 -0400)
tornado/ioloop.py
tornado/test/ioloop_test.py

index 742a8487914f106e756cef9cc7064c2c75ccbea9..c23cb33e480e1aa35e3ea5789557556f21f66f1a 100644 (file)
@@ -598,12 +598,21 @@ class IOLoop(Configurable):
         """
         try:
             ret = callback()
-            if ret is not None and is_future(ret):
+            if ret is not None:
+                from tornado import gen
                 # Functions that return Futures typically swallow all
                 # exceptions and store them in the Future.  If a Future
                 # makes it out to the IOLoop, ensure its exception (if any)
                 # gets logged too.
-                self.add_future(ret, lambda f: f.result())
+                try:
+                    ret = gen.convert_yielded(ret)
+                except gen.BadYieldError:
+                    # It's not unusual for add_callback to be used with
+                    # methods returning a non-None and non-yieldable
+                    # result, which should just be ignored.
+                    pass
+                else:
+                    self.add_future(ret, lambda f: f.result())
         except Exception:
             self.handle_callback_exception(callback)
 
index 8c2edd2f9962cf73087a90c762ba17bac7a3af2d..71b4ef873e389691c7c33c09e65ffd641127ce0d 100644 (file)
@@ -363,6 +363,18 @@ class TestIOLoop(AsyncTestCase):
             with ExpectLog(app_log, "Exception in callback"):
                 self.wait()
 
+    @skipBefore35
+    def test_exception_logging_native_coro(self):
+        """The IOLoop examines exceptions from awaitables and logs them."""
+        namespace = exec_test(globals(), locals(), """
+        async def callback():
+            self.io_loop.add_callback(self.stop)
+            1 / 0
+        """)
+        with NullContext():
+            self.io_loop.add_callback(namespace["callback"])
+            with ExpectLog(app_log, "Exception in callback"):
+                self.wait()
     def test_spawn_callback(self):
         # An added callback runs in the test's stack_context, so will be
         # re-arised in wait().