Why does asyncio.wait_for() need a timeout?

Ian Kelly ian.g.kelly at gmail.com
Fri Nov 24 11:40:48 EST 2017


On Fri, Nov 24, 2017 at 9:35 AM, Ian Kelly <ian.g.kelly at gmail.com> wrote:
> On Fri, Nov 24, 2017 at 6:31 AM, Frank Millman <frank at chagford.com> wrote:
>> "Frank Millman"  wrote in message news:ov5v3s$bv7$1 at blaine.gmane.org...
>>
>>> Below is a simple asyncio loop that runs two background tasks.
>>>
>> [...]
>>>
>>>
>>> Both take an optional timeout.
>>>
>>> If I use the first method without a timeout, the cancellation completes
>>> and the loop stops.
>>>
>>> If I use the second method without a timeout, the future is cancelled, but
>>> the program hangs.
>>>
>>> If I add a timeout to the second one, it behaves the same as the first
>>> one.
>>>
>>> Is there a reason for this?
>>>
>>
>> I have figured out half of the answer.
>>
>> 'timeout' is an optional argument when using wait(), but a required one when
>> using wait_for().
>>
>> Therefore asyncio is raising an exception.
>>
>> However, I do not understand why no traceback appears.
>
> Here's the output I get when I try it and then interrupt with Ctrl-C:
>
> $ python3 test.py
> From 1: 1
> From 2: 1
> From 1: 2
> From 2: 2
> From 1: 3
> From 2: 3
> From 1: 4
> From 2: 4
> From 1: 5
> From 2: 5
> From 1: 6
> counter1 cancelled
> ^CTraceback (most recent call last):
>   File "test.py", line 27, in <module>
>     loop.run_forever()
>   File "/usr/lib/python3.5/asyncio/base_events.py", line 345, in run_forever
>     self._run_once()
>   File "/usr/lib/python3.5/asyncio/base_events.py", line 1276, in _run_once
>     event_list = self._selector.select(timeout)
>   File "/usr/lib/python3.5/selectors.py", line 441, in select
>     fd_event_list = self._epoll.poll(timeout, max_ev)
> KeyboardInterrupt
> Task exception was never retrieved
> future: <Task finished coro=<counter2() done, defined at test.py:13>
> exception=TypeError("wait_for() missing 1 required positional
> argument: 'timeout'",)>
> Traceback (most recent call last):
>   File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
>     result = coro.send(None)
>   File "test.py", line 20, in counter2
>     await asyncio.wait_for(cnt1)  # hangs
> TypeError: wait_for() missing 1 required positional argument: 'timeout'
>
>
> The unhandled exception is shown as a warning when the loop exits. It
> can't be shown prior to that because there could be some other task,
> even one that hasn't been scheduled yet, that might try to get the
> result of the counter2 task and handle the exception.

By the way, this is what you get instead when you replace
run_forever() with run_until_complete(cnt2):

$ python3 test.py
>From 1: 1
>From 2: 1
>From 1: 2
>From 2: 2
>From 1: 3
>From 2: 3
>From 1: 4
>From 2: 4
>From 1: 5
>From 2: 5
>From 1: 6
counter1 cancelled
Traceback (most recent call last):
  File "test.py", line 27, in <module>
    loop.run_until_complete(cnt2)
  File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in
run_until_complete
    return future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "test.py", line 20, in counter2
    await asyncio.wait_for(cnt1)  # hangs
TypeError: wait_for() missing 1 required positional argument: 'timeout'

No need for Ctrl-C in this case because the loop notices that counter2
died and stops on its own, and you get a nice traceback explaining
what happened.



More information about the Python-list mailing list