Я придумал такую обёртку:
async def run_tasks_and_cancel_on_first_return(*tasks: ty.Set[aio.Future],
return_when=aio.FIRST_COMPLETED,
) -> ty.Set[aio.Future]:
try:
done, pending = await aio.wait(tasks, return_when=return_when)
except aio.CancelledError:
for t in tasks:
if isinstance(t, aio.Task) and not t.done():
t.cancel()
try:
await t
except aio.CancelledError:
pass
raise
for t in pending:
if isinstance(t, aio.Task):
t.cancel()
for t in pending:
if isinstance(t, aio.Task):
try:
await t
except aio.CancelledError:
pass
return done