1
1
from __future__ import annotations
2
2
3
+ import re
3
4
from typing import TypeVar
4
5
5
6
import pytest
@@ -233,6 +234,53 @@ async def dummy_task(
233
234
234
235
235
236
async def test_parking_lot_breaker_basic () -> None :
237
+ """Test basic functionality for breaking lots."""
238
+ lot = ParkingLot ()
239
+ task = current_task ()
240
+
241
+ # defaults to current task
242
+ lot .break_lot ()
243
+ assert lot .broken_by == [task ]
244
+
245
+ # breaking the lot again with the same task appends another copy in `broken_by`
246
+ lot .break_lot ()
247
+ assert lot .broken_by == [task , task ]
248
+
249
+ # trying to park in broken lot errors
250
+ broken_by_str = re .escape (str ([task , task ]))
251
+ with pytest .raises (
252
+ _core .BrokenResourceError ,
253
+ match = f"^Attempted to park in parking lot broken by { broken_by_str } $" ,
254
+ ):
255
+ await lot .park ()
256
+
257
+
258
+ async def test_parking_lot_break_parking_tasks () -> None :
259
+ """Checks that tasks currently waiting to park raise an error when the breaker exits."""
260
+
261
+ async def bad_parker (lot : ParkingLot , scope : _core .CancelScope ) -> None :
262
+ add_parking_lot_breaker (current_task (), lot )
263
+ with scope :
264
+ await trio .sleep_forever ()
265
+
266
+ lot = ParkingLot ()
267
+ cs = _core .CancelScope ()
268
+
269
+ # check that parked task errors
270
+ with RaisesGroup (
271
+ Matcher (_core .BrokenResourceError , match = "^Parking lot broken by" ),
272
+ ):
273
+ async with _core .open_nursery () as nursery :
274
+ nursery .start_soon (bad_parker , lot , cs )
275
+ await wait_all_tasks_blocked ()
276
+
277
+ nursery .start_soon (lot .park )
278
+ await wait_all_tasks_blocked ()
279
+
280
+ cs .cancel ()
281
+
282
+
283
+ async def test_parking_lot_breaker_registration () -> None :
236
284
lot = ParkingLot ()
237
285
task = current_task ()
238
286
@@ -254,58 +302,60 @@ async def test_parking_lot_breaker_basic() -> None:
254
302
):
255
303
remove_parking_lot_breaker (task , lot )
256
304
257
- # defaults to current task
258
- lot .break_lot ()
259
- assert lot .broken_by == task
260
-
261
- # breaking the lot again with the same task is a no-op
305
+ # registering a task as breaker on an already broken lot is fine
262
306
lot .break_lot ()
263
-
264
- # registering a task as a breaker on an already broken lot is a no-op.
265
307
child_task = None
266
308
async with trio .open_nursery () as nursery :
267
309
child_task = await nursery .start (dummy_task )
268
310
add_parking_lot_breaker (child_task , lot )
269
311
nursery .cancel_scope .cancel ()
312
+ assert lot .broken_by == [task , child_task ]
270
313
271
314
# manually breaking a lot with an already exited task is fine
272
315
lot = ParkingLot ()
273
316
lot .break_lot (child_task )
317
+ assert lot .broken_by == [child_task ]
274
318
275
319
276
- async def test_parking_lot_breaker_warnings () -> None :
320
+ async def test_parking_lot_breaker_rebreak () -> None :
277
321
lot = ParkingLot ()
278
322
task = current_task ()
279
323
lot .break_lot ()
280
324
281
- warn_str = "attempted to break parking .* already broken by .*"
282
- # breaking an already broken lot with a different task gives a warning
325
+ # breaking an already broken lot with a different task is allowed
283
326
# The nursery is only to create a task we can pass to lot.break_lot
284
327
async with trio .open_nursery () as nursery :
285
328
child_task = await nursery .start (dummy_task )
286
- with pytest .warns (
287
- RuntimeWarning ,
288
- match = warn_str ,
289
- ):
290
- lot .break_lot (child_task )
329
+ lot .break_lot (child_task )
291
330
nursery .cancel_scope .cancel ()
292
331
293
- # and doesn't change broken_by
294
- assert lot .broken_by == task
332
+ # and appends the task
333
+ assert lot .broken_by == [task , child_task ]
334
+
295
335
336
+ async def test_parking_lot_multiple_breakers_exit () -> None :
296
337
# register multiple tasks as lot breakers, then have them all exit
297
338
# No warning is given on task exit, even if the lot is already broken.
298
339
lot = ParkingLot ()
299
- child_task = None
300
340
async with trio .open_nursery () as nursery :
301
- child_task = await nursery .start (dummy_task )
341
+ child_task1 = await nursery .start (dummy_task )
302
342
child_task2 = await nursery .start (dummy_task )
303
343
child_task3 = await nursery .start (dummy_task )
304
- add_parking_lot_breaker (child_task , lot )
344
+ add_parking_lot_breaker (child_task1 , lot )
305
345
add_parking_lot_breaker (child_task2 , lot )
306
346
add_parking_lot_breaker (child_task3 , lot )
307
347
nursery .cancel_scope .cancel ()
308
348
349
+ # I think the order is guaranteed currently, but doesn't hurt to be safe.
350
+ assert set (lot .broken_by ) == {child_task1 , child_task2 , child_task3 }
351
+
352
+
353
+ async def test_parking_lot_breaker_register_exited_task () -> None :
354
+ lot = ParkingLot ()
355
+ child_task = None
356
+ async with trio .open_nursery () as nursery :
357
+ child_task = await nursery .start (dummy_task )
358
+ nursery .cancel_scope .cancel ()
309
359
# trying to register an exited task as lot breaker errors
310
360
with pytest .raises (
311
361
trio .BrokenResourceError ,
@@ -314,34 +364,7 @@ async def test_parking_lot_breaker_warnings() -> None:
314
364
add_parking_lot_breaker (child_task , lot )
315
365
316
366
317
- async def test_parking_lot_breaker_bad_parker () -> None :
318
- async def bad_parker (lot : ParkingLot , scope : _core .CancelScope ) -> None :
319
- add_parking_lot_breaker (current_task (), lot )
320
- with scope :
321
- await trio .sleep_forever ()
322
-
323
- lot = ParkingLot ()
324
- cs = _core .CancelScope ()
325
-
326
- # check that parked task errors
327
- with RaisesGroup (
328
- Matcher (_core .BrokenResourceError , match = "^Parking lot broken by" ),
329
- ):
330
- async with _core .open_nursery () as nursery :
331
- nursery .start_soon (bad_parker , lot , cs )
332
- await wait_all_tasks_blocked ()
333
-
334
- nursery .start_soon (lot .park )
335
- await wait_all_tasks_blocked ()
336
-
337
- cs .cancel ()
338
-
339
- # check that trying to park in broken lot errors
340
- with pytest .raises (_core .BrokenResourceError ):
341
- await lot .park ()
342
-
343
-
344
- async def test_parking_lot_weird () -> None :
367
+ async def test_parking_lot_break_itself () -> None :
345
368
"""Break a parking lot, where the breakee is parked.
346
369
Doing this is weird, but should probably be supported.
347
370
"""
@@ -359,5 +382,5 @@ async def return_me_and_park(
359
382
Matcher (_core .BrokenResourceError , match = "^Parking lot broken by" ),
360
383
):
361
384
async with _core .open_nursery () as nursery :
362
- task = await nursery .start (return_me_and_park , lot )
363
- lot .break_lot (task )
385
+ child_task = await nursery .start (return_me_and_park , lot )
386
+ lot .break_lot (child_task )
0 commit comments