from tornado.concurrent import Future, TracebackFuture, is_future
from tornado.ioloop import IOLoop
-from tornado.stack_context import ExceptionStackContext, wrap
+from tornado import stack_context
class KeyReuseError(Exception):
typ, value, tb = sys.exc_info()
future.set_exc_info((typ, value, tb))
return True
- with ExceptionStackContext(handle_exception) as deactivate:
+ with stack_context.ExceptionStackContext(handle_exception) as deactivate:
future.add_done_callback(lambda f: deactivate())
try:
result = func(*args, **kwargs)
except Exception:
self.exc_info = sys.exc_info()
try:
+ orig_stack_contexts = stack_context._state.contexts
if self.exc_info is not None:
self.had_exception = True
exc_info = self.exc_info
yielded = self.gen.throw(*exc_info)
else:
yielded = self.gen.send(next)
+ if stack_context._state.contexts is not orig_stack_contexts:
+ self.gen.throw(
+ stack_context.StackContextInconsistentError(
+ 'stack_context inconsistency (probably caused '
+ 'by yield within a "with StackContext" block)'))
except (StopIteration, Return) as e:
self.finished = True
self.future = _null_future
else:
result = None
self.set_result(key, result)
- return wrap(inner)
+ return stack_context.wrap(inner)
def handle_exception(self, typ, value, tb):
if not self.running and not self.finished:
def test_yield_in_with(self):
@gen.engine
def f():
- try:
- self.callback = yield gen.Callback('a')
- with StackContext(functools.partial(self.context, 'c1')):
- # This yield is a problem: the generator will be suspended
- # and the StackContext's __exit__ is not called yet, so
- # the context will be left on _state.contexts for anything
- # that runs before the yield resolves.
- yield gen.Wait('a')
- except StackContextInconsistentError:
- # In python <= 3.3, this suspended generator is never garbage
- # collected, so it remains suspended in the 'yield' forever.
- # Starting in 3.4, it is made collectable by raising
- # a GeneratorExit exception from the yield, which gets
- # converted into a StackContextInconsistentError by the
- # exit of the 'with' block.
- pass
+ self.callback = yield gen.Callback('a')
+ with StackContext(functools.partial(self.context, 'c1')):
+ # This yield is a problem: the generator will be suspended
+ # and the StackContext's __exit__ is not called yet, so
+ # the context will be left on _state.contexts for anything
+ # that runs before the yield resolves.
+ yield gen.Wait('a')
with self.assertRaises(StackContextInconsistentError):
f()
# As above, but with ExceptionStackContext instead of StackContext.
@gen.engine
def f():
- try:
- with ExceptionStackContext(lambda t, v, tb: False):
- yield gen.Task(self.io_loop.add_callback)
- except StackContextInconsistentError:
- pass
+ with ExceptionStackContext(lambda t, v, tb: False):
+ yield gen.Task(self.io_loop.add_callback)
with self.assertRaises(StackContextInconsistentError):
f()