]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
TwistedIOLoop now supports overriding handle_callback_exception.
authorBen Darnell <ben@bendarnell.com>
Sun, 9 Dec 2012 00:17:27 +0000 (19:17 -0500)
committerBen Darnell <ben@bendarnell.com>
Sun, 9 Dec 2012 00:17:27 +0000 (19:17 -0500)
tornado/platform/twisted.py
tornado/test/ioloop_test.py

index 8aec9377585f62f030527f97062a996a861b6974..1efc82b70aeb3d4f0ec500d74e5c46d433b18727 100644 (file)
@@ -446,6 +446,12 @@ class TwistedIOLoop(tornado.ioloop.IOLoop):
     def stop(self):
         self.reactor.crash()
 
+    def _run_callback(self, callback, *args, **kwargs):
+        try:
+            callback(*args, **kwargs)
+        except Exception:
+            self.handle_callback_exception(callback)
+
     def add_timeout(self, deadline, callback):
         if isinstance(deadline, (int, long, float)):
             delay = max(deadline - self.time(), 0)
@@ -453,14 +459,14 @@ class TwistedIOLoop(tornado.ioloop.IOLoop):
             delay = deadline.total_seconds()
         else:
             raise TypeError("Unsupported deadline %r")
-        return self.reactor.callLater(delay, wrap(callback))
+        return self.reactor.callLater(delay, self._run_callback, wrap(callback))
 
     def remove_timeout(self, timeout):
         timeout.cancel()
 
     def add_callback(self, callback, *args, **kwargs):
-        self.reactor.callFromThread(functools.partial(wrap(callback),
-                                                      *args, **kwargs))
+        self.reactor.callFromThread(self._run_callback,
+                                    wrap(callback), *args, **kwargs)
 
     def add_callback_from_signal(self, callback, *args, **kwargs):
         self.add_callback(callback, *args, **kwargs)
index 1f8e5401a44fc845f2dc5fa2f11d6de997458043..53bb8a4491290a10466260d37583ee505c3f1a25 100644 (file)
@@ -5,11 +5,12 @@ from __future__ import absolute_import, division, with_statement
 import contextlib
 import datetime
 import functools
+import sys
 import threading
 import time
 
 from tornado.ioloop import IOLoop
-from tornado.stack_context import ExceptionStackContext, StackContext, wrap
+from tornado.stack_context import ExceptionStackContext, StackContext, wrap, NullContext
 from tornado.testing import AsyncTestCase, bind_unused_port
 from tornado.test.util import unittest
 
@@ -113,6 +114,19 @@ class TestIOLoop(AsyncTestCase):
                 self.assertEqual("IOLoop is closing", str(e))
                 break
 
+    def test_handle_callback_exception(self):
+        # IOLoop.handle_callback_exception can be overridden to catch
+        # exceptions in callbacks.
+        def handle_callback_exception(callback):
+            self.assertIs(sys.exc_info()[0], ZeroDivisionError)
+            self.stop()
+        self.io_loop.handle_callback_exception = handle_callback_exception
+        with NullContext():
+            # remove the test StackContext that would see this uncaught
+            # exception as a test failure.
+            self.io_loop.add_callback(lambda: 1 / 0)
+        self.wait()
+
 
 class TestIOLoopAddCallback(AsyncTestCase):
     def setUp(self):