Added new optional credentials argument to SMTPHandler.__init__, and smtp.login(...
[python.git] / Lib / contextlib.py
bloba807c42ce47561c6a05f42123ad7e5582f02c4c2
1 """Utilities for with-statement contexts. See PEP 343."""
3 import sys
5 __all__ = ["contextmanager", "nested", "closing"]
7 class GeneratorContextManager(object):
8 """Helper for @contextmanager decorator."""
10 def __init__(self, gen):
11 self.gen = gen
13 def __enter__(self):
14 try:
15 return self.gen.next()
16 except StopIteration:
17 raise RuntimeError("generator didn't yield")
19 def __exit__(self, type, value, traceback):
20 if type is None:
21 try:
22 self.gen.next()
23 except StopIteration:
24 return
25 else:
26 raise RuntimeError("generator didn't stop")
27 else:
28 try:
29 self.gen.throw(type, value, traceback)
30 raise RuntimeError("generator didn't stop after throw()")
31 except StopIteration, exc:
32 # Suppress the exception *unless* it's the same exception that
33 # was passed to throw(). This prevents a StopIteration
34 # raised inside the "with" statement from being suppressed
35 return exc is not value
36 except:
37 # only re-raise if it's *not* the exception that was
38 # passed to throw(), because __exit__() must not raise
39 # an exception unless __exit__() itself failed. But throw()
40 # has to raise the exception to signal propagation, so this
41 # fixes the impedance mismatch between the throw() protocol
42 # and the __exit__() protocol.
44 if sys.exc_info()[1] is not value:
45 raise
48 def contextmanager(func):
49 """@contextmanager decorator.
51 Typical usage:
53 @contextmanager
54 def some_generator(<arguments>):
55 <setup>
56 try:
57 yield <value>
58 finally:
59 <cleanup>
61 This makes this:
63 with some_generator(<arguments>) as <variable>:
64 <body>
66 equivalent to this:
68 <setup>
69 try:
70 <variable> = <value>
71 <body>
72 finally:
73 <cleanup>
75 """
76 def helper(*args, **kwds):
77 return GeneratorContextManager(func(*args, **kwds))
78 try:
79 helper.__name__ = func.__name__
80 helper.__doc__ = func.__doc__
81 helper.__dict__ = func.__dict__
82 except:
83 pass
84 return helper
87 @contextmanager
88 def nested(*managers):
89 """Support multiple context managers in a single with-statement.
91 Code like this:
93 with nested(A, B, C) as (X, Y, Z):
94 <body>
96 is equivalent to this:
98 with A as X:
99 with B as Y:
100 with C as Z:
101 <body>
104 exits = []
105 vars = []
106 exc = (None, None, None)
107 try:
108 try:
109 for mgr in managers:
110 exit = mgr.__exit__
111 enter = mgr.__enter__
112 vars.append(enter())
113 exits.append(exit)
114 yield vars
115 except:
116 exc = sys.exc_info()
117 finally:
118 while exits:
119 exit = exits.pop()
120 try:
121 if exit(*exc):
122 exc = (None, None, None)
123 except:
124 exc = sys.exc_info()
125 if exc != (None, None, None):
126 # Don't rely on sys.exc_info() still containing
127 # the right information. Another exception may
128 # have been raised and caught by an exit method
129 raise exc[0], exc[1], exc[2]
132 class closing(object):
133 """Context to automatically close something at the end of a block.
135 Code like this:
137 with closing(<module>.open(<arguments>)) as f:
138 <block>
140 is equivalent to this:
142 f = <module>.open(<arguments>)
143 try:
144 <block>
145 finally:
146 f.close()
149 def __init__(self, thing):
150 self.thing = thing
151 def __enter__(self):
152 return self.thing
153 def __exit__(self, *exc_info):
154 self.thing.close()