@@ -116,15 +116,68 @@ async def test_foreign_exception_passed(self):
116
116
raise KeyError
117
117
self .assertFalse (cm .expired ())
118
118
119
+ async def test_timeout_exception_context (self ):
120
+ with self .assertRaises (TimeoutError ) as cm :
121
+ async with asyncio .timeout (0.01 ):
122
+ try :
123
+ 1 / 0
124
+ finally :
125
+ await asyncio .sleep (1 )
126
+ e = cm .exception
127
+ # Expect TimeoutError caused by CancelledError raised during handling
128
+ # of ZeroDivisionError.
129
+ e2 = e .__cause__
130
+ self .assertIsInstance (e2 , asyncio .CancelledError )
131
+ self .assertIs (e .__context__ , e2 )
132
+ self .assertIsNone (e2 .__cause__ )
133
+ self .assertIsInstance (e2 .__context__ , ZeroDivisionError )
134
+
119
135
async def test_foreign_exception_on_timeout (self ):
120
136
async def crash ():
121
137
try :
122
138
await asyncio .sleep (1 )
123
139
finally :
124
140
1 / 0
125
- with self .assertRaises (ZeroDivisionError ):
141
+ with self .assertRaises (ZeroDivisionError ) as cm :
126
142
async with asyncio .timeout (0.01 ):
127
143
await crash ()
144
+ e = cm .exception
145
+ # Expect ZeroDivisionError raised during handling of TimeoutError
146
+ # caused by CancelledError.
147
+ self .assertIsNone (e .__cause__ )
148
+ e2 = e .__context__
149
+ self .assertIsInstance (e2 , TimeoutError )
150
+ e3 = e2 .__cause__
151
+ self .assertIsInstance (e3 , asyncio .CancelledError )
152
+ self .assertIs (e2 .__context__ , e3 )
153
+
154
+ async def test_foreign_exception_on_timeout_2 (self ):
155
+ with self .assertRaises (ZeroDivisionError ) as cm :
156
+ async with asyncio .timeout (0.01 ):
157
+ try :
158
+ try :
159
+ raise ValueError
160
+ finally :
161
+ await asyncio .sleep (1 )
162
+ finally :
163
+ try :
164
+ raise KeyError
165
+ finally :
166
+ 1 / 0
167
+ e = cm .exception
168
+ # Expect ZeroDivisionError raised during handling of KeyError
169
+ # raised during handling of TimeoutError caused by CancelledError.
170
+ self .assertIsNone (e .__cause__ )
171
+ e2 = e .__context__
172
+ self .assertIsInstance (e2 , KeyError )
173
+ self .assertIsNone (e2 .__cause__ )
174
+ e3 = e2 .__context__
175
+ self .assertIsInstance (e3 , TimeoutError )
176
+ e4 = e3 .__cause__
177
+ self .assertIsInstance (e4 , asyncio .CancelledError )
178
+ self .assertIsNone (e4 .__cause__ )
179
+ self .assertIsInstance (e4 .__context__ , ValueError )
180
+ self .assertIs (e3 .__context__ , e4 )
128
181
129
182
async def test_foreign_cancel_doesnt_timeout_if_not_expired (self ):
130
183
with self .assertRaises (asyncio .CancelledError ):
@@ -219,14 +272,30 @@ async def test_repr_disabled(self):
219
272
self .assertEqual (repr (cm ), r"<Timeout [active] when=None>" )
220
273
221
274
async def test_nested_timeout_in_finally (self ):
222
- with self .assertRaises (TimeoutError ):
275
+ with self .assertRaises (TimeoutError ) as cm1 :
223
276
async with asyncio .timeout (0.01 ):
224
277
try :
225
278
await asyncio .sleep (1 )
226
279
finally :
227
- with self .assertRaises (TimeoutError ):
280
+ with self .assertRaises (TimeoutError ) as cm2 :
228
281
async with asyncio .timeout (0.01 ):
229
282
await asyncio .sleep (10 )
283
+ e1 = cm1 .exception
284
+ # Expect TimeoutError caused by CancelledError.
285
+ e12 = e1 .__cause__
286
+ self .assertIsInstance (e12 , asyncio .CancelledError )
287
+ self .assertIsNone (e12 .__cause__ )
288
+ self .assertIsNone (e12 .__context__ )
289
+ self .assertIs (e1 .__context__ , e12 )
290
+ e2 = cm2 .exception
291
+ # Expect TimeoutError caused by CancelledError raised during
292
+ # handling of other CancelledError (which is the same as in
293
+ # the above chain).
294
+ e22 = e2 .__cause__
295
+ self .assertIsInstance (e22 , asyncio .CancelledError )
296
+ self .assertIsNone (e22 .__cause__ )
297
+ self .assertIs (e22 .__context__ , e12 )
298
+ self .assertIs (e2 .__context__ , e22 )
230
299
231
300
async def test_timeout_after_cancellation (self ):
232
301
try :
@@ -235,7 +304,7 @@ async def test_timeout_after_cancellation(self):
235
304
except asyncio .CancelledError :
236
305
pass
237
306
finally :
238
- with self .assertRaises (TimeoutError ):
307
+ with self .assertRaises (TimeoutError ) as cm :
239
308
async with asyncio .timeout (0.0 ):
240
309
await asyncio .sleep (1 ) # some cleanup
241
310
@@ -251,13 +320,6 @@ async def test_cancel_in_timeout_after_cancellation(self):
251
320
asyncio .current_task ().cancel ()
252
321
await asyncio .sleep (2 ) # some cleanup
253
322
254
- async def test_timeout_exception_cause (self ):
255
- with self .assertRaises (asyncio .TimeoutError ) as exc :
256
- async with asyncio .timeout (0 ):
257
- await asyncio .sleep (1 )
258
- cause = exc .exception .__cause__
259
- assert isinstance (cause , asyncio .CancelledError )
260
-
261
323
async def test_timeout_already_entered (self ):
262
324
async with asyncio .timeout (0.01 ) as cm :
263
325
with self .assertRaisesRegex (RuntimeError , "has already been entered" ):
@@ -303,6 +365,47 @@ async def test_timeout_without_task(self):
303
365
with self .assertRaisesRegex (RuntimeError , "has not been entered" ):
304
366
cm .reschedule (0.02 )
305
367
368
+ async def test_timeout_taskgroup (self ):
369
+ async def task ():
370
+ try :
371
+ await asyncio .sleep (2 ) # Will be interrupted after 0.01 second
372
+ finally :
373
+ 1 / 0 # Crash in cleanup
374
+
375
+ with self .assertRaises (ExceptionGroup ) as cm :
376
+ async with asyncio .timeout (0.01 ):
377
+ async with asyncio .TaskGroup () as tg :
378
+ tg .create_task (task ())
379
+ try :
380
+ raise ValueError
381
+ finally :
382
+ await asyncio .sleep (1 )
383
+ eg = cm .exception
384
+ # Expect ExceptionGroup raised during handling of TimeoutError caused
385
+ # by CancelledError raised during handling of ValueError.
386
+ self .assertIsNone (eg .__cause__ )
387
+ e_1 = eg .__context__
388
+ self .assertIsInstance (e_1 , TimeoutError )
389
+ e_2 = e_1 .__cause__
390
+ self .assertIsInstance (e_2 , asyncio .CancelledError )
391
+ self .assertIsNone (e_2 .__cause__ )
392
+ self .assertIsInstance (e_2 .__context__ , ValueError )
393
+ self .assertIs (e_1 .__context__ , e_2 )
394
+
395
+ self .assertEqual (len (eg .exceptions ), 1 , eg )
396
+ e1 = eg .exceptions [0 ]
397
+ # Expect ZeroDivisionError raised during handling of TimeoutError
398
+ # caused by CancelledError (it is a different CancelledError).
399
+ self .assertIsInstance (e1 , ZeroDivisionError )
400
+ self .assertIsNone (e1 .__cause__ )
401
+ e2 = e1 .__context__
402
+ self .assertIsInstance (e2 , TimeoutError )
403
+ e3 = e2 .__cause__
404
+ self .assertIsInstance (e3 , asyncio .CancelledError )
405
+ self .assertIsNone (e3 .__context__ )
406
+ self .assertIsNone (e3 .__cause__ )
407
+ self .assertIs (e2 .__context__ , e3 )
408
+
306
409
307
410
if __name__ == '__main__' :
308
411
unittest .main ()
0 commit comments