1

I'm not asking how to cancel such task. It is not possible and it it is explained here: https://stackoverflow.com/a/33578893/5378816

My concern is this change in wait_for:

Changed in version 3.7: When aw is cancelled due to a timeout, wait_for waits for aw to be cancelled. Previously, it raised asyncio.TimeoutError immediately.

Don't get me wrong, I like it, it is an improvement.

However, this program will now hang in wait_for (Python 3.7):

import asyncio

async def uncancellable():
    while True:
        try:
            await asyncio.sleep(99)
        except asyncio.CancelledError:
            print("sorry!")

TIMEOUT = 1.0

async def test():
    task = asyncio.get_event_loop().create_task(uncancellable())
    try:
        await asyncio.wait_for(task, TIMEOUT)
    except asyncio.TimeoutError:
        print("timeout")

asyncio.get_event_loop().run_until_complete(test())

An uncancellable task is a programming error. But if I need to be defensive, how do I prevent the wait_for from hanging indefinitely?

I tried this. First timeout: before cancelling, second timeout: before giving up.

await asyncio.wait_for(asyncio.wait_for(task, TIMEOUT1), TIMEOUT1+TIMEOUT2)

There is one tiny issue I don't much care about. When it raises asyncio.TimeoutError I cannot tell if it happened at first or at the second timeout. Basically I think it works, but is it really correct?

VPfB
  • 14,927
  • 6
  • 41
  • 75

1 Answers1

1

But if I need to be defensive, how do I prevent the wait_for from hanging indefinitely?

I don't think to consider each task as potentially uncancellable is a good idea.

Usually you just assume this situation won't happen and it's ok because, yes, uncancellable task is a programming error and it's not a kind of error that you expect to see often. Same way you usually don't expect some inner code will suppress KeyboardInterrupt or any other BaseException.

There's nothing wrong in expecting third-party code to follow some contracts (like in example above or, let's say not randomly to call sys.exit()). Otherwise you'll have to write much more code and probably still not cover all possible cases.

Mikhail Gerasimov
  • 36,989
  • 16
  • 116
  • 159