Using asyncio a coroutine can be executed with a timeout so it gets cancelled after the timeout:
@asyncio.coroutine
def coro():
yield from asyncio.sleep(10)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait_for(coro(), 5))
The above example works as expected (it times out after 5 seconds).
However, when the coroutine doesn't use asyncio.sleep()
(or other asyncio coroutines) it doesn't seem to time out. Example:
@asyncio.coroutine
def coro():
import time
time.sleep(10)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait_for(coro(), 1))
This takes more than 10 seconds to run because the time.sleep(10)
isn't cancelled. Is it possible to enforce the cancellation of the coroutine in such a case?
If asyncio should be used to solve this, how could I do that?
No, you can't interrupt a coroutine unless it yields control back to the event loop, which means it needs to be inside a
yield from
call.asyncio
is single-threaded, so when you're blocking on thetime.sleep(10)
call in your second example, there's no way for the event loop to run. That means when the timeout you set usingwait_for
expires, the event loop won't be able to take action on it. The event loop doesn't get an opportunity to run again untilcoro
exits, at which point its too late.This is why in general, you should always avoid any blocking calls that aren't asynchronous; any time a call blocks without yielding to the event loop, nothing else in your program can execute, which is probably not what you want. If you really need to do a long, blocking operation, you should try to use
BaseEventLoop.run_in_executor
to run it in a thread or process pool, which will avoid blocking the event loop:Thx @dano for your answer. If running a
coroutine
is not a hard requirement, here is a reworked, more compact versionFor the curious, a little debugging in my
Python 3.5.2
showed that passingNone
as an executor results in the creation of a_default_executor
, as follows:The examples I've seen for timeout handling are very trivial. Given reality, my app is bit more complex. The sequence is:
To achieve all of the above, while keeping the event loop running, the resulting code contains following code: