Viewing file: contextlib.py (4.04 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
"""Utilities for with-statement contexts. See PEP 343."""
import sys from functools import wraps
__all__ = ["contextmanager", "nested", "closing"]
class GeneratorContextManager(object): """Helper for @contextmanager decorator."""
def __init__(self, gen): self.gen = gen
def __enter__(self): try: return self.gen.next() except StopIteration: raise RuntimeError("generator didn't yield")
def __exit__(self, type, value, traceback): if type is None: try: self.gen.next() except StopIteration: return else: raise RuntimeError("generator didn't stop") else: if value is None: # Need to force instantiation so we can reliably # tell if we get the same exception back value = type() try: self.gen.throw(type, value, traceback) raise RuntimeError("generator didn't stop after throw()") except StopIteration, 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 except: # 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()[1] is not value: raise
def contextmanager(func): """@contextmanager decorator.
Typical usage:
@contextmanager def some_generator(<arguments>): <setup> try: yield <value> finally: <cleanup>
This makes this:
with some_generator(<arguments>) as <variable>: <body>
equivalent to this:
<setup> try: <variable> = <value> <body> finally: <cleanup>
""" @wraps(func) def helper(*args, **kwds): return GeneratorContextManager(func(*args, **kwds)) return helper
@contextmanager def nested(*managers): """Support multiple context managers in a single with-statement.
Code like this:
with nested(A, B, C) as (X, Y, Z): <body>
is equivalent to this:
with A as X: with B as Y: with C as Z: <body>
""" exits = [] vars = [] exc = (None, None, None) try: for mgr in managers: exit = mgr.__exit__ enter = mgr.__enter__ vars.append(enter()) exits.append(exit) yield vars except: exc = sys.exc_info() finally: while exits: exit = exits.pop() try: if exit(*exc): exc = (None, None, None) except: exc = sys.exc_info() if exc != (None, None, None): # Don't rely on sys.exc_info() still containing # the right information. Another exception may # have been raised and caught by an exit method raise exc[0], exc[1], exc[2]
class closing(object): """Context to automatically close something at the end of a block.
Code like this:
with closing(<module>.open(<arguments>)) as f: <block>
is equivalent to this:
f = <module>.open(<arguments>) try: <block> finally: f.close()
""" def __init__(self, thing): self.thing = thing def __enter__(self): return self.thing def __exit__(self, *exc_info): self.thing.close()
|