Reputation: 93
I have an asyncio.Task
that I need to cancel after some amount of time. Before cancelling, the task needs to do some cleanup. According to the docs, I should just be able to call task.cancel or asyncio.wait_for(coroutine, delay)
and intercept an asyncio.TimeoutError
in the coroutine, but the following example is not working. I've tried intercepting other errors, and calling task.cancel
instead, but neither have worked. Am I misunderstanding how cancelling a task works?
@asyncio.coroutine
def toTimeout():
try:
i = 0
while True:
print("iteration ", i, "......"); i += 1
yield from asyncio.sleep(1)
except asyncio.TimeoutError:
print("timed out")
def main():
#... do some stuff
yield from asyncio.wait_for(toTimeout(), 10)
#... do some more stuff
asyncio.get_event_loop().run_until_complete(main())
asyncio.get_event_loop().run_forever()
Upvotes: 3
Views: 19767
Reputation: 94881
The documentation for asyncio.wait_for
specifies that it will cancel the underlying task, and then raise TimeoutError
from the wait_for
call itself:
Returns result of the Future or coroutine. When a timeout occurs, it cancels the task and raises
asyncio.TimeoutError
.
And you are correct that task cancellation can indeed be intercepted:
[
Task.cancel
] arranges for aCancelledError
to be thrown into the wrapped coroutine on the next cycle through the event loop. The coroutine then has a chance to clean up or even deny the request usingtry
/except
/finally
.
Note that the docs specify that CancelledError
is thrown into the coroutine, not TimeoutError
.
If you make that adjustment, things work the way you expect:
import asyncio
@asyncio.coroutine
def toTimeout():
try:
i = 0
while True:
print("iteration ", i, "......"); i += 1
yield from asyncio.sleep(1)
except asyncio.CancelledError:
print("timed out")
def main():
#... do some stuff
yield from asyncio.wait_for(toTimeout(), 3)
#... do some more stuff
asyncio.get_event_loop().run_until_complete(main())
Output:
iteration 0 ......
iteration 1 ......
iteration 2 ......
timed out
Traceback (most recent call last):
File "aio.py", line 18, in <module>
asyncio.get_event_loop().run_until_complete(main())
File "/usr/lib/python3.4/asyncio/base_events.py", line 316, in run_until_complete
return future.result()
File "/usr/lib/python3.4/asyncio/futures.py", line 275, in result
raise self._exception
File "/usr/lib/python3.4/asyncio/tasks.py", line 238, in _step
result = next(coro)
File "aio.py", line 15, in main
yield from asyncio.wait_for(toTimeout(), 3)
File "/usr/lib/python3.4/asyncio/tasks.py", line 381, in wait_for
raise futures.TimeoutError()
concurrent.futures._base.TimeoutError
As you can see, now 'timed out'
gets printed before the TimeoutError
is raised by wait_for
.
Upvotes: 8