classification
Title: Incorrect import of TimeoutError while creating happy eyeballs connection
Type: behavior Stage: test needed
Components: asyncio Versions: Python 3.9, Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: asvetlov, xtreak, yselivanov
Priority: normal Keywords: patch

Created on 2019-12-24 07:04 by xtreak, last changed 2020-01-08 19:00 by xtreak.

Pull Requests
URL Status Linked Edit
PR 17691 merged asvetlov, 2019-12-24 08:44
PR 17693 merged miss-islington, 2019-12-24 10:48
Messages (5)
msg358841 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2019-12-24 07:04
I guess the TimeoutError exception needs to be imported from asyncio.exceptions and not from asyncio.futures that causes AttributeError while instantiating a connection with happy eyeballs.

./python.exe -m asyncio
asyncio REPL 3.9.0a2+ (heads/master:068768faf6, Dec 23 2019, 18:35:26)
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Use "await" directly instead of "asyncio.run()".
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> conn = await asyncio.open_connection("localhost", port=8000, happy_eyeballs_delay=1)
Traceback (most recent call last):
  File "/Users/kasingar/stuff/python/cpython/Lib/concurrent/futures/_base.py", line 439, in result
    return self.__get_result()
  File "/Users/kasingar/stuff/python/cpython/Lib/concurrent/futures/_base.py", line 388, in __get_result
    raise self._exception
  File "<console>", line 1, in <module>
  File "/Users/kasingar/stuff/python/cpython/Lib/asyncio/streams.py", line 52, in open_connection
    transport, _ = await loop.create_connection(
  File "/Users/kasingar/stuff/python/cpython/Lib/asyncio/base_events.py", line 1041, in create_connection
    sock, _, _ = await staggered.staggered_race(
  File "/Users/kasingar/stuff/python/cpython/Lib/asyncio/staggered.py", line 144, in staggered_race
    raise d.exception()
  File "/Users/kasingar/stuff/python/cpython/Lib/asyncio/staggered.py", line 86, in run_one_coro
    with contextlib.suppress(futures.TimeoutError):
AttributeError: module 'asyncio.futures' has no attribute 'TimeoutError'
msg358845 - (view) Author: Andrew Svetlov (asvetlov) * (Python committer) Date: 2019-12-24 08:42
You are right.

Hmm, I thought that Happy Eyeballs is better tested.
msg358847 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2019-12-24 11:23
Is there a way this can be tested? I tried simulating OSError for IPv4 address and returning socket for ipv6 one like it resolves correctly. I guess that's the underlying idea of happy eyeballs but a test can be added for basic workflow since it lacks tests.

@patch_socket
def test_create_connection_happy_eyeballs_exception(self, sock_mock):
    async def getaddrinfo(*args, **kw):
        return [(2, 1, 6, '', ('127.0.0.1', 8000)),
                (2, 1, 6, '', ('::1', 8000, 0, 0)),]

    def getaddrinfo_task(*args, **kwds):
        return self.loop.create_task(getaddrinfo(*args, **kwds))

    self.loop.getaddrinfo = getaddrinfo_task
    with mock.patch.object(self.loop, 'sock_connect',
                           side_effect=[OSError, sock_mock]):
        coro = self.loop.create_connection(MyProto, host='localhost', port=8000,
                                           happy_eyeballs_delay=1)
        transport, _ = self.loop.run_until_complete(coro)
        transport.close()
msg358848 - (view) Author: Andrew Svetlov (asvetlov) * (Python committer) Date: 2019-12-24 13:27
The main problem of mock-based tests in asyncio is that the tests become unreliably very easy after mocked asyncio internal changes.

I'm +0 on adding the proposed test. The better idea is serving on explicit AF_INET '127.0.0.1:<port>' address when the connection is established to AF_INET6 '[::1]:<port>'.
msg359625 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2020-01-08 19:00
Sorry, I never got around writing a proper test for this so I am moving it to test needed if someone wants to volunteer for it. trio has some test cases for their happy eyeball implementation if it helps : https://github.com/python-trio/trio/blob/c5497c5ac4f6c457e3390c69cb8b5b62eca41979/trio/tests/test_highlevel_open_tcp_stream.py#L326
History
Date User Action Args
2020-01-08 19:00:16xtreaksetmessages: + msg359625
stage: patch review -> test needed
2019-12-24 13:27:14asvetlovsetmessages: + msg358848
2019-12-24 11:23:15xtreaksetmessages: + msg358847
2019-12-24 10:48:26miss-islingtonsetpull_requests: + pull_request17147
2019-12-24 08:44:08asvetlovsetkeywords: + patch
stage: patch review
pull_requests: + pull_request17146
2019-12-24 08:42:21asvetlovsetmessages: + msg358845
2019-12-24 07:04:13xtreakcreate