@@ -34,6 +34,8 @@ async def valid(value):
3434
3535@sync
3636async def test_contextmanager_no_yield ():
37+ """Test that it is an error for a context to not yield"""
38+
3739 @a .contextmanager
3840 async def no_yield ():
3941 if False :
@@ -46,6 +48,8 @@ async def no_yield():
4648
4749@sync
4850async def test_contextmanager_no_stop ():
51+ """Test that it is an error for a context to yield again after stopping"""
52+
4953 @a .contextmanager
5054 async def no_stop ():
5155 yield
@@ -69,6 +73,8 @@ async def supress_no_stop():
6973
7074@sync
7175async def test_contextmanager_raise_asyncstop ():
76+ """Test that StopAsyncIteration may propagate out of a context block"""
77+
7278 @a .contextmanager
7379 async def no_raise ():
7480 yield
@@ -113,6 +119,8 @@ async def replace():
113119
114120@sync
115121async def test_contextmanager_raise_same ():
122+ """Test that outer exceptions do not shadow inner/newer ones"""
123+
116124 @a .contextmanager
117125 async def reraise ():
118126 try :
@@ -136,6 +144,65 @@ async def recreate():
136144 raise KeyError ("outside" )
137145
138146
147+ @sync
148+ async def test_contextmanager_raise_generatorexit ():
149+ """Test that shutdown via GeneratorExit is propagated"""
150+
151+ @a .contextmanager
152+ async def no_op ():
153+ yield
154+
155+ with pytest .raises (GeneratorExit ):
156+ async with no_op ():
157+ raise GeneratorExit ("used to tear down coroutines" )
158+
159+ # during shutdown, generators may be killed in arbitrary order
160+ # make sure we do not suppress GeneratorExit
161+ with pytest .raises (GeneratorExit , match = "inner" ):
162+ context = no_op ()
163+ async with context :
164+ # simulate cleanup closing the child early
165+ await context .gen .aclose ()
166+ raise GeneratorExit ("inner" )
167+
168+
169+ @sync
170+ async def test_contextmanager_no_suppress_generatorexit ():
171+ """Test that GeneratorExit is not suppressed"""
172+
173+ @a .contextmanager
174+ async def no_op ():
175+ yield
176+
177+ exc = GeneratorExit ("GE should not be replaced normally" )
178+ with pytest .raises (type (exc )) as exc_info :
179+ async with no_op ():
180+ raise exc
181+ assert exc_info .value is exc
182+
183+ @a .contextmanager
184+ async def exit_ge ():
185+ try :
186+ yield
187+ except GeneratorExit :
188+ pass
189+
190+ with pytest .raises (GeneratorExit ):
191+ async with exit_ge ():
192+ raise GeneratorExit ("Resume teardown if child exited" )
193+
194+ @a .contextmanager
195+ async def ignore_ge ():
196+ try :
197+ yield
198+ except GeneratorExit :
199+ yield
200+
201+ with pytest .raises (RuntimeError ):
202+ async with ignore_ge ():
203+ raise GeneratorExit ("Warn if child does not exit" )
204+
205+
139206@sync
140207async def test_nullcontext ():
141208 async with a .nullcontext (1337 ) as value :
0 commit comments