blob: 282fc51004554476d685a264eee131b86c64d297 [file] [log] [blame]
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00001"""Utilities for with-statement contexts. See PEP 343."""
2
3import sys
4
5__all__ = ["contextmanager", "nested", "closing"]
6
7class GeneratorContextManager(object):
8 """Helper for @contextmanager decorator."""
9
10 def __init__(self, gen):
11 self.gen = gen
12
13 def __context__(self):
14 return self
15
16 def __enter__(self):
17 try:
18 return self.gen.next()
19 except StopIteration:
20 raise RuntimeError("generator didn't yield")
21
22 def __exit__(self, type, value, traceback):
23 if type is None:
24 try:
25 self.gen.next()
26 except StopIteration:
27 return
28 else:
29 raise RuntimeError("generator didn't stop")
30 else:
31 try:
32 self.gen.throw(type, value, traceback)
Phillip J. Eby6edd2582006-03-25 00:28:24 +000033 raise RuntimeError("generator didn't stop after throw()")
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000034 except StopIteration:
Guido van Rossumf6694362006-03-10 02:28:35 +000035 return True
Phillip J. Eby6edd2582006-03-25 00:28:24 +000036 except:
37 if sys.exc_info()[1] is not value:
38 raise
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000039
40
41def contextmanager(func):
42 """@contextmanager decorator.
43
44 Typical usage:
45
46 @contextmanager
47 def some_generator(<arguments>):
48 <setup>
49 try:
50 yield <value>
51 finally:
52 <cleanup>
53
54 This makes this:
55
56 with some_generator(<arguments>) as <variable>:
57 <body>
58
59 equivalent to this:
60
61 <setup>
62 try:
63 <variable> = <value>
64 <body>
65 finally:
66 <cleanup>
67
68 """
69 def helper(*args, **kwds):
70 return GeneratorContextManager(func(*args, **kwds))
71 try:
72 helper.__name__ = func.__name__
73 helper.__doc__ = func.__doc__
74 except:
75 pass
76 return helper
77
78
79@contextmanager
80def nested(*contexts):
81 """Support multiple context managers in a single with-statement.
82
83 Code like this:
84
85 with nested(A, B, C) as (X, Y, Z):
86 <body>
87
88 is equivalent to this:
89
90 with A as X:
91 with B as Y:
92 with C as Z:
93 <body>
94
95 """
96 exits = []
97 vars = []
Guido van Rossumf6694362006-03-10 02:28:35 +000098 exc = (None, None, None)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000099 try:
100 try:
101 for context in contexts:
102 mgr = context.__context__()
103 exit = mgr.__exit__
104 enter = mgr.__enter__
105 vars.append(enter())
106 exits.append(exit)
107 yield vars
108 except:
109 exc = sys.exc_info()
110 finally:
111 while exits:
112 exit = exits.pop()
113 try:
Guido van Rossumf6694362006-03-10 02:28:35 +0000114 if exit(*exc):
115 exc = (None, None, None)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000116 except:
117 exc = sys.exc_info()
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000118 if exc != (None, None, None):
119 raise
120
121
122@contextmanager
123def closing(thing):
124 """Context manager to automatically close something at the end of a block.
125
126 Code like this:
127
128 with closing(<module>.open(<arguments>)) as f:
129 <block>
130
131 is equivalent to this:
132
133 f = <module>.open(<arguments>)
134 try:
135 <block>
136 finally:
137 f.close()
138
139 """
140 try:
141 yield thing
142 finally:
143 thing.close()