Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TaskGroup in Python 3.11 freezes if one task raises an exception - is it a known bug?

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.

like image 729
VPfB Avatar asked Nov 15 '25 15:11

VPfB


1 Answers

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()
like image 90
Jean Hominal Avatar answered Nov 17 '25 10:11

Jean Hominal



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!