10
10
from contextlib import * # Tests __all__
11
11
from test import support
12
12
from test .support import os_helper
13
+ from test .support .testcase import ExceptionIsLikeMixin
13
14
import weakref
14
15
15
16
@@ -158,9 +159,45 @@ def whoo():
158
159
yield
159
160
ctx = whoo ()
160
161
ctx .__enter__ ()
161
- self .assertRaises (
162
- RuntimeError , ctx .__exit__ , TypeError , TypeError ("foo" ), None
163
- )
162
+ with self .assertRaises (RuntimeError ):
163
+ ctx .__exit__ (TypeError , TypeError ("foo" ), None )
164
+ if support .check_impl_detail (cpython = True ):
165
+ # The "gen" attribute is an implementation detail.
166
+ self .assertFalse (ctx .gen .gi_suspended )
167
+
168
+ def test_contextmanager_trap_no_yield (self ):
169
+ @contextmanager
170
+ def whoo ():
171
+ if False :
172
+ yield
173
+ ctx = whoo ()
174
+ with self .assertRaises (RuntimeError ):
175
+ ctx .__enter__ ()
176
+
177
+ def test_contextmanager_trap_second_yield (self ):
178
+ @contextmanager
179
+ def whoo ():
180
+ yield
181
+ yield
182
+ ctx = whoo ()
183
+ ctx .__enter__ ()
184
+ with self .assertRaises (RuntimeError ):
185
+ ctx .__exit__ (None , None , None )
186
+ if support .check_impl_detail (cpython = True ):
187
+ # The "gen" attribute is an implementation detail.
188
+ self .assertFalse (ctx .gen .gi_suspended )
189
+
190
+ def test_contextmanager_non_normalised (self ):
191
+ @contextmanager
192
+ def whoo ():
193
+ try :
194
+ yield
195
+ except RuntimeError :
196
+ raise SyntaxError
197
+ ctx = whoo ()
198
+ ctx .__enter__ ()
199
+ with self .assertRaises (SyntaxError ):
200
+ ctx .__exit__ (RuntimeError , None , None )
164
201
165
202
def test_contextmanager_except (self ):
166
203
state = []
@@ -241,6 +278,23 @@ def test_issue29692():
241
278
self .assertEqual (ex .args [0 ], 'issue29692:Unchained' )
242
279
self .assertIsNone (ex .__cause__ )
243
280
281
+ def test_contextmanager_wrap_runtimeerror (self ):
282
+ @contextmanager
283
+ def woohoo ():
284
+ try :
285
+ yield
286
+ except Exception as exc :
287
+ raise RuntimeError (f'caught { exc } ' ) from exc
288
+ with self .assertRaises (RuntimeError ):
289
+ with woohoo ():
290
+ 1 / 0
291
+ # If the context manager wrapped StopIteration in a RuntimeError,
292
+ # we also unwrap it, because we can't tell whether the wrapping was
293
+ # done by the generator machinery or by the generator itself.
294
+ with self .assertRaises (StopIteration ):
295
+ with woohoo ():
296
+ raise StopIteration
297
+
244
298
def _create_contextmanager_attribs (self ):
245
299
def attribs (** kw ):
246
300
def decorate (func ):
@@ -252,6 +306,7 @@ def decorate(func):
252
306
@attribs (foo = 'bar' )
253
307
def baz (spam ):
254
308
"""Whee!"""
309
+ yield
255
310
return baz
256
311
257
312
def test_contextmanager_attribs (self ):
@@ -308,8 +363,11 @@ def woohoo(a, *, b):
308
363
309
364
def test_recursive (self ):
310
365
depth = 0
366
+ ncols = 0
311
367
@contextmanager
312
368
def woohoo ():
369
+ nonlocal ncols
370
+ ncols += 1
313
371
nonlocal depth
314
372
before = depth
315
373
depth += 1
@@ -323,6 +381,7 @@ def recursive():
323
381
recursive ()
324
382
325
383
recursive ()
384
+ self .assertEqual (ncols , 10 )
326
385
self .assertEqual (depth , 0 )
327
386
328
387
@@ -374,12 +433,10 @@ class FileContextTestCase(unittest.TestCase):
374
433
def testWithOpen (self ):
375
434
tfn = tempfile .mktemp ()
376
435
try :
377
- f = None
378
436
with open (tfn , "w" , encoding = "utf-8" ) as f :
379
437
self .assertFalse (f .closed )
380
438
f .write ("Booh\n " )
381
439
self .assertTrue (f .closed )
382
- f = None
383
440
with self .assertRaises (ZeroDivisionError ):
384
441
with open (tfn , "r" , encoding = "utf-8" ) as f :
385
442
self .assertFalse (f .closed )
@@ -1160,7 +1217,7 @@ class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
1160
1217
orig_stream = "stderr"
1161
1218
1162
1219
1163
- class TestSuppress (unittest .TestCase ):
1220
+ class TestSuppress (ExceptionIsLikeMixin , unittest .TestCase ):
1164
1221
1165
1222
@support .requires_docstrings
1166
1223
def test_instance_docs (self ):
@@ -1214,6 +1271,51 @@ def test_cm_is_reentrant(self):
1214
1271
1 / 0
1215
1272
self .assertTrue (outer_continued )
1216
1273
1274
+ # TODO: RUSTPYTHON
1275
+ @unittest .expectedFailure
1276
+ def test_exception_groups (self ):
1277
+ eg_ve = lambda : ExceptionGroup (
1278
+ "EG with ValueErrors only" ,
1279
+ [ValueError ("ve1" ), ValueError ("ve2" ), ValueError ("ve3" )],
1280
+ )
1281
+ eg_all = lambda : ExceptionGroup (
1282
+ "EG with many types of exceptions" ,
1283
+ [ValueError ("ve1" ), KeyError ("ke1" ), ValueError ("ve2" ), KeyError ("ke2" )],
1284
+ )
1285
+ with suppress (ValueError ):
1286
+ raise eg_ve ()
1287
+ with suppress (ValueError , KeyError ):
1288
+ raise eg_all ()
1289
+ with self .assertRaises (ExceptionGroup ) as eg1 :
1290
+ with suppress (ValueError ):
1291
+ raise eg_all ()
1292
+ self .assertExceptionIsLike (
1293
+ eg1 .exception ,
1294
+ ExceptionGroup (
1295
+ "EG with many types of exceptions" ,
1296
+ [KeyError ("ke1" ), KeyError ("ke2" )],
1297
+ ),
1298
+ )
1299
+
1300
+ # Check handling of BaseExceptionGroup, using GeneratorExit so that
1301
+ # we don't accidentally discard a ctrl-c with KeyboardInterrupt.
1302
+ with suppress (GeneratorExit ):
1303
+ raise BaseExceptionGroup ("message" , [GeneratorExit ()])
1304
+ # If we raise a BaseException group, we can still suppress parts
1305
+ with self .assertRaises (BaseExceptionGroup ) as eg1 :
1306
+ with suppress (KeyError ):
1307
+ raise BaseExceptionGroup ("message" , [GeneratorExit ("g" ), KeyError ("k" )])
1308
+ self .assertExceptionIsLike (
1309
+ eg1 .exception , BaseExceptionGroup ("message" , [GeneratorExit ("g" )]),
1310
+ )
1311
+ # If we suppress all the leaf BaseExceptions, we get a non-base ExceptionGroup
1312
+ with self .assertRaises (ExceptionGroup ) as eg1 :
1313
+ with suppress (GeneratorExit ):
1314
+ raise BaseExceptionGroup ("message" , [GeneratorExit ("g" ), KeyError ("k" )])
1315
+ self .assertExceptionIsLike (
1316
+ eg1 .exception , ExceptionGroup ("message" , [KeyError ("k" )]),
1317
+ )
1318
+
1217
1319
1218
1320
class TestChdir (unittest .TestCase ):
1219
1321
def make_relative_path (self , * parts ):
0 commit comments