classification
Title: wait_for(gather(...)) logs weird error message
Type: behavior Stage:
Components: asyncio Versions: Python 3.7, Python 3.6, Python 3.5, Python 3.4
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Martin.Teichmann, chris.jerdonek, gvanrossum, yselivanov
Priority: normal Keywords:

Created on 2017-02-03 11:25 by Martin.Teichmann, last changed 2017-08-07 23:45 by chris.jerdonek.

Pull Requests
URL Status Linked Edit
PR 73 Martin.Teichmann, 2017-02-03 11:25
Messages (4)
msg286858 - (view) Author: Martin Teichmann (Martin.Teichmann) * Date: 2017-02-03 11:25
when waiting for a gather that times out, everything works as expected, yet a weird error message is logged. To give a minimal example:

    import asyncio

    @asyncio.coroutine
    def main():
        try:
            sleep = asyncio.sleep(0.2)
            yield from asyncio.wait_for(asyncio.gather(sleep),
                                        timeout=0.1)
        except asyncio.TimeoutError:
            print("timed out: fine")
        yield from asyncio.sleep(0.1)

    asyncio.get_event_loop().run_until_complete(main())

This outputs:

    timed out: fine
    _GatheringFuture exception was never retrieved
    future: <_GatheringFuture finished exception=CancelledError()>
    concurrent.futures._base.CancelledError

As you can see, I used the pre-3.5 syntax so I could test whether it works on older systems. No, it doesn't.

I wrote a unit test for this problem, unfortunately I couldn't solve it yet.
msg287312 - (view) Author: Martin Teichmann (Martin.Teichmann) * Date: 2017-02-08 12:41
I added a solution to this problem. I just silence the bad error message by overwriting _GatheringFuture.__del__ to do nothing. This may have undesired side effects, though.
msg299847 - (view) Author: Chris Jerdonek (chris.jerdonek) * (Python committer) Date: 2017-08-07 12:25
I noticed that the future defined by asyncio.gather(sleep) is in a "pending" state immediately after the asyncio.TimeoutError.

One workaround is to wait for the cancellation to finish:

    @asyncio.coroutine
    def main():
        sleep = asyncio.sleep(0.2)
        future = asyncio.gather(sleep)
        try:
            yield from asyncio.wait_for(future, timeout=0.1)
        except asyncio.TimeoutError:
            print(f'future: {future}')
            try:
                yield from future
            except asyncio.CancelledError:
                print(f'future: {future}')
        yield from asyncio.sleep(0.1)

    asyncio.get_event_loop().run_until_complete(main())

Outputs:

    future: <_GatheringFuture pending>
    future: <_GatheringFuture finished exception=CancelledError()>

Another option is to pass return_exceptions=True to gather(). This appears to make the log messages you were concerned about go away:

    future: <_GatheringFuture pending>
msg299878 - (view) Author: Chris Jerdonek (chris.jerdonek) * (Python committer) Date: 2017-08-07 23:45
By the way, I see this exact issue was also raised and discussed here, with a couple responses by Guido, too:
https://github.com/python/asyncio/issues/253
History
Date User Action Args
2017-08-07 23:45:23chris.jerdoneksetmessages: + msg299878
2017-08-07 12:25:42chris.jerdoneksetnosy: + chris.jerdonek
messages: + msg299847
2017-02-08 12:41:20Martin.Teichmannsetmessages: + msg287312
2017-02-03 11:26:06Martin.Teichmannsettype: behavior
2017-02-03 11:25:24Martin.Teichmanncreate