diff options
author | Guido van Rossum <guido@python.org> | 2006-02-28 21:57:43 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2006-02-28 21:57:43 (GMT) |
commit | 1a5e21e0334a6d4e1c756575023c7157fc9ee306 (patch) | |
tree | d2c1c9383b3c6d8194449ae756e663b0b0ac9e4e /Lib/contextlib.py | |
parent | 87a8b4fee56b8204ee9f7b0ce2e5db0564e8f86e (diff) | |
download | cpython-1a5e21e0334a6d4e1c756575023c7157fc9ee306.zip cpython-1a5e21e0334a6d4e1c756575023c7157fc9ee306.tar.gz cpython-1a5e21e0334a6d4e1c756575023c7157fc9ee306.tar.bz2 |
Updates to the with-statement:
- New semantics for __exit__() -- it must re-raise the exception
if type is not None; the with-statement itself doesn't do this.
(See the updated PEP for motivation.)
- Added context managers to:
- file
- thread.LockType
- threading.{Lock,RLock,Condition,Semaphore,BoundedSemaphore}
- decimal.Context
- Added contextlib.py, which defines @contextmanager, nested(), closing().
- Unit tests all around; bot no docs yet.
Diffstat (limited to 'Lib/contextlib.py')
-rw-r--r-- | Lib/contextlib.py | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/Lib/contextlib.py b/Lib/contextlib.py new file mode 100644 index 0000000..33d83a6 --- /dev/null +++ b/Lib/contextlib.py @@ -0,0 +1,138 @@ +"""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 __context__(self): + return self + + 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: + try: + self.gen.throw(type, value, traceback) + except StopIteration: + pass + + +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__ + except: + pass + return helper + + +@contextmanager +def nested(*contexts): + """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: + try: + for context in contexts: + mgr = context.__context__() + 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: + exit(*exc) + except: + exc = sys.exc_info() + if exc != (None, None, None): + raise + + +@contextmanager +def closing(thing): + """Context manager 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() + + """ + try: + yield thing + finally: + thing.close() |