aiohttp
aiohttp copied to clipboard
Tests failing with IPv6 disabled
Describe the bug
Some aiohttp tests fail when IPv6 is disabled with the kernel parameter ipv6.disable=1.
To Reproduce
Run make on a system with IPv6 disabled.
Expected behavior
Tests should pass.
Logs/tracebacks
Log
$ make test
============================= test session starts ==============================
platform linux -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0
codspeed: 3.0.0 (disabled, mode: walltime, timer_resolution: 1.0ns)
rootdir: aiohttp
configfile: setup.cfg
testpaths: tests/
plugins: cov-5.0.0, mock-3.14.0, codspeed-3.0.0, xdist-3.6.1
created: 16/16 workers
16 workers [3430 items]
.....................x.................x................................ [ 2%]
...........F..............................................E............. [ 4%]
........................................................................ [ 6%]
........................................................................ [ 8%]
........................................................................ [ 10%]
........................................................................ [ 12%]
.......................................x.........x...................... [ 14%]
........................................................................ [ 16%]
..........................................s.ss......................s... [ 18%]
.................................................................s...... [ 20%]
........................................................................ [ 23%]
............................................................x........... [ 25%]
........................................................................ [ 27%]
....................................................................F... [ 29%]
.............................F........................E................. [ 31%]
......E................................................................. [ 33%]
........................................................................ [ 35%]
........................................................................ [ 37%]
............................................x........................... [ 39%]
...........................................................s.s........ss [ 41%]
........................................................................ [ 43%]
.......................s................................................ [ 46%]
........................................................................ [ 48%]
...........................................................s.s.......... [ 50%]
........................................................................ [ 52%]
..................x...................................s................. [ 54%]
.................s.............................................F........ [ 56%]
........................................................................ [ 58%]
........................................................................ [ 60%]
..............................................s......................... [ 62%]
........................................................................ [ 64%]
........................................................................ [ 67%]
.....s.................................................................. [ 69%]
............s........................................................... [ 71%]
........................................................................ [ 73%]
........................................................................ [ 75%]
.x...................................................................... [ 77%]
........................................................................ [ 79%]
........................................................................ [ 81%]
........................................................................ [ 83%]
..............................s.s....................................... [ 85%]
........................................................................ [ 88%]
........................................................................ [ 90%]
........................................................................ [ 92%]
........................................................................ [ 94%]
........................................................................ [ 96%]
........................................................................ [ 98%]
................................................. [100%]
==================================== ERRORS ====================================
__________ ERROR at teardown of test_tcp_connector_interleave[pyloop] __________
[gw15] linux -- Python 3.13.0 aiohttp/.direnv/python-3.13/bin/python
cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x7fd883055940>
when = 'teardown'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
@classmethod
def from_call(
cls,
func: Callable[[], TResult],
when: Literal["collect", "setup", "call", "teardown"],
reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,
) -> CallInfo[TResult]:
"""Call func, wrapping the result in a CallInfo.
:param func:
The function to call. Called without arguments.
:type func: Callable[[], _pytest.runner.TResult]
:param when:
The phase in which the function is called.
:param reraise:
Exception or exceptions that shall propagate if raised by the
function, instead of being wrapped in the CallInfo.
"""
excinfo = None
start = timing.time()
precise_start = timing.perf_counter()
try:
> result: TResult | None = func()
cls = <class '_pytest.runner.CallInfo'>
duration = 0.12177757800236577
excinfo = <ExceptionInfo PytestUnraisableExceptionWarning('Exception ignored in: <function Connection.__del__ at 0x7fd88ab63740>...=\'mocked.host\', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>\n') tblen=11>
func = <function call_and_report.<locals>.<lambda> at 0x7fd883055940>
precise_start = 29088.698029066
precise_stop = 29088.819806644
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
result = None
start = 1731621293.6572301
stop = 1731621293.7790093
when = 'teardown'
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/runner.py:341:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/runner.py:242: in <lambda>
lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
item = <Function test_tcp_connector_interleave[pyloop]>
kwds = {'nextitem': <Function test_tcp_connector_family_is_respected[pyloop]>}
runtest_hook = <HookCaller 'pytest_runtest_teardown'>
.direnv/python-3.13/lib/python3.13/site-packages/pluggy/_hooks.py:513: in __call__
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
firstresult = False
kwargs = {'item': <Function test_tcp_connector_interleave[pyloop]>, 'nextitem': <Function test_tcp_connector_family_is_respected[pyloop]>}
self = <HookCaller 'pytest_runtest_teardown'>
.direnv/python-3.13/lib/python3.13/site-packages/pluggy/_manager.py:120: in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
firstresult = False
hook_name = 'pytest_runtest_teardown'
kwargs = {'item': <Function test_tcp_connector_interleave[pyloop]>, 'nextitem': <Function test_tcp_connector_family_is_respected[pyloop]>}
methods = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'aiohttp/.direnv/python-3...n' from 'aiohttp/.direnv/python-3.13/lib/python3.13/site-packages/_pytest/threadexception.py'>>]
self = <_pytest.config.PytestPluginManager object at 0x7fd88be35d30>
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/threadexception.py:97: in pytest_runtest_teardown
yield from thread_exception_runtest_hook()
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/threadexception.py:68: in thread_exception_runtest_hook
yield
cm = <_pytest.threadexception.catch_threading_exception object at 0x7fd883147e30>
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/unraisableexception.py:100: in pytest_runtest_teardown
yield from unraisable_exception_runtest_hook()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def unraisable_exception_runtest_hook() -> Generator[None]:
with catch_unraisable_exception() as cm:
try:
yield
finally:
if cm.unraisable:
if cm.unraisable.err_msg is not None:
err_msg = cm.unraisable.err_msg
else:
err_msg = "Exception ignored in"
msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
msg += "".join(
traceback.format_exception(
cm.unraisable.exc_type,
cm.unraisable.exc_value,
cm.unraisable.exc_traceback,
)
)
> warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
E pytest.PytestUnraisableExceptionWarning: Exception ignored in: <function Connection.__del__ at 0x7fd88ab63740>
E
E Traceback (most recent call last):
E File "aiohttp/aiohttp/connector.py", line 143, in __del__
E _warnings.warn(f"Unclosed connection {self!r}", ResourceWarning, **kwargs)
E ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E ResourceWarning: Unclosed connection Connection<ConnectionKey(host='mocked.host', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
cm = <_pytest.unraisableexception.catch_unraisable_exception object at 0x7fd88319b9b0>
err_msg = 'Exception ignored in'
msg = 'Exception ignored in: <function Connection.__del__ at 0x7fd88ab63740>\n\nTraceback (most recent call last):\n File "...ionKey(host=\'mocked.host\', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>\n'
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/unraisableexception.py:85: PytestUnraisableExceptionWarning
_____ ERROR at teardown of test_tcp_connector_happy_eyeballs[pyloop-0.25] ______
[gw13] linux -- Python 3.13.0 aiohttp/.direnv/python-3.13/bin/python
cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x7f0f3c5d1da0>
when = 'teardown'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
@classmethod
def from_call(
cls,
func: Callable[[], TResult],
when: Literal["collect", "setup", "call", "teardown"],
reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,
) -> CallInfo[TResult]:
"""Call func, wrapping the result in a CallInfo.
:param func:
The function to call. Called without arguments.
:type func: Callable[[], _pytest.runner.TResult]
:param when:
The phase in which the function is called.
:param reraise:
Exception or exceptions that shall propagate if raised by the
function, instead of being wrapped in the CallInfo.
"""
excinfo = None
start = timing.time()
precise_start = timing.perf_counter()
try:
> result: TResult | None = func()
cls = <class '_pytest.runner.CallInfo'>
duration = 0.14997371899880818
excinfo = <ExceptionInfo PytestUnraisableExceptionWarning('Exception ignored in: <function Connection.__del__ at 0x7f0f44e5b740>...=\'mocked.host\', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>\n') tblen=11>
func = <function call_and_report.<locals>.<lambda> at 0x7f0f3c5d1da0>
precise_start = 29097.925701192
precise_stop = 29098.075674911
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
result = None
start = 1731621302.8849025
stop = 1731621303.0348785
when = 'teardown'
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/runner.py:341:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/runner.py:242: in <lambda>
lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
item = <Function test_tcp_connector_happy_eyeballs[pyloop-0.25]>
kwds = {'nextitem': <Function test_http_request_parser_bad_method[py-parser-pyloop-;]>}
runtest_hook = <HookCaller 'pytest_runtest_teardown'>
.direnv/python-3.13/lib/python3.13/site-packages/pluggy/_hooks.py:513: in __call__
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
firstresult = False
kwargs = {'item': <Function test_tcp_connector_happy_eyeballs[pyloop-0.25]>, 'nextitem': <Function test_http_request_parser_bad_method[py-parser-pyloop-;]>}
self = <HookCaller 'pytest_runtest_teardown'>
.direnv/python-3.13/lib/python3.13/site-packages/pluggy/_manager.py:120: in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
firstresult = False
hook_name = 'pytest_runtest_teardown'
kwargs = {'item': <Function test_tcp_connector_happy_eyeballs[pyloop-0.25]>, 'nextitem': <Function test_http_request_parser_bad_method[py-parser-pyloop-;]>}
methods = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'aiohttp/.direnv/python-3...n' from 'aiohttp/.direnv/python-3.13/lib/python3.13/site-packages/_pytest/threadexception.py'>>]
self = <_pytest.config.PytestPluginManager object at 0x7f0f46139d30>
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/threadexception.py:97: in pytest_runtest_teardown
yield from thread_exception_runtest_hook()
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/threadexception.py:68: in thread_exception_runtest_hook
yield
cm = <_pytest.threadexception.catch_threading_exception object at 0x7f0f3c94bbf0>
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/unraisableexception.py:100: in pytest_runtest_teardown
yield from unraisable_exception_runtest_hook()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def unraisable_exception_runtest_hook() -> Generator[None]:
with catch_unraisable_exception() as cm:
try:
yield
finally:
if cm.unraisable:
if cm.unraisable.err_msg is not None:
err_msg = cm.unraisable.err_msg
else:
err_msg = "Exception ignored in"
msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
msg += "".join(
traceback.format_exception(
cm.unraisable.exc_type,
cm.unraisable.exc_value,
cm.unraisable.exc_traceback,
)
)
> warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
E pytest.PytestUnraisableExceptionWarning: Exception ignored in: <function Connection.__del__ at 0x7f0f44e5b740>
E
E Traceback (most recent call last):
E File "aiohttp/aiohttp/connector.py", line 143, in __del__
E _warnings.warn(f"Unclosed connection {self!r}", ResourceWarning, **kwargs)
E ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E ResourceWarning: Unclosed connection Connection<ConnectionKey(host='mocked.host', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
cm = <_pytest.unraisableexception.catch_unraisable_exception object at 0x7f0f3c94bad0>
err_msg = 'Exception ignored in'
msg = 'Exception ignored in: <function Connection.__del__ at 0x7f0f44e5b740>\n\nTraceback (most recent call last):\n File "...ionKey(host=\'mocked.host\', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>\n'
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/unraisableexception.py:85: PytestUnraisableExceptionWarning
_ ERROR at teardown of test_http_request_parser_bad_method[py-parser-pyloop-;] _
[gw13] linux -- Python 3.13.0 aiohttp/.direnv/python-3.13/bin/python
cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x7f0f3c5d3560>
when = 'teardown'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
@classmethod
def from_call(
cls,
func: Callable[[], TResult],
when: Literal["collect", "setup", "call", "teardown"],
reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,
) -> CallInfo[TResult]:
"""Call func, wrapping the result in a CallInfo.
:param func:
The function to call. Called without arguments.
:type func: Callable[[], _pytest.runner.TResult]
:param when:
The phase in which the function is called.
:param reraise:
Exception or exceptions that shall propagate if raised by the
function, instead of being wrapped in the CallInfo.
"""
excinfo = None
start = timing.time()
precise_start = timing.perf_counter()
try:
> result: TResult | None = func()
cls = <class '_pytest.runner.CallInfo'>
duration = 0.15063231300155167
excinfo = <ExceptionInfo PytestUnraisableExceptionWarning('Exception ignored in: <function Connection.__del__ at 0x7f0f44e5b740>...=\'mocked.host\', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>\n') tblen=11>
func = <function call_and_report.<locals>.<lambda> at 0x7f0f3c5d3560>
precise_start = 29098.188133088
precise_stop = 29098.338765401
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
result = None
start = 1731621303.1473334
stop = 1731621303.2979698
when = 'teardown'
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/runner.py:341:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/runner.py:242: in <lambda>
lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
item = <Function test_http_request_parser_bad_method[py-parser-pyloop-;]>
kwds = {'nextitem': <Function test_http_request_parser_bad_method[py-parser-pyloop-<]>}
runtest_hook = <HookCaller 'pytest_runtest_teardown'>
.direnv/python-3.13/lib/python3.13/site-packages/pluggy/_hooks.py:513: in __call__
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
firstresult = False
kwargs = {'item': <Function test_http_request_parser_bad_method[py-parser-pyloop-;]>, 'nextitem': <Function test_http_request_parser_bad_method[py-parser-pyloop-<]>}
self = <HookCaller 'pytest_runtest_teardown'>
.direnv/python-3.13/lib/python3.13/site-packages/pluggy/_manager.py:120: in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
firstresult = False
hook_name = 'pytest_runtest_teardown'
kwargs = {'item': <Function test_http_request_parser_bad_method[py-parser-pyloop-;]>, 'nextitem': <Function test_http_request_parser_bad_method[py-parser-pyloop-<]>}
methods = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'aiohttp/.direnv/python-3...n' from 'aiohttp/.direnv/python-3.13/lib/python3.13/site-packages/_pytest/threadexception.py'>>]
self = <_pytest.config.PytestPluginManager object at 0x7f0f46139d30>
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/threadexception.py:97: in pytest_runtest_teardown
yield from thread_exception_runtest_hook()
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/threadexception.py:68: in thread_exception_runtest_hook
yield
cm = <_pytest.threadexception.catch_threading_exception object at 0x7f0f3d372210>
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/unraisableexception.py:100: in pytest_runtest_teardown
yield from unraisable_exception_runtest_hook()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def unraisable_exception_runtest_hook() -> Generator[None]:
with catch_unraisable_exception() as cm:
try:
yield
finally:
if cm.unraisable:
if cm.unraisable.err_msg is not None:
err_msg = cm.unraisable.err_msg
else:
err_msg = "Exception ignored in"
msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
msg += "".join(
traceback.format_exception(
cm.unraisable.exc_type,
cm.unraisable.exc_value,
cm.unraisable.exc_traceback,
)
)
> warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
E pytest.PytestUnraisableExceptionWarning: Exception ignored in: <function Connection.__del__ at 0x7f0f44e5b740>
E
E Traceback (most recent call last):
E File "aiohttp/aiohttp/connector.py", line 143, in __del__
E _warnings.warn(f"Unclosed connection {self!r}", ResourceWarning, **kwargs)
E ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E ResourceWarning: Unclosed connection Connection<ConnectionKey(host='mocked.host', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
cm = <_pytest.unraisableexception.catch_unraisable_exception object at 0x7f0f3c99f1d0>
err_msg = 'Exception ignored in'
msg = 'Exception ignored in: <function Connection.__del__ at 0x7f0f44e5b740>\n\nTraceback (most recent call last):\n File "...ionKey(host=\'mocked.host\', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>\n'
.direnv/python-3.13/lib/python3.13/site-packages/_pytest/unraisableexception.py:85: PytestUnraisableExceptionWarning
=================================== FAILURES ===================================
________________ test_tcp_connector_happy_eyeballs[pyloop-None] ________________
[gw15] linux -- Python 3.13.0 aiohttp/.direnv/python-3.13/bin/python
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
happy_eyeballs_delay = None
@pytest.mark.parametrize(
("happy_eyeballs_delay"),
[0.1, 0.25, None],
)
async def test_tcp_connector_happy_eyeballs(
loop: Any, happy_eyeballs_delay: Optional[float]
) -> None:
conn = aiohttp.TCPConnector(happy_eyeballs_delay=happy_eyeballs_delay)
ip1 = "dead::beef::"
ip2 = "192.168.1.1"
ips = [ip1, ip2]
addrs_tried = []
req = ClientRequest(
"GET",
URL("https://mocked.host"),
loop=loop,
)
async def _resolve_host(host, port, traces=None):
return [
{
"hostname": host,
"host": ip,
"port": port,
"family": socket.AF_INET6 if ":" in ip else socket.AF_INET,
"proto": 0,
"flags": socket.AI_NUMERICHOST,
}
for ip in ips
]
conn._resolve_host = _resolve_host
os_error = False
connected = False
async def sock_connect(*args, **kwargs):
addr = args[1]
nonlocal os_error
addrs_tried.append(addr)
if addr[0] == ip1:
os_error = True
raise OSError
async def create_connection(*args, **kwargs):
sock: socket.socket = kwargs["sock"]
# Close the socket since we are not actually connecting
# and we don't want to leak it.
sock.close()
nonlocal connected
connected = True
tr = create_mocked_conn(loop)
pr = create_mocked_conn(loop)
return tr, pr
conn._loop.sock_connect = sock_connect
conn._loop.create_connection = create_connection
established_connection = await conn.connect(req, [], ClientTimeout())
> assert addrs_tried == [(ip1, 443, 0, 0), (ip2, 443)]
E AssertionError: assert [('192.168.1.1', 443)] == [('dead::beef...68.1.1', 443)]
E
E At index 0 diff: ('192.168.1.1', 443) != ('dead::beef::', 443, 0, 0)
E Right contains one more item: ('192.168.1.1', 443)
E Use -v to get more diff
_resolve_host = <function test_tcp_connector_happy_eyeballs.<locals>._resolve_host at 0x7fd883055c60>
addrs_tried = [('192.168.1.1', 443)]
conn = <aiohttp.connector.TCPConnector object at 0x7fd88303d400>
connected = True
create_connection = <function test_tcp_connector_happy_eyeballs.<locals>.create_connection at 0x7fd883055e40>
established_connection = Connection<ConnectionKey(host='mocked.host', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
happy_eyeballs_delay = None
ip1 = 'dead::beef::'
ip2 = '192.168.1.1'
ips = ['dead::beef::', '192.168.1.1']
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
os_error = False
req = <aiohttp.client_reqrep.ClientRequest object at 0x7fd88303da90>
sock_connect = <function test_tcp_connector_happy_eyeballs.<locals>.sock_connect at 0x7fd883055d00>
tests/test_connector.py:879: AssertionError
________________ test_tcp_connector_happy_eyeballs[pyloop-0.1] _________________
[gw13] linux -- Python 3.13.0 aiohttp/.direnv/python-3.13/bin/python
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
happy_eyeballs_delay = 0.1
@pytest.mark.parametrize(
("happy_eyeballs_delay"),
[0.1, 0.25, None],
)
async def test_tcp_connector_happy_eyeballs(
loop: Any, happy_eyeballs_delay: Optional[float]
) -> None:
conn = aiohttp.TCPConnector(happy_eyeballs_delay=happy_eyeballs_delay)
ip1 = "dead::beef::"
ip2 = "192.168.1.1"
ips = [ip1, ip2]
addrs_tried = []
req = ClientRequest(
"GET",
URL("https://mocked.host"),
loop=loop,
)
async def _resolve_host(host, port, traces=None):
return [
{
"hostname": host,
"host": ip,
"port": port,
"family": socket.AF_INET6 if ":" in ip else socket.AF_INET,
"proto": 0,
"flags": socket.AI_NUMERICHOST,
}
for ip in ips
]
conn._resolve_host = _resolve_host
os_error = False
connected = False
async def sock_connect(*args, **kwargs):
addr = args[1]
nonlocal os_error
addrs_tried.append(addr)
if addr[0] == ip1:
os_error = True
raise OSError
async def create_connection(*args, **kwargs):
sock: socket.socket = kwargs["sock"]
# Close the socket since we are not actually connecting
# and we don't want to leak it.
sock.close()
nonlocal connected
connected = True
tr = create_mocked_conn(loop)
pr = create_mocked_conn(loop)
return tr, pr
conn._loop.sock_connect = sock_connect
conn._loop.create_connection = create_connection
established_connection = await conn.connect(req, [], ClientTimeout())
> assert addrs_tried == [(ip1, 443, 0, 0), (ip2, 443)]
E AssertionError: assert [('192.168.1.1', 443)] == [('dead::beef...68.1.1', 443)]
E
E At index 0 diff: ('192.168.1.1', 443) != ('dead::beef::', 443, 0, 0)
E Right contains one more item: ('192.168.1.1', 443)
E Use -v to get more diff
_resolve_host = <function test_tcp_connector_happy_eyeballs.<locals>._resolve_host at 0x7f0f3c5d3e20>
addrs_tried = [('192.168.1.1', 443)]
conn = <aiohttp.connector.TCPConnector object at 0x7f0f3d39d6e0>
connected = True
create_connection = <function test_tcp_connector_happy_eyeballs.<locals>.create_connection at 0x7f0f3c5d3a60>
established_connection = Connection<ConnectionKey(host='mocked.host', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
happy_eyeballs_delay = 0.1
ip1 = 'dead::beef::'
ip2 = '192.168.1.1'
ips = ['dead::beef::', '192.168.1.1']
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
os_error = False
req = <aiohttp.client_reqrep.ClientRequest object at 0x7f0f3d3cf850>
sock_connect = <function test_tcp_connector_happy_eyeballs.<locals>.sock_connect at 0x7f0f3c5d18a0>
tests/test_connector.py:879: AssertionError
________________ test_tcp_connector_happy_eyeballs[pyloop-0.25] ________________
[gw13] linux -- Python 3.13.0 aiohttp/.direnv/python-3.13/bin/python
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
happy_eyeballs_delay = 0.25
@pytest.mark.parametrize(
("happy_eyeballs_delay"),
[0.1, 0.25, None],
)
async def test_tcp_connector_happy_eyeballs(
loop: Any, happy_eyeballs_delay: Optional[float]
) -> None:
conn = aiohttp.TCPConnector(happy_eyeballs_delay=happy_eyeballs_delay)
ip1 = "dead::beef::"
ip2 = "192.168.1.1"
ips = [ip1, ip2]
addrs_tried = []
req = ClientRequest(
"GET",
URL("https://mocked.host"),
loop=loop,
)
async def _resolve_host(host, port, traces=None):
return [
{
"hostname": host,
"host": ip,
"port": port,
"family": socket.AF_INET6 if ":" in ip else socket.AF_INET,
"proto": 0,
"flags": socket.AI_NUMERICHOST,
}
for ip in ips
]
conn._resolve_host = _resolve_host
os_error = False
connected = False
async def sock_connect(*args, **kwargs):
addr = args[1]
nonlocal os_error
addrs_tried.append(addr)
if addr[0] == ip1:
os_error = True
raise OSError
async def create_connection(*args, **kwargs):
sock: socket.socket = kwargs["sock"]
# Close the socket since we are not actually connecting
# and we don't want to leak it.
sock.close()
nonlocal connected
connected = True
tr = create_mocked_conn(loop)
pr = create_mocked_conn(loop)
return tr, pr
conn._loop.sock_connect = sock_connect
conn._loop.create_connection = create_connection
established_connection = await conn.connect(req, [], ClientTimeout())
> assert addrs_tried == [(ip1, 443, 0, 0), (ip2, 443)]
E AssertionError: assert [('192.168.1.1', 443)] == [('dead::beef...68.1.1', 443)]
E
E At index 0 diff: ('192.168.1.1', 443) != ('dead::beef::', 443, 0, 0)
E Right contains one more item: ('192.168.1.1', 443)
E Use -v to get more diff
_resolve_host = <function test_tcp_connector_happy_eyeballs.<locals>._resolve_host at 0x7f0f3c5d3ec0>
addrs_tried = [('192.168.1.1', 443)]
conn = <aiohttp.connector.TCPConnector object at 0x7f0f3d39e060>
connected = True
create_connection = <function test_tcp_connector_happy_eyeballs.<locals>.create_connection at 0x7f0f3c5d1c60>
established_connection = Connection<ConnectionKey(host='mocked.host', port=443, is_ssl=True, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
happy_eyeballs_delay = 0.25
ip1 = 'dead::beef::'
ip2 = '192.168.1.1'
ips = ['dead::beef::', '192.168.1.1']
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
os_error = False
req = <aiohttp.client_reqrep.ClientRequest object at 0x7f0f3c9e8250>
sock_connect = <function test_tcp_connector_happy_eyeballs.<locals>.sock_connect at 0x7f0f3c5d1b20>
tests/test_connector.py:879: AssertionError
_____________________ test_test_server_hostnames[::1-::1] ______________________
[gw3] linux -- Python 3.13.0 aiohttp/.direnv/python-3.13/bin/python
hostname = '::1', expected_host = '::1'
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
@pytest.mark.parametrize(
("hostname", "expected_host"),
[("127.0.0.1", "127.0.0.1"), ("localhost", "127.0.0.1"), ("::1", "::1")],
)
async def test_test_server_hostnames(hostname, expected_host, loop) -> None:
app = _create_example_app()
server = TestServer(app, host=hostname, loop=loop)
> async with server:
app = <Application 0x7f6823881220>
expected_host = '::1'
hostname = '::1'
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
server = <aiohttp.test_utils.TestServer object at 0x7f682388d440>
tests/test_test_utils.py:425:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
aiohttp/test_utils.py:226: in __aenter__
await self.start_server(loop=self._loop)
self = <aiohttp.test_utils.TestServer object at 0x7f682388d440>
aiohttp/test_utils.py:151: in start_server
_sock = self.socket_factory(self.host, self.port, family)
absolute_host = '[::1]'
family = <AddressFamily.AF_INET6: 10>
kwargs = {}
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
self = <aiohttp.test_utils.TestServer object at 0x7f682388d440>
version = 6
aiohttp/test_utils.py:89: in get_port_socket
s = socket.socket(family, socket.SOCK_STREAM)
family = <AddressFamily.AF_INET6: 10>
host = '::1'
port = 0
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <socket.socket fd=-1, family=0, type=0, proto=0>
family = <AddressFamily.AF_INET6: 10>, type = <SocketKind.SOCK_STREAM: 1>
proto = 0, fileno = None
def __init__(self, family=-1, type=-1, proto=-1, fileno=None):
# For user code address family and type values are IntEnum members, but
# for the underlying _socket.socket they're just integers. The
# constructor of _socket.socket converts the given argument to an
# integer automatically.
if fileno is None:
if family == -1:
family = AF_INET
if type == -1:
type = SOCK_STREAM
if proto == -1:
proto = 0
> _socket.socket.__init__(self, family, type, proto, fileno)
E OSError: [Errno 97] Address family not supported by protocol
family = <AddressFamily.AF_INET6: 10>
fileno = None
proto = 0
self = <socket.socket fd=-1, family=0, type=0, proto=0>
type = <SocketKind.SOCK_STREAM: 1>
/nix/store/b5w6mr7v3mnl9s163yimdksddnwhvqyl-python3-3.13.0/lib/python3.13/socket.py:233: OSError
---------- coverage: platform linux, python 3.13.0-final-0 -----------
Name Stmts Miss Branch BrPart Cover
---------------------------------------------------------------------------
aiohttp/__init__.py 27 2 2 0 93%
aiohttp/_websocket/__init__.py 0 0 0 0 100%
aiohttp/_websocket/helpers.py 77 0 40 0 100%
aiohttp/_websocket/models.py 51 0 0 0 100%
aiohttp/_websocket/reader.py 6 0 0 0 100%
aiohttp/_websocket/reader_py.py 268 226 106 0 11%
aiohttp/_websocket/writer.py 73 0 24 0 100%
aiohttp/abc.py 103 0 66 0 100%
aiohttp/base_protocol.py 71 0 26 0 100%
aiohttp/client.py 562 12 244 5 98%
aiohttp/client_exceptions.py 156 2 46 0 99%
aiohttp/client_proto.py 164 4 66 5 96%
aiohttp/client_reqrep.py 699 4 342 9 99%
aiohttp/client_ws.py 277 5 98 4 98%
aiohttp/compression_utils.py 70 4 22 3 92%
aiohttp/connector.py 767 51 366 19 92%
aiohttp/cookiejar.py 277 6 136 1 98%
aiohttp/formdata.py 97 2 48 2 97%
aiohttp/hdrs.py 94 0 0 0 100%
aiohttp/helpers.py 468 5 182 12 97%
aiohttp/http.py 11 0 0 0 100%
aiohttp/http_exceptions.py 50 0 4 0 100%
aiohttp/http_parser.py 512 13 214 14 95%
aiohttp/http_websocket.py 6 0 0 0 100%
aiohttp/http_writer.py 140 8 60 1 94%
aiohttp/log.py 7 0 0 0 100%
aiohttp/multipart.py 625 14 280 16 96%
aiohttp/payload.py 251 0 92 3 99%
aiohttp/payload_streamer.py 31 1 4 0 97%
aiohttp/pytest_plugin.py 163 9 64 2 94%
aiohttp/resolver.py 79 1 26 1 98%
aiohttp/streams.py 388 5 144 5 98%
aiohttp/tcp_helpers.py 19 0 8 1 96%
aiohttp/test_utils.py 324 2 74 5 98%
aiohttp/tracing.py 191 0 64 0 100%
aiohttp/typedefs.py 24 0 0 0 100%
aiohttp/web.py 127 0 56 1 99%
aiohttp/web_app.py 292 1 106 1 99%
aiohttp/web_exceptions.py 157 0 10 0 100%
aiohttp/web_fileresponse.py 160 0 53 1 99%
aiohttp/web_log.py 103 0 40 0 100%
aiohttp/web_middlewares.py 59 0 24 0 100%
aiohttp/web_protocol.py 367 18 159 26 92%
aiohttp/web_request.py 446 6 218 4 98%
aiohttp/web_response.py 487 3 276 4 99%
aiohttp/web_routedef.py 100 0 12 0 100%
aiohttp/web_runner.py 213 17 66 3 92%
aiohttp/web_server.py 42 0 12 0 100%
aiohttp/web_urldispatcher.py 742 9 245 6 98%
aiohttp/web_ws.py 391 12 146 12 96%
aiohttp/worker.py 126 7 32 3 94%
tests/autobahn/test_autobahn.py 60 38 24 0 45%
tests/conftest.py 142 16 65 6 88%
tests/test_base_protocol.py 200 0 8 0 100%
tests/test_benchmarks_client.py 153 0 34 0 100%
tests/test_benchmarks_client_request.py 52 0 12 0 100%
tests/test_benchmarks_client_ws.py 66 0 18 0 100%
tests/test_benchmarks_cookiejar.py 14 0 2 0 100%
tests/test_benchmarks_http_websocket.py 71 0 24 0 100%
tests/test_benchmarks_http_writer.py 11 0 4 0 100%
tests/test_circular_imports.py 29 0 13 0 100%
tests/test_classbasedview.py 39 2 4 0 95%
tests/test_client_connection.py 94 0 14 1 99%
tests/test_client_exceptions.py 199 0 24 0 100%
tests/test_client_fingerprint.py 59 0 24 0 100%
tests/test_client_functional.py 2630 30 514 10 99%
tests/test_client_proto.py 99 0 0 0 100%
tests/test_client_request.py 843 1 128 0 99%
tests/test_client_response.py 480 5 38 1 99%
tests/test_client_session.py 593 7 127 4 98%
tests/test_client_ws.py 489 0 158 0 100%
tests/test_client_ws_functional.py 834 19 84 4 97%
tests/test_compression_utils.py 15 0 0 0 100%
tests/test_connector.py 2121 59 340 10 97%
tests/test_cookiejar.py 466 0 68 2 99%
tests/test_flowcontrol_streams.py 83 0 4 0 100%
tests/test_formdata.py 86 0 24 0 100%
tests/test_helpers.py 454 10 170 8 97%
tests/test_http_exceptions.py 109 0 10 0 100%
tests/test_http_parser.py 1106 17 218 0 98%
tests/test_http_writer.py 260 0 20 0 100%
tests/test_imports.py 40 17 16 0 59%
tests/test_loop.py 36 2 4 0 95%
tests/test_multipart.py 829 0 226 1 99%
tests/test_multipart_helpers.py 446 19 82 0 95%
tests/test_payload.py 93 3 8 0 97%
tests/test_proxy.py 325 0 110 0 100%
tests/test_proxy_functional.py 462 175 110 3 66%
tests/test_pytest_plugin.py 27 0 0 0 100%
tests/test_resolver.py 231 4 78 0 99%
tests/test_route_def.py 210 20 26 0 92%
tests/test_run_app.py 577 19 100 13 95%
tests/test_streams.py 1077 0 128 0 100%
tests/test_tcp_helpers.py 52 6 14 3 83%
tests/test_test_utils.py 274 4 76 7 97%
tests/test_tracing.py 48 0 2 0 100%
tests/test_urldispatch.py 859 12 102 1 98%
tests/test_web_app.py 453 4 56 0 99%
tests/test_web_cli.py 94 0 28 0 100%
tests/test_web_exceptions.py 179 0 38 0 100%
tests/test_web_functional.py 1605 28 206 3 98%
tests/test_web_log.py 132 2 16 0 99%
tests/test_web_middleware.py 284 3 54 2 99%
tests/test_web_request.py 513 0 34 0 100%
tests/test_web_request_handler.py 42 1 2 0 98%
tests/test_web_response.py 914 3 90 1 99%
tests/test_web_runner.py 151 9 32 0 94%
tests/test_web_sendfile.py 84 0 0 0 100%
tests/test_web_sendfile_functional.py 674 5 72 0 99%
tests/test_web_server.py 165 1 16 0 99%
tests/test_web_urldispatcher.py 524 2 82 1 99%
tests/test_web_websocket.py 444 2 82 0 99%
tests/test_web_websocket_functional.py 862 51 46 4 94%
tests/test_websocket_data_queue.py 23 0 6 0 100%
tests/test_websocket_handshake.py 152 0 26 0 100%
tests/test_websocket_parser.py 365 9 58 1 98%
tests/test_websocket_writer.py 97 0 16 0 100%
tests/test_worker.py 187 2 20 1 99%
---------------------------------------------------------------------------
TOTAL 36327 1056 8538 256 97%
============================= slowest 10 durations =============================
9.53s call tests/test_run_app.py::TestShutdown::test_shutdown_new_conn_rejected
4.01s call tests/test_run_app.py::TestShutdown::test_shutdown_pending_handler_responds
2.52s call tests/test_run_app.py::TestShutdown::test_shutdown_wait_for_handler
2.41s call tests/test_run_app.py::TestShutdown::test_shutdown_handler_cancellation_suppressed
2.02s call tests/test_client_functional.py::test_read_timeout_between_chunks[pyloop]
2.01s call tests/test_client_functional.py::test_set_cookies_max_age[pyloop]
1.82s call tests/test_pytest_plugin.py::test_aiohttp_plugin
1.62s call tests/test_run_app.py::TestShutdown::test_shutdown_timeout_handler
1.51s call tests/test_run_app.py::TestShutdown::test_shutdown_timeout_not_reached
1.40s call tests/test_circular_imports.py::test_no_warnings[aiohttp.pytest_plugin]
=========================== short test summary info ============================
SKIPPED [1] tests/test_connector.py:3054: Proactor Event loop present only in Windows
SKIPPED [1] tests/test_connector.py:3062: Proactor Event loop present only in Windows
SKIPPED [1] tests/test_connector.py:3073: Proactor Event loop present only in Windows
SKIPPED [1] tests/test_client_session.py:949: The check is applied in DEBUG mode only
SKIPPED [1] tests/test_connector.py:3271: Proactor Event loop present only in Windows
SKIPPED [1] tests/test_multipart_helpers.py:446: should raise decoding error: %82 is invalid for latin1
SKIPPED [1] tests/test_multipart_helpers.py:455: should raise decoding error: %E4 is invalid for utf-8
SKIPPED [1] tests/test_multipart_helpers.py:510: urllib.parse.unquote is tolerate to standalone % chars
SKIPPED [1] tests/test_multipart_helpers.py:519: urllib.parse.unquote is tolerate to standalone % chars
SKIPPED [1] tests/test_multipart_helpers.py:99: need more smart parser which respects quoted text
SKIPPED [1] tests/test_proxy_functional.py:386: we need to reconsider how we test this
SKIPPED [1] tests/test_proxy_functional.py:407: we need to reconsider how we test this
SKIPPED [1] tests/test_run_app.py:595: IPv6 is not available
SKIPPED [1] tests/test_resolver.py:404: aiodns <3.2.0 required
SKIPPED [1] tests/test_tcp_helpers.py:52: IPv6 is not available
SKIPPED [1] tests/test_proxy_functional.py:129: asyncio on this python supports TLS in TLS
SKIPPED [1] tests/test_web_app.py:365: The check is applied in DEBUG mode only
SKIPPED [1] tests/test_web_runner.py:121: Proactor Event loop present only in Windows
SKIPPED [1] tests/test_web_runner.py:133: Proactor Event loop present only in Windows
XFAIL tests/autobahn/test_autobahn.py::test_client
XFAIL tests/autobahn/test_autobahn.py::test_server
XFAIL tests/test_connector.py::test_del_with_scheduled_cleanup[pyloop]
XFAIL tests/test_client_functional.py::test_broken_connection[pyloop]
XFAIL tests/test_http_parser.py::test_parse_unusual_request_line[c-parser-pyloop] - Regression test for Py parser. May match C behaviour later.
XFAIL tests/test_http_parser.py::test_parse_uri_utf8[c-parser-pyloop] - reason: Not valid HTTP. Maybe update py-parser to reject later.
XFAIL tests/test_http_parser.py::test_http_request_parser_utf8_request_line[c-parser-pyloop] - Regression test for Py parser. May match C behaviour later.
XFAIL tests/test_web_functional.py::test_http10_keep_alive_default[pyloop]
ERROR tests/test_connector.py::test_tcp_connector_interleave[pyloop] - pytest...
ERROR tests/test_connector.py::test_tcp_connector_happy_eyeballs[pyloop-0.25]
ERROR tests/test_http_parser.py::test_http_request_parser_bad_method[py-parser-pyloop-;]
FAILED tests/test_connector.py::test_tcp_connector_happy_eyeballs[pyloop-None]
FAILED tests/test_connector.py::test_tcp_connector_happy_eyeballs[pyloop-0.1]
FAILED tests/test_connector.py::test_tcp_connector_happy_eyeballs[pyloop-0.25]
FAILED tests/test_test_utils.py::test_test_server_hostnames[::1-::1] - OSErro...
======= 4 failed, 3399 passed, 19 skipped, 8 xfailed, 3 errors in 57.44s =======
make: *** [Makefile:105: test] Error 1
Python Version
$ python --version
Python 3.13.0
aiohttp Version
$ python -m pip show aiohttp
Name: aiohttp
Version: 3.11.1
Summary: Async http client/server framework (asyncio)
Home-page: https://github.com/aio-libs/aiohttp
Author:
Author-email:
License: Apache 2
Location: aiohttp/.direnv/python-3.13/lib/python3.13/site-packages
Editable project location: aiohttp
Requires: aiohappyeyeballs, aiosignal, attrs, frozenlist, multidict, propcache, yarl
Required-by:
multidict Version
$ python -m pip show multidict
Name: multidict
Version: 6.1.0
Summary: multidict implementation
Home-page: https://github.com/aio-libs/multidict
Author: Andrew Svetlov
Author-email: [email protected]
License: Apache 2
Location: aiohttp/.direnv/python-3.13/lib/python3.13/site-packages
Requires:
Required-by: aiohttp, yarl
propcache Version
$ python -m pip show propcache
Name: propcache
Version: 0.2.0
Summary: Accelerated property cache
Home-page: https://github.com/aio-libs/propcache
Author: Andrew Svetlov
Author-email: [email protected]
License: Apache-2.0
Location: aiohttp/.direnv/python-3.13/lib/python3.13/site-packages
Requires:
Required-by: aiohttp, yarl
yarl Version
$ python -m pip show yarl
Name: yarl
Version: 1.17.1
Summary: Yet another URL library
Home-page: https://github.com/aio-libs/yarl
Author: Andrew Svetlov
Author-email: [email protected]
License: Apache-2.0
Location: aiohttp/.direnv/python-3.13/lib/python3.13/site-packages
Requires: idna, multidict, propcache
Required-by: aiohttp
OS
NixOS
Related component
Server, Client
Additional context
No response
Code of Conduct
- [X] I agree to follow the aio-libs Code of Conduct
We don't currently run the CI with IPv6 disabled so these are easy to miss.
Triage: not a regression in 3.11. Likely broken for a long time.
Isn't that expected? All the failures are testing IPv6 addresses...
They are expected to not work with IPv6, but test_run_app.py has HAS_IPV6 and disables the tests that need ipv6 with skipif but connector (and maybe others do not) so I'm not sure if the intent was to skip tests that require IPv6 or if they should fail.
Right, I guess we should add a skipif to those 4 tests then.
IIRC, existing skips were added after similar bug report. We never check non-IPv6 run explicitly.