blob: e26d77ae2a2c4011f7c9ef1e71b4b1ddca26480e [file] [log] [blame]
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00001"""Utilities for with-statement contexts. See PEP 343."""
2
3import sys
Christian Heimes81ee3ef2008-05-04 22:42:01 +00004from functools import wraps
Raymond Hettinger91e3b9d2009-05-28 22:20:03 +00005from warnings import warn
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00006
Benjamin Peterson04e40c12009-07-01 01:38:23 +00007__all__ = ["contextmanager", "closing"]
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00008
9class GeneratorContextManager(object):
10 """Helper for @contextmanager decorator."""
11
12 def __init__(self, gen):
13 self.gen = gen
14
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000015 def __enter__(self):
16 try:
Georg Brandla18af4e2007-04-21 15:47:16 +000017 return next(self.gen)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000018 except StopIteration:
19 raise RuntimeError("generator didn't yield")
20
21 def __exit__(self, type, value, traceback):
22 if type is None:
23 try:
Georg Brandla18af4e2007-04-21 15:47:16 +000024 next(self.gen)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000025 except StopIteration:
26 return
27 else:
28 raise RuntimeError("generator didn't stop")
29 else:
Guido van Rossum2cc30da2007-11-02 23:46:40 +000030 if value is None:
31 # Need to force instantiation so we can reliably
32 # tell if we get the same exception back
33 value = type()
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000034 try:
35 self.gen.throw(type, value, traceback)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000036 raise RuntimeError("generator didn't stop after throw()")
Guido van Rossumb940e112007-01-10 16:19:56 +000037 except StopIteration as exc:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000038 # Suppress the exception *unless* it's the same exception that
39 # was passed to throw(). This prevents a StopIteration
40 # raised inside the "with" statement from being suppressed
41 return exc is not value
42 except:
43 # only re-raise if it's *not* the exception that was
44 # passed to throw(), because __exit__() must not raise
45 # an exception unless __exit__() itself failed. But throw()
46 # has to raise the exception to signal propagation, so this
47 # fixes the impedance mismatch between the throw() protocol
48 # and the __exit__() protocol.
49 #
50 if sys.exc_info()[1] is not value:
51 raise
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000052
53
54def contextmanager(func):
55 """@contextmanager decorator.
56
57 Typical usage:
58
59 @contextmanager
60 def some_generator(<arguments>):
61 <setup>
62 try:
63 yield <value>
64 finally:
65 <cleanup>
66
67 This makes this:
68
69 with some_generator(<arguments>) as <variable>:
70 <body>
71
72 equivalent to this:
73
74 <setup>
75 try:
76 <variable> = <value>
77 <body>
78 finally:
79 <cleanup>
80
81 """
Christian Heimes81ee3ef2008-05-04 22:42:01 +000082 @wraps(func)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000083 def helper(*args, **kwds):
84 return GeneratorContextManager(func(*args, **kwds))
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000085 return helper
86
87
Thomas Wouters477c8d52006-05-27 19:21:47 +000088class closing(object):
89 """Context to automatically close something at the end of a block.
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000090
91 Code like this:
92
93 with closing(<module>.open(<arguments>)) as f:
94 <block>
95
96 is equivalent to this:
97
98 f = <module>.open(<arguments>)
99 try:
100 <block>
101 finally:
102 f.close()
103
104 """
Thomas Wouters477c8d52006-05-27 19:21:47 +0000105 def __init__(self, thing):
106 self.thing = thing
107 def __enter__(self):
108 return self.thing
109 def __exit__(self, *exc_info):
110 self.thing.close()