blob: 33c302dd96fbcf0ee00ab1a0274d88b1b3224196 [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)
33 except StopIteration:
34 pass
35
36
37def contextmanager(func):
38 """@contextmanager decorator.
39
40 Typical usage:
41
42 @contextmanager
43 def some_generator(<arguments>):
44 <setup>
45 try:
46 yield <value>
47 finally:
48 <cleanup>
49
50 This makes this:
51
52 with some_generator(<arguments>) as <variable>:
53 <body>
54
55 equivalent to this:
56
57 <setup>
58 try:
59 <variable> = <value>
60 <body>
61 finally:
62 <cleanup>
63
64 """
65 def helper(*args, **kwds):
66 return GeneratorContextManager(func(*args, **kwds))
67 try:
68 helper.__name__ = func.__name__
69 helper.__doc__ = func.__doc__
70 except:
71 pass
72 return helper
73
74
75@contextmanager
76def nested(*contexts):
77 """Support multiple context managers in a single with-statement.
78
79 Code like this:
80
81 with nested(A, B, C) as (X, Y, Z):
82 <body>
83
84 is equivalent to this:
85
86 with A as X:
87 with B as Y:
88 with C as Z:
89 <body>
90
91 """
92 exits = []
93 vars = []
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000094 try:
95 try:
96 for context in contexts:
97 mgr = context.__context__()
98 exit = mgr.__exit__
99 enter = mgr.__enter__
100 vars.append(enter())
101 exits.append(exit)
102 yield vars
103 except:
104 exc = sys.exc_info()
Guido van Rossuma9f06872006-03-01 17:10:01 +0000105 else:
106 exc = (None, None, None)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000107 finally:
108 while exits:
109 exit = exits.pop()
110 try:
111 exit(*exc)
112 except:
113 exc = sys.exc_info()
Guido van Rossuma9f06872006-03-01 17:10:01 +0000114 else:
115 exc = (None, None, None)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000116 if exc != (None, None, None):
117 raise
118
119
120@contextmanager
121def closing(thing):
122 """Context manager to automatically close something at the end of a block.
123
124 Code like this:
125
126 with closing(<module>.open(<arguments>)) as f:
127 <block>
128
129 is equivalent to this:
130
131 f = <module>.open(<arguments>)
132 try:
133 <block>
134 finally:
135 f.close()
136
137 """
138 try:
139 yield thing
140 finally:
141 thing.close()