From: Ben Darnell Date: Fri, 31 Aug 2012 20:13:40 +0000 (-0400) Subject: Capture stack_context in IOLoop.add_future X-Git-Tag: v3.0.0~263^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e5e15314921affa21cf1fdb305e05c7ee272ffda;p=thirdparty%2Ftornado.git Capture stack_context in IOLoop.add_future --- diff --git a/tornado/ioloop.py b/tornado/ioloop.py index 4e9267240..745bdf898 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -431,6 +431,7 @@ class IOLoop(object): """Schedules a callback on the IOLoop when the given future is finished. """ assert isinstance(future, IOLoop._FUTURE_TYPES) + callback = stack_context.wrap(callback) future.add_done_callback( lambda future: self.add_callback( functools.partial(callback, future))) diff --git a/tornado/test/ioloop_test.py b/tornado/test/ioloop_test.py index 0bac29eeb..bfdc6ca58 100644 --- a/tornado/test/ioloop_test.py +++ b/tornado/test/ioloop_test.py @@ -4,10 +4,12 @@ from __future__ import absolute_import, division, with_statement import datetime import socket +import threading import time from tornado.ioloop import IOLoop from tornado.netutil import bind_sockets +from tornado.stack_context import ExceptionStackContext from tornado.testing import AsyncTestCase, LogTrapTestCase, get_unused_port from tornado.test.util import unittest @@ -62,6 +64,35 @@ class TestIOLoopFutures(AsyncTestCase, LogTrapTestCase): future = self.wait() self.assertTrue(future.done()) self.assertTrue(future.result() is None) + + def test_add_future_stack_context(self): + ready = threading.Event() + def task(): + # we must wait for the ioloop callback to be scheduled before + # the task completes to ensure that add_future adds the callback + # asynchronously (which is the scenario in which capturing + # the stack_context matters) + ready.wait(1) + assert ready.isSet(), "timed out" + raise Exception("worker") + def callback(future): + self.future = future + raise Exception("callback") + def handle_exception(typ, value, traceback): + self.exception = value + self.stop() + return True + + # stack_context propagates to the ioloop callback, but the worker + # task just has its exceptions caught and saved in the Future. + with futures.ThreadPoolExecutor(1) as pool: + with ExceptionStackContext(handle_exception): + self.io_loop.add_future(pool.submit(task), callback) + ready.set() + self.wait() + + self.assertEqual(self.exception.args[0], "callback") + self.assertEqual(self.future.exception().args[0], "worker") TestIOLoopFutures = unittest.skipIf( futures is None, "futures module not present")(TestIOLoopFutures)