From: Ben Darnell Date: Sat, 29 Sep 2012 21:29:22 +0000 (-0700) Subject: Simplify StackContext logic and behavior. X-Git-Tag: v3.0.0~265 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2ded26610f9f3f7962129617bc2b60d1856708da;p=thirdparty%2Ftornado.git Simplify StackContext logic and behavior. Previously, there was an optimization to avoid recreating contexts that were already on the stack. This optimization rarely mattered (especially given the movement to run all callbacks through IOLoop.add_callback), and sometimes caused surprising behavior as other exception handlers could be inserted between stack contexts that expected to be adjacent to each other or to the wrapped function. Now each wrapped function recreates its full stack of contexts, even if some of those contexts were already on the stack. This change allows a use of NullContext in testing.py to be removed. --- diff --git a/tornado/stack_context.py b/tornado/stack_context.py index 4b490e598..b0bf108ac 100644 --- a/tornado/stack_context.py +++ b/tornado/stack_context.py @@ -70,7 +70,6 @@ from __future__ import absolute_import, division, with_statement import contextlib import functools -import itertools import operator import sys import threading @@ -198,31 +197,14 @@ def wrap(fn): def wrapped(*args, **kwargs): callback, contexts, args = args[0], args[1], args[2:] - if contexts is _state.contexts: - callback(*args, **kwargs) - return - if not _state.contexts: - new_contexts = [cls(arg, active_cell) - for (cls, arg, active_cell) in contexts - if active_cell[0]] - # If we're moving down the stack, _state.contexts is a prefix - # of contexts. For each element of contexts not in that prefix, - # create a new StackContext object. - # If we're moving up the stack (or to an entirely different stack), - # _state.contexts will have elements not in contexts. Use - # NullContext to clear the state and then recreate from contexts. - elif (len(_state.contexts) > len(contexts) or - any(a[1] is not b[1] - for a, b in itertools.izip(_state.contexts, contexts))): - # contexts have been removed or changed, so start over - new_contexts = ([NullContext()] + - [cls(arg, active_cell) - for (cls, arg, active_cell) in contexts - if active_cell[0]]) + if _state.contexts: + new_contexts = [NullContext()] else: - new_contexts = [cls(arg, active_cell) - for (cls, arg, active_cell) in contexts[len(_state.contexts):] - if active_cell[0]] + new_contexts = [] + if contexts: + new_contexts.extend(cls(arg, active_cell) + for (cls, arg, active_cell) in contexts + if active_cell[0]) if len(new_contexts) > 1: with _nested(*new_contexts): callback(*args, **kwargs) diff --git a/tornado/testing.py b/tornado/testing.py index 5b529a268..f67841167 100644 --- a/tornado/testing.py +++ b/tornado/testing.py @@ -36,7 +36,7 @@ except ImportError: netutil = None SimpleAsyncHTTPClient = None from tornado.log import gen_log -from tornado.stack_context import StackContext, NullContext +from tornado.stack_context import StackContext from tornado.util import raise_exc_info import contextlib import logging @@ -223,11 +223,7 @@ class AsyncTestCase(unittest.TestCase): self.__timeout = self.io_loop.add_timeout(time.time() + timeout, timeout_func) while True: self.__running = True - with NullContext(): - # Wipe out the StackContext that was established in - # self.run() so that all callbacks executed inside the - # IOLoop will re-run it. - self.io_loop.start() + self.io_loop.start() if (self.__failure is not None or condition is None or condition()): break