future = Future()
callback, args, kwargs = replacer.replace(future.set_result,
args, kwargs)
- if callback is not None:
- future.add_done_callback(wrap(callback))
def handle_error(typ, value, tb):
future.set_exception(value)
# go ahead and raise it to the caller directly without waiting
# for them to inspect the Future.
raise_exc_info(exc_info)
+
+ # If the caller passed in a callback, schedule it to be called
+ # when the future resolves. It is important that this happens
+ # just before we return the future, or else we risk confusing
+ # stack contexts with multiple exceptions (one here with the
+ # immediate exception, and again when the future resolves and
+ # the callback triggers its exception by calling future.result()).
+ if callback is not None:
+ future.add_done_callback(wrap(callback))
return future
return wrapper
with self.assertRaises(ZeroDivisionError):
# The caller sees the error just like a normal function.
self.immediate_failure(callback=self.stop)
- # The callback is also run, with a future that contains the error.
- future = self.wait()
- with self.assertRaises(ZeroDivisionError):
- future.result()
+ # The callback is not run because the function failed synchronously.
+ self.io_loop.add_timeout(self.io_loop.time() + 0.05, self.stop)
+ result = self.wait()
+ self.assertIs(result, None)
def test_return_value(self):
with self.assertRaises(ReturnValueIgnoredError):
self.return_value(callback=self.stop)
- future = self.wait()
- with self.assertRaises(ReturnValueIgnoredError):
- future.result()
def test_callback_kw(self):
future = self.sync_future(callback=self.stop)