summaryrefslogtreecommitdiff
path: root/Lib/contextlib.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2006-02-28 21:57:43 +0000
committerGuido van Rossum <guido@python.org>2006-02-28 21:57:43 +0000
commit1a5e21e0334a6d4e1c756575023c7157fc9ee306 (patch)
treed2c1c9383b3c6d8194449ae756e663b0b0ac9e4e /Lib/contextlib.py
parent87a8b4fee56b8204ee9f7b0ce2e5db0564e8f86e (diff)
downloadcpython-git-1a5e21e0334a6d4e1c756575023c7157fc9ee306.tar.gz
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.py138
1 files changed, 138 insertions, 0 deletions
diff --git a/Lib/contextlib.py b/Lib/contextlib.py
new file mode 100644
index 0000000000..33d83a6e14
--- /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()