Tornado update
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
'''StackContext allows applications to maintain threadlocal-like state
|
||||
"""StackContext allows applications to maintain threadlocal-like state
|
||||
that follows execution as it moves to other execution contexts.
|
||||
|
||||
The motivating examples are to eliminate the need for explicit
|
||||
@@ -64,9 +64,9 @@ Here are a few rules of thumb for when it's necessary:
|
||||
persist across asynchronous calls, create a new `StackContext` (or
|
||||
`ExceptionStackContext`), and make your asynchronous calls in a ``with``
|
||||
block that references your `StackContext`.
|
||||
'''
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, with_statement
|
||||
from __future__ import absolute_import, division, print_function, with_statement
|
||||
|
||||
import contextlib
|
||||
import functools
|
||||
@@ -77,6 +77,10 @@ import threading
|
||||
from tornado.util import raise_exc_info
|
||||
|
||||
|
||||
class StackContextInconsistentError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class _State(threading.local):
|
||||
def __init__(self):
|
||||
self.contexts = ()
|
||||
@@ -84,7 +88,7 @@ _state = _State()
|
||||
|
||||
|
||||
class StackContext(object):
|
||||
'''Establishes the given context as a StackContext that will be transferred.
|
||||
"""Establishes the given context as a StackContext that will be transferred.
|
||||
|
||||
Note that the parameter is a callable that returns a context
|
||||
manager, not the context itself. That is, where for a
|
||||
@@ -102,7 +106,7 @@ class StackContext(object):
|
||||
deactivating a context does not affect any instances of that
|
||||
context that are currently pending). This is an advanced feature
|
||||
and not necessary in most applications.
|
||||
'''
|
||||
"""
|
||||
def __init__(self, context_factory, _active_cell=None):
|
||||
self.context_factory = context_factory
|
||||
self.active_cell = _active_cell or [True]
|
||||
@@ -113,8 +117,10 @@ class StackContext(object):
|
||||
def __enter__(self):
|
||||
self.old_contexts = _state.contexts
|
||||
# _state.contexts is a tuple of (class, arg, active_cell) tuples
|
||||
_state.contexts = (self.old_contexts +
|
||||
((StackContext, self.context_factory, self.active_cell),))
|
||||
self.new_contexts = (self.old_contexts +
|
||||
((StackContext, self.context_factory,
|
||||
self.active_cell),))
|
||||
_state.contexts = self.new_contexts
|
||||
try:
|
||||
self.context = self.context_factory()
|
||||
self.context.__enter__()
|
||||
@@ -127,11 +133,23 @@ class StackContext(object):
|
||||
try:
|
||||
return self.context.__exit__(type, value, traceback)
|
||||
finally:
|
||||
final_contexts = _state.contexts
|
||||
_state.contexts = self.old_contexts
|
||||
# Generator coroutines and with-statements with non-local
|
||||
# effects interact badly. Check here for signs of
|
||||
# the stack getting out of sync.
|
||||
# Note that this check comes after restoring _state.context
|
||||
# so that if it fails things are left in a (relatively)
|
||||
# consistent state.
|
||||
if final_contexts is not self.new_contexts:
|
||||
raise StackContextInconsistentError(
|
||||
'stack_context inconsistency (may be caused by yield '
|
||||
'within a "with StackContext" block)')
|
||||
self.old_contexts = self.new_contexts = None
|
||||
|
||||
|
||||
class ExceptionStackContext(object):
|
||||
'''Specialization of StackContext for exception handling.
|
||||
"""Specialization of StackContext for exception handling.
|
||||
|
||||
The supplied exception_handler function will be called in the
|
||||
event of an uncaught exception in this context. The semantics are
|
||||
@@ -142,16 +160,17 @@ class ExceptionStackContext(object):
|
||||
|
||||
If the exception handler returns true, the exception will be
|
||||
consumed and will not be propagated to other exception handlers.
|
||||
'''
|
||||
"""
|
||||
def __init__(self, exception_handler, _active_cell=None):
|
||||
self.exception_handler = exception_handler
|
||||
self.active_cell = _active_cell or [True]
|
||||
|
||||
def __enter__(self):
|
||||
self.old_contexts = _state.contexts
|
||||
_state.contexts = (self.old_contexts +
|
||||
((ExceptionStackContext, self.exception_handler,
|
||||
self.active_cell),))
|
||||
self.new_contexts = (self.old_contexts +
|
||||
((ExceptionStackContext, self.exception_handler,
|
||||
self.active_cell),))
|
||||
_state.contexts = self.new_contexts
|
||||
return lambda: operator.setitem(self.active_cell, 0, False)
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
@@ -159,17 +178,22 @@ class ExceptionStackContext(object):
|
||||
if type is not None:
|
||||
return self.exception_handler(type, value, traceback)
|
||||
finally:
|
||||
final_contexts = _state.contexts
|
||||
_state.contexts = self.old_contexts
|
||||
self.old_contexts = None
|
||||
if final_contexts is not self.new_contexts:
|
||||
raise StackContextInconsistentError(
|
||||
'stack_context inconsistency (may be caused by yield '
|
||||
'within a "with StackContext" block)')
|
||||
self.old_contexts = self.new_contexts = None
|
||||
|
||||
|
||||
class NullContext(object):
|
||||
'''Resets the StackContext.
|
||||
"""Resets the StackContext.
|
||||
|
||||
Useful when creating a shared resource on demand (e.g. an AsyncHTTPClient)
|
||||
where the stack that caused the creating is not relevant to future
|
||||
operations.
|
||||
'''
|
||||
"""
|
||||
def __enter__(self):
|
||||
self.old_contexts = _state.contexts
|
||||
_state.contexts = ()
|
||||
@@ -183,13 +207,13 @@ class _StackContextWrapper(functools.partial):
|
||||
|
||||
|
||||
def wrap(fn):
|
||||
'''Returns a callable object that will restore the current StackContext
|
||||
"""Returns a callable object that will restore the current StackContext
|
||||
when executed.
|
||||
|
||||
Use this whenever saving a callback to be executed later in a
|
||||
different execution context (either in a different thread or
|
||||
asynchronously in the same thread).
|
||||
'''
|
||||
"""
|
||||
if fn is None or fn.__class__ is _StackContextWrapper:
|
||||
return fn
|
||||
# functools.wraps doesn't appear to work on functools.partial objects
|
||||
|
||||
Reference in New Issue
Block a user