@@ -113,18 +113,20 @@ def __init__(self, func, args, kwds):
113113 # for the class instead.
114114 # See http://bugs.python.org/issue19404 for more details.
115115
116-
117- class _GeneratorContextManager (_GeneratorContextManagerBase ,
118- AbstractContextManager ,
119- ContextDecorator ):
120- """Helper for @contextmanager decorator."""
121-
122116 def _recreate_cm (self ):
123- # _GCM instances are one-shot context managers, so the
117+ # _GCMB instances are one-shot context managers, so the
124118 # CM must be recreated each time a decorated function is
125119 # called
126120 return self .__class__ (self .func , self .args , self .kwds )
127121
122+
123+ class _GeneratorContextManager (
124+ _GeneratorContextManagerBase ,
125+ AbstractContextManager ,
126+ ContextDecorator ,
127+ ):
128+ """Helper for @contextmanager decorator."""
129+
128130 def __enter__ (self ):
129131 # do not keep args and kwds alive unnecessarily
130132 # they are only needed for recreation, which is not possible anymore
@@ -134,8 +136,8 @@ def __enter__(self):
134136 except StopIteration :
135137 raise RuntimeError ("generator didn't yield" ) from None
136138
137- def __exit__ (self , type , value , traceback ):
138- if type is None :
139+ def __exit__ (self , typ , value , traceback ):
140+ if typ is None :
139141 try :
140142 next (self .gen )
141143 except StopIteration :
@@ -146,9 +148,9 @@ def __exit__(self, type, value, traceback):
146148 if value is None :
147149 # Need to force instantiation so we can reliably
148150 # tell if we get the same exception back
149- value = type ()
151+ value = typ ()
150152 try :
151- self .gen .throw (type , value , traceback )
153+ self .gen .throw (typ , value , traceback )
152154 except StopIteration as exc :
153155 # Suppress StopIteration *unless* it's the same exception that
154156 # was passed to throw(). This prevents a StopIteration
@@ -158,81 +160,93 @@ def __exit__(self, type, value, traceback):
158160 # Don't re-raise the passed in exception. (issue27122)
159161 if exc is value :
160162 return False
161- # Likewise, avoid suppressing if a StopIteration exception
163+ # Avoid suppressing if a StopIteration exception
162164 # was passed to throw() and later wrapped into a RuntimeError
163- # (see PEP 479).
164- if type is StopIteration and exc .__cause__ is value :
165+ # (see PEP 479 for sync generators; async generators also
166+ # have this behavior). But do this only if the exception wrapped
167+ # by the RuntimeError is actually Stop(Async)Iteration (see
168+ # issue29692).
169+ if (
170+ isinstance (value , StopIteration )
171+ and exc .__cause__ is value
172+ ):
165173 return False
166174 raise
167- except :
175+ except BaseException as exc :
168176 # only re-raise if it's *not* the exception that was
169177 # passed to throw(), because __exit__() must not raise
170178 # an exception unless __exit__() itself failed. But throw()
171179 # has to raise the exception to signal propagation, so this
172180 # fixes the impedance mismatch between the throw() protocol
173181 # and the __exit__() protocol.
174- #
175- # This cannot use 'except BaseException as exc' (as in the
176- # async implementation) to maintain compatibility with
177- # Python 2, where old-style class exceptions are not caught
178- # by 'except BaseException'.
179- if sys .exc_info ()[1 ] is value :
180- return False
181- raise
182+ if exc is not value :
183+ raise
184+ return False
182185 raise RuntimeError ("generator didn't stop after throw()" )
183186
184-
185- class _AsyncGeneratorContextManager (_GeneratorContextManagerBase ,
186- AbstractAsyncContextManager ,
187- AsyncContextDecorator ):
188- """Helper for @asynccontextmanager."""
189-
190- def _recreate_cm (self ):
191- # _AGCM instances are one-shot context managers, so the
192- # ACM must be recreated each time a decorated function is
193- # called
194- return self .__class__ (self .func , self .args , self .kwds )
187+ class _AsyncGeneratorContextManager (
188+ _GeneratorContextManagerBase ,
189+ AbstractAsyncContextManager ,
190+ AsyncContextDecorator ,
191+ ):
192+ """Helper for @asynccontextmanager decorator."""
195193
196194 async def __aenter__ (self ):
195+ # do not keep args and kwds alive unnecessarily
196+ # they are only needed for recreation, which is not possible anymore
197+ del self .args , self .kwds , self .func
197198 try :
198- return await self .gen . __anext__ ( )
199+ return await anext ( self .gen )
199200 except StopAsyncIteration :
200201 raise RuntimeError ("generator didn't yield" ) from None
201202
202203 async def __aexit__ (self , typ , value , traceback ):
203204 if typ is None :
204205 try :
205- await self .gen . __anext__ ( )
206+ await anext ( self .gen )
206207 except StopAsyncIteration :
207- return
208+ return False
208209 else :
209210 raise RuntimeError ("generator didn't stop" )
210211 else :
211212 if value is None :
213+ # Need to force instantiation so we can reliably
214+ # tell if we get the same exception back
212215 value = typ ()
213- # See _GeneratorContextManager.__exit__ for comments on subtleties
214- # in this implementation
215216 try :
216217 await self .gen .athrow (typ , value , traceback )
217- raise RuntimeError ("generator didn't stop after athrow()" )
218218 except StopAsyncIteration as exc :
219+ # Suppress StopIteration *unless* it's the same exception that
220+ # was passed to throw(). This prevents a StopIteration
221+ # raised inside the "with" statement from being suppressed.
219222 return exc is not value
220223 except RuntimeError as exc :
224+ # Don't re-raise the passed in exception. (issue27122)
221225 if exc is value :
222226 return False
223- # Avoid suppressing if a StopIteration exception
224- # was passed to throw () and later wrapped into a RuntimeError
227+ # Avoid suppressing if a Stop(Async)Iteration exception
228+ # was passed to athrow () and later wrapped into a RuntimeError
225229 # (see PEP 479 for sync generators; async generators also
226230 # have this behavior). But do this only if the exception wrapped
227231 # by the RuntimeError is actually Stop(Async)Iteration (see
228232 # issue29692).
229- if isinstance (value , (StopIteration , StopAsyncIteration )):
230- if exc .__cause__ is value :
231- return False
233+ if (
234+ isinstance (value , (StopIteration , StopAsyncIteration ))
235+ and exc .__cause__ is value
236+ ):
237+ return False
232238 raise
233239 except BaseException as exc :
240+ # only re-raise if it's *not* the exception that was
241+ # passed to throw(), because __exit__() must not raise
242+ # an exception unless __exit__() itself failed. But throw()
243+ # has to raise the exception to signal propagation, so this
244+ # fixes the impedance mismatch between the throw() protocol
245+ # and the __exit__() protocol.
234246 if exc is not value :
235247 raise
248+ return False
249+ raise RuntimeError ("generator didn't stop after athrow()" )
236250
237251
238252def contextmanager (func ):
0 commit comments