Yup, I think you've found a bug. wait_for() should definitely cancel
the thing it's waiting for -- if you don't want that, you should call
wait_for(shield(<coroutine_or_task>), <timeout>).

It's likely that most of the wait_for() (or its unittests :-)
implementation predates the rethinking of cancellation.

Can you help fixing this before Python 3.4 beta 3 goes out 9Jan 26 I
think)? I'm really busy through the 31st so I need help.

On Mon, Jan 20, 2014 at 7:41 AM, Gustavo Carneiro <[email protected]> wrote:
> Hi.
>
> I spent a few hours chasing a nasty bug in either tulip or my code.  I got
> this traceback:
>
> Traceback (most recent call last):
>   File "/home/gjc/projects/mollytest/tests/base.py", line 63, in runTest
>     return loop.run_until_complete(self.runTestTask())
>   File "lib/asyncio/base_events.py", line 177, in run_until_complete
>     return future.result()
>   File "lib/asyncio/futures.py", line 236, in result
>     raise self._exception
>   File "lib/asyncio/tasks.py", line 279, in _step
>     result = coro.send(value)
>   File "tests/450-api-place-hide-exchange.py", line 42, in runTestTask
>     lambda line: line[:2] == ['betslip_account_added', '3'] and line[5] ==
> 'dummyex')),
>   File "tests/450-api-place-hide-exchange.py", line 35, in wait_for_line
>     line = yield from api.read_async_line()
>   File "/home/gjc/projects/mollytest/mollyapiclient.py", line 68, in
> read_async_line
>     data = yield from read
>   File "lib/asyncio/streams.py", line 321, in readline
>     assert self._waiter is None
> AssertionError
>
> After much debugging I discovered that:
>
>   1. That assertion fails when there are two concurrent StreamReader read
> operations;
>
>   2. I eventually found out that the "previous" running read operation was
> this one:
>
>                # self.async_reader is a StreamReader object
>   data = yield from asyncio.wait_for(self.async_reader.readline(), timeout)
>
> In the above code, if the timeout actually happens, then
> self.async_reader.readline(), which has been converted to a Task by
> asyncio.wait_for(), is left running.  After the timeout, my script keeps
> running, but the next call to StreamReader.readline() will fail and generate
> the above traceback because the previous Task is still running.  I confirmed
> this by adding a "fut.cancel()" call to tasks.wait_for() right before "raise
> futures.TimeoutError()", which made the problem go away.
>
> I was a bit surprised by the behaviour of wait_for().  If wait_for() creates
> a Task, I reckon it should be the one to cancel it when the timeout happens.
> If you think shouldn't cancel a Task, then I think wait_for() shouldn't
> create it in the first place, and so it should accept only Task as
> parameter, and not coroutine (generator): in this case it's the application
> programmer who should take care of cancelling the task on timeout.
>
> Thoughts?
>
> --
> Gustavo J. A. M. Carneiro
> Gambit Research LLC
> "The universe is always one step beyond logic." -- Frank Herbert



-- 
--Guido van Rossum (python.org/~guido)

Reply via email to