"""Utilities for with-statement contexts. See PEP 343."""
from collections import deque
from functools import wraps
__all__ = ["contextmanager", "closing", "ContextDecorator", "ExitStack", "ignored"]
"A base class or mixin that enables context managers to work as decorators."
"""Return a recreated instance of self.
Allows an otherwise one-shot context manager like
_GeneratorContextManager to support use as
a decorator via implicit recreation.
This is a private interface just for _GeneratorContextManager.
See issue #11647 for details.
def __call__(self, func):
def inner(*args, **kwds):
return func(*args, **kwds)
"""Helper for @contextmanager decorator."""
def __init__(self, func, *args, **kwds):
self.gen = func(*args, **kwds)
self.func, self.args, self.kwds = func, args, kwds
# _GCM instances are one-shot context managers, so the
# CM must be recreated each time a decorated function is
return self.__class__(self.func, *self.args, **self.kwds)
raise RuntimeError("generator didn't yield")
def __exit__(self, type, value, traceback):
if type is None:
raise RuntimeError("generator didn't stop")
if value is None:
# Need to force instantiation so we can reliably
# tell if we get the same exception back
value = type()
self.gen.throw(type, value, traceback)
raise RuntimeError("generator didn't stop after throw()")
except StopIteration as exc:
# Suppress the exception *unless* it's the same exception that
# was passed to throw(). This prevents a StopIteration
# raised inside the "with" statement from being suppressed
return exc is not value
# only re-raise if it's *not* the exception that was
# passed to throw(), because __exit__() must not raise
# an exception unless __exit__() itself failed. But throw()
# has to raise the exception to signal propagation, so this
# fixes the impedance mismatch between the throw() protocol
# and the __exit__() protocol.
if sys.exc_info() is not value:
This makes this:
with some_generator(<arguments>) as <variable>:
equivalent to this:
<variable> = <value>
def helper(*args, **kwds):
return _GeneratorContextManager(func, *args, **kwds)
"""Context to automatically close something at the end of a block.
Code like this:
with closing(<module>.open(<arguments>)) as f:
is equivalent to this:
f = <module>.open(<arguments>)
def __init__(self, thing):
self.thing = thing
def __exit__(self, *exc_info):
"""Context manager to ignore specifed exceptions
# Inspired by discussions on http://bugs.python.org/issue13585
"""Context manager for dynamic management of a stack of exit callbacks
with ExitStack() as stack:
files = [stack.enter_context(open(fname)) for fname in filenames]
# All opened files will automatically be closed at the end of
# the with statement, even if attempts to open files later
# in the list raise an exception
self._exit_callbacks = deque()
"""Preserve the context stack by transferring it to a new instance"""
new_stack = type(self)()
new_stack._exit_callbacks = self._exit_callbacks
self._exit_callbacks = deque()
def _push_cm_exit(self, cm, cm_exit):
"""Helper to correctly register callbacks to __exit__ methods"""
return cm_exit(cm, *exc_details)
_exit_wrapper.__self__ = cm
def push(self, exit):
"""Registers a callback with the standard __exit__ method signature
Can suppress exceptions the same way __exit__ methods can.
Also accepts any object with an __exit__ method (registering a call
to the method instead of the object itself)
# We use an unbound method rather than a bound method to follow
# the standard lookup behaviour for special methods
_cb_type = type(exit)
exit_method = _cb_type.__exit__
# Not a context manager, so assume its a callable
return exit # Allow use as a decorator
def callback(self, callback, *args, **kwds):
"""Registers an arbitrary callback and arguments.
Cannot suppress exceptions.
def _exit_wrapper(exc_type, exc, tb):
# We changed the signature, so using @wraps is not appropriate, but
# setting __wrapped__ may still help with introspection
_exit_wrapper.__wrapped__ = callback
return callback # Allow use as a decorator
def enter_context(self, cm):
"""Enters the supplied context manager
If successful, also pushes its __exit__ method as a callback and
returns the result of the __enter__ method.
# We look up the special methods on the type to match the with statement
_cm_type = type(cm)
_exit = _cm_type.__exit__
result = _cm_type.__enter__(cm)
"""Immediately unwind the context stack"""
self.__exit__(None, None, None)
def __exit__(self, *exc_details):
# We manipulate the exception state so it behaves as though
# we were actually nesting multiple with statements
frame_exc = sys.exc_info()
def _fix_exception_context(new_exc, old_exc):
exc_context = new_exc.__context__
if exc_context in (None, frame_exc):
new_exc = exc_context
new_exc.__context__ = old_exc
# Callbacks are invoked in LIFO order to match the behaviour of
# nested context managers
suppressed_exc = False
cb = self._exit_callbacks.pop()
suppressed_exc = True
exc_details = (None, None, None)
new_exc_details = sys.exc_info()
# simulate the stack of exceptions by setting the context
if not self._exit_callbacks:
exc_details = new_exc_details