Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1a5e21e

Browse files
committed
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.
1 parent 87a8b4f commit 1a5e21e

12 files changed

Lines changed: 609 additions & 96 deletions

File tree

Lib/contextlib.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""Utilities for with-statement contexts. See PEP 343."""
2+
3+
import sys
4+
5+
__all__ = ["contextmanager", "nested", "closing"]
6+
7+
class 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+
37+
def 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
76+
def 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 = []
94+
exc = (None, None, None)
95+
try:
96+
try:
97+
for context in contexts:
98+
mgr = context.__context__()
99+
exit = mgr.__exit__
100+
enter = mgr.__enter__
101+
vars.append(enter())
102+
exits.append(exit)
103+
yield vars
104+
except:
105+
exc = sys.exc_info()
106+
finally:
107+
while exits:
108+
exit = exits.pop()
109+
try:
110+
exit(*exc)
111+
except:
112+
exc = sys.exc_info()
113+
if exc != (None, None, None):
114+
raise
115+
116+
117+
@contextmanager
118+
def closing(thing):
119+
"""Context manager to automatically close something at the end of a block.
120+
121+
Code like this:
122+
123+
with closing(<module>.open(<arguments>)) as f:
124+
<block>
125+
126+
is equivalent to this:
127+
128+
f = <module>.open(<arguments>)
129+
try:
130+
<block>
131+
finally:
132+
f.close()
133+
134+
"""
135+
try:
136+
yield thing
137+
finally:
138+
thing.close()

Lib/decimal.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2173,6 +2173,32 @@ def __deepcopy__(self, memo):
21732173

21742174
del name, val, globalname, rounding_functions
21752175

2176+
class ContextManager(object):
2177+
"""Helper class to simplify Context management.
2178+
2179+
Sample usage:
2180+
2181+
with decimal.ExtendedContext:
2182+
s = ...
2183+
return +s # Convert result to normal precision
2184+
2185+
with decimal.getcontext() as ctx:
2186+
ctx.prec += 2
2187+
s = ...
2188+
return +s
2189+
2190+
"""
2191+
def __init__(self, new_context):
2192+
self.new_context = new_context
2193+
def __enter__(self):
2194+
self.saved_context = getcontext()
2195+
setcontext(self.new_context)
2196+
return self.new_context
2197+
def __exit__(self, t, v, tb):
2198+
setcontext(self.saved_context)
2199+
if t is not None:
2200+
raise t, v, tb
2201+
21762202
class Context(object):
21772203
"""Contains the context for a Decimal instance.
21782204
@@ -2224,6 +2250,9 @@ def __repr__(self):
22242250
s.append('traps=[' + ', '.join([t.__name__ for t, v in self.traps.items() if v]) + ']')
22252251
return ', '.join(s) + ')'
22262252

2253+
def __context__(self):
2254+
return ContextManager(self.copy())
2255+
22272256
def clear_flags(self):
22282257
"""Reset all flags to zero"""
22292258
for flag in self.flags:

Lib/test/contextmanager.py

Lines changed: 0 additions & 33 deletions
This file was deleted.

Lib/test/nested.py

Lines changed: 0 additions & 40 deletions
This file was deleted.

0 commit comments

Comments
 (0)