• Guido van Rossum's avatar
    Merged revisions 58742-58816 via svnmerge from · 2cc30daa
    Guido van Rossum yazdı
    svn+ssh://pythondev@svn.python.org/python/trunk
    
    ........
      r58745 | georg.brandl | 2007-11-01 10:19:33 -0700 (Thu, 01 Nov 2007) | 2 lines
    
      #1364: os.lstat is available on Windows too, as an alias to os.stat.
    ........
      r58750 | christian.heimes | 2007-11-01 12:48:10 -0700 (Thu, 01 Nov 2007) | 1 line
    
      Backport of import tests for bug http://bugs.python.org/issue1293 and bug http://bugs.python.org/issue1342
    ........
      r58751 | christian.heimes | 2007-11-01 13:11:06 -0700 (Thu, 01 Nov 2007) | 1 line
    
      Removed non ASCII text from test as requested by Guido. Sorry :/
    ........
      r58753 | georg.brandl | 2007-11-01 13:37:02 -0700 (Thu, 01 Nov 2007) | 2 lines
    
      Fix markup glitch.
    ........
      r58757 | gregory.p.smith | 2007-11-01 14:08:14 -0700 (Thu, 01 Nov 2007) | 4 lines
    
      Fix bug introduced in revision 58385.  Database keys could no longer
      have NULL bytes in them.  Replace the errant strdup with a
      malloc+memcpy.  Adds a unit test for the correct behavior.
    ........
      r58758 | gregory.p.smith | 2007-11-01 14:15:36 -0700 (Thu, 01 Nov 2007) | 3 lines
    
      Undo revision 58533 58534 fixes.  Those were a workaround for
      a problem introduced by 58385.
    ........
      r58759 | gregory.p.smith | 2007-11-01 14:17:47 -0700 (Thu, 01 Nov 2007) | 2 lines
    
      false "fix" undone as correct problem was found and fixed.
    ........
      r58765 | mark.summerfield | 2007-11-02 01:24:59 -0700 (Fri, 02 Nov 2007) | 3 lines
    
      Added more file-handling related cross-references.
    ........
      r58766 | nick.coghlan | 2007-11-02 03:09:12 -0700 (Fri, 02 Nov 2007) | 1 line
    
      Fix for bug 1705170 - contextmanager swallowing StopIteration (2.5 backport candidate)
    ........
      r58784 | thomas.heller | 2007-11-02 12:10:24 -0700 (Fri, 02 Nov 2007) | 4 lines
    
      Issue #1292: On alpha, arm, ppc, and s390 linux systems the
      --with-system-ffi configure option defaults to "yes" because the
      bundled libffi sources are too old.
    ........
      r58785 | thomas.heller | 2007-11-02 12:11:23 -0700 (Fri, 02 Nov 2007) | 1 line
    
      Enable the full ctypes c_longdouble tests again.
    ........
      r58796 | georg.brandl | 2007-11-02 13:06:17 -0700 (Fri, 02 Nov 2007) | 4 lines
    
      Make "hashable" a glossary entry and clarify docs on __cmp__, __eq__ and __hash__.
      I hope the concept of hashability is better understandable now.
      Thanks to Tim Hatch for pointing out the flaws here.
    ........
    2cc30daa
contextlib.py 4.2 KB
"""Utilities for with-statement contexts.  See PEP 343."""

import sys

__all__ = ["contextmanager", "nested", "closing"]

class GeneratorContextManager(object):
    """Helper for @contextmanager decorator."""

    def __init__(self, gen):
        self.gen = gen

    def __enter__(self):
        try:
            return next(self.gen)
        except StopIteration:
            raise RuntimeError("generator didn't yield")

    def __exit__(self, type, value, traceback):
        if type is None:
            try:
                next(self.gen)
            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 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
            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>

    """
    def helper(*args, **kwds):
        return GeneratorContextManager(func(*args, **kwds))
    try:
        helper.__name__ = func.__name__
        helper.__doc__ = func.__doc__
        helper.__dict__ = func.__dict__
    except:
        pass
    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
            # exc[1] already has the __traceback__ attribute populated
            raise exc[1]


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()