@ -16,6 +16,7 @@
from pathlib import Path
from pathlib import Path
from collections import deque
from collections import deque
from contextlib import suppress
from copy import deepcopy
from copy import deepcopy
import argparse
import argparse
import asyncio
import asyncio
@ -1161,11 +1162,6 @@ def check_testdata(objs: T.List[TestSerialisation]) -> T.List[TestSerialisation]
# Custom waiting primitives for asyncio
# Custom waiting primitives for asyncio
async def try_wait_one ( * awaitables : T . Any , timeout : T . Optional [ T . Union [ int , float ] ] ) - > None :
""" Wait for completion of one of the given futures, ignoring timeouts. """
await asyncio . wait ( awaitables ,
timeout = timeout , return_when = asyncio . FIRST_COMPLETED )
async def queue_iter ( q : ' asyncio.Queue[T.Optional[str]] ' ) - > T . AsyncIterator [ str ] :
async def queue_iter ( q : ' asyncio.Queue[T.Optional[str]] ' ) - > T . AsyncIterator [ str ] :
while True :
while True :
item = await q . get ( )
item = await q . get ( )
@ -1273,13 +1269,15 @@ class TestSubprocess:
# Make sure the termination signal actually kills the process
# Make sure the termination signal actually kills the process
# group, otherwise retry with a SIGKILL.
# group, otherwise retry with a SIGKILL.
await try_wait_one ( p . wait ( ) , timeout = 0.5 )
with suppress ( TimeoutError ) :
await asyncio . wait_for ( p . wait ( ) , timeout = 0.5 )
if p . returncode is not None :
if p . returncode is not None :
return None
return None
os . killpg ( p . pid , signal . SIGKILL )
os . killpg ( p . pid , signal . SIGKILL )
await try_wait_one ( p . wait ( ) , timeout = 1 )
with suppress ( TimeoutError ) :
await asyncio . wait_for ( p . wait ( ) , timeout = 1 )
if p . returncode is not None :
if p . returncode is not None :
return None
return None
@ -1287,7 +1285,8 @@ class TestSubprocess:
# Try to kill it one last time with a direct call.
# Try to kill it one last time with a direct call.
# If the process has spawned children, they will remain around.
# If the process has spawned children, they will remain around.
p . kill ( )
p . kill ( )
await try_wait_one ( p . wait ( ) , timeout = 1 )
with suppress ( TimeoutError ) :
await asyncio . wait_for ( p . wait ( ) , timeout = 1 )
if p . returncode is not None :
if p . returncode is not None :
return None
return None
return ' Test process could not be killed. '
return ' Test process could not be killed. '