There are two tasks in a task group. One of them raises, the other one should be cancelled. This works fine in Python 3.12+, but freezes in Python 3.11. Older versions did not support task groups.
Is this a known problem? They will probably not fix a bug in 3.11 at this stage of its life-cycle. I'm looking for information how to avoid, solve or mitigate the issue. So far I found out that small changes in the code make a difference. And it looks like the wait_for plays some role there.
import asyncio
ev = asyncio.Event()
async def t1():
try:
while True:
try:
print("Waiting")
await asyncio.wait_for(ev.wait(), 99)
print("Done waiting")
ev.clear()
except TimeoutError:
print("Timeout")
raise
except asyncio.CancelledError:
print("Cancelled - as expected")
raise
async def t2():
ev.set()
raise RuntimeError()
async def main():
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(t1())
tg.create_task(t2())
except* RuntimeError:
print("RuntimeError - as expected")
if __name__ == "__main__":
asyncio.run(main())
Normal output:
Waiting
Done waiting
Waiting
Cancelled - as expected
RuntimeError - as expected
Wrong output in Python 3.11:
Waiting
Done waiting
Waiting
And then it hangs until Ctrl-C is pressed twice.
Thanks to @mkrieger1's comment, it is likely related to bpo-42130 - indeed, "wait_for suppresses a cancellation occurring in the wait_for inner subtask" seems to be the issue.
The official Python resolution was to rewrite asyncio.wait_for using asyncio.timeout. I suggest that, instead of using asyncio.wait_for, you use asyncio.timeout directly.
That is, instead of:
await asyncio.wait_for(ev.wait(), 99)
To use:
async with asyncio.timeout(99):
await ev.wait()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With