Avoid use of the @contextlib.contextmanager decorator.

This decorator has over 5x the overhead of a hand-written class
with __enter__ and __exit__ methods.
This commit is contained in:
Ben Darnell 2010-12-10 15:56:38 -08:00
parent 8e4a7730d3
commit 6e7c8b99d0
2 changed files with 63 additions and 31 deletions

View File

@ -59,39 +59,69 @@ class _State(threading.local):
self.contexts = ()
_state = _State()
@contextlib.contextmanager
def StackContext(context_factory):
'''Establishes the given context as a StackContext that will be transferred.
class StackContext(object):
def __init__(self, context_factory):
'''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
non-transferable context manager you would say
with my_context():
StackContext takes the function itself rather than its result:
with StackContext(my_context):
Note that the parameter is a callable that returns a context
manager, not the context itself. That is, where for a
non-transferable context manager you would say
with my_context():
StackContext takes the function itself rather than its result:
with StackContext(my_context):
'''
self.context_factory = context_factory
def __enter__(self):
self.old_contexts = _state.contexts
_state.contexts = self.old_contexts + (self.context_factory,)
try:
self.context = self.context_factory()
self.context.__enter__()
except Exception:
_state.contexts = self.old_contexts
raise
def __exit__(self, type, value, traceback):
try:
return self.context.__exit__(type, value, traceback)
finally:
_state.contexts = self.old_contexts
def ExceptionStackContext(exception_handler):
'''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
similar to a try/finally clause, and intended use cases are to log
an error, close a socket, or similar cleanup actions. The
exc_info triple (type, value, traceback) will be passed to the
exception_handler function.
If the exception handler returns true, the exception will be
consumed and will not be propagated to other exception handlers.
'''
old_contexts = _state.contexts
try:
_state.contexts = old_contexts + (context_factory,)
with context_factory():
yield
finally:
_state.contexts = old_contexts
class Context(object):
def __enter__(self):
pass
def __exit__(self, type, value, traceback):
if type is not None:
return exception_handler(type, value, traceback)
return StackContext(Context)
@contextlib.contextmanager
def NullContext():
class NullContext(object):
'''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.
'''
old_contexts = _state.contexts
try:
def __enter__(self):
self.old_contexts = _state.contexts
_state.contexts = ()
yield
finally:
_state.contexts = old_contexts
def __exit__(self, type, value, traceback):
_state.contexts = self.old_contexts
def wrap(fn):
'''Returns a callable object that will resore the current StackContext
@ -121,9 +151,12 @@ def wrap(fn):
else:
new_contexts = [StackContext(c)
for c in contexts[len(_state.contexts):]]
if new_contexts:
if len(new_contexts) > 1:
with contextlib.nested(*new_contexts):
callback(*args, **kwargs)
elif new_contexts:
with new_contexts[0]:
callback(*args, **kwargs)
else:
callback(*args, **kwargs)
if getattr(fn, 'stack_context_wrapped', False):
@ -132,3 +165,4 @@ def wrap(fn):
result = functools.partial(wrapped, fn, contexts)
result.stack_context_wrapped = True
return result

View File

@ -809,17 +809,15 @@ class RequestHandler(object):
def reverse_url(self, name, *args):
return self.application.reverse_url(name, *args)
@contextlib.contextmanager
def _stack_context(self):
try:
yield
except Exception, e:
self._handle_request_exception(e)
def _stack_context_handle_exception(self, type, value, traceback):
self._handle_request_exception(value)
return True
def _execute(self, transforms, *args, **kwargs):
"""Executes this request with the given output transforms."""
self._transforms = transforms
with stack_context.StackContext(self._stack_context):
with stack_context.ExceptionStackContext(
self._stack_context_handle_exception):
if self.request.method not in self.SUPPORTED_METHODS:
raise HTTPError(405)
# If XSRF cookies are turned on, reject form submissions without