MagicStack/uvloop

libuv 1.48.0: getaddrinfo('', 0): OSError: [Errno 22] Invalid argument

hroncok opened this issue · 7 comments

  • uvloop version: 0.19.0
  • Python version: 3.12.2
  • Platform: Fedora Linux 41
  • Can you reproduce the bug with PYTHONASYNCIODEBUG in env?: yes
  • Does uvloop behave differently from vanilla asyncio? How?: yes, see details

We are trying to update the Fedora uvloop package to 0.19.0 with Cython 3 and #587 but I was able to reproduce the failure with cython 0.29.x as well.

The tests initially passed 24 days ago with libuv 1.47.0, but now they fail with libuv 1.48.0:

=================================== FAILURES ===================================
________________________ Test_UV_DNS.test_getaddrinfo_8 ________________________
Traceback (most recent call last):
  File "/builddir/build/BUILD/uvloop-0.19.0/_empty/tests/test_dns.py", line 33, in _test_getaddrinfo
    a1 = socket.getaddrinfo(*args, **kwargs)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/socket.py", line 963, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
socket.gaierror: [Errno -2] Name or service not known

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib64/python3.12/unittest/case.py", line 58, in testPartExecutor
    yield
  File "/usr/lib64/python3.12/unittest/case.py", line 634, in run
    self._callTestMethod(testMethod)
  File "/usr/lib64/python3.12/unittest/case.py", line 589, in _callTestMethod
    if method() is not None:
       ^^^^^^^^
  File "/builddir/build/BUILD/uvloop-0.19.0/_empty/tests/test_dns.py", line 107, in test_getaddrinfo_8
    self._test_getaddrinfo('', 0)
  File "/builddir/build/BUILD/uvloop-0.19.0/_empty/tests/test_dns.py", line 48, in _test_getaddrinfo
    raise ex
  File "/builddir/build/BUILD/uvloop-0.19.0/_empty/tests/test_dns.py", line 38, in _test_getaddrinfo
    a2 = self.loop.run_until_complete(
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "uvloop/loop.pyx", line 1516, in uvloop.loop.Loop.run_until_complete
  File "uvloop/loop.pyx", line 1527, in getaddrinfo
OSError: [Errno 22] Invalid argument
________________________ Test_UV_DNS.test_getaddrinfo_9 ________________________
Traceback (most recent call last):
  File "/builddir/build/BUILD/uvloop-0.19.0/_empty/tests/test_dns.py", line 33, in _test_getaddrinfo
    a1 = socket.getaddrinfo(*args, **kwargs)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/socket.py", line 963, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
socket.gaierror: [Errno -2] Name or service not known

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib64/python3.12/unittest/case.py", line 58, in testPartExecutor
    yield
  File "/usr/lib64/python3.12/unittest/case.py", line 634, in run
    self._callTestMethod(testMethod)
  File "/usr/lib64/python3.12/unittest/case.py", line 589, in _callTestMethod
    if method() is not None:
       ^^^^^^^^
  File "/builddir/build/BUILD/uvloop-0.19.0/_empty/tests/test_dns.py", line 111, in test_getaddrinfo_9
    self._test_getaddrinfo(b'', 0)
  File "/builddir/build/BUILD/uvloop-0.19.0/_empty/tests/test_dns.py", line 48, in _test_getaddrinfo
    raise ex
  File "/builddir/build/BUILD/uvloop-0.19.0/_empty/tests/test_dns.py", line 38, in _test_getaddrinfo
    a2 = self.loop.run_until_complete(
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "uvloop/loop.pyx", line 1516, in uvloop.loop.Loop.run_until_complete
  File "uvloop/loop.pyx", line 1527, in getaddrinfo
OSError: [Errno 22] Invalid argument
=============================== warnings summary ===============================
_empty/tests/test_aiohttp.py::Test_UV_AioHTTP::test_aiohttp_graceful_shutdown
  /builddir/build/BUILD/uvloop-0.19.0/_empty/tests/test_aiohttp.py:81: NotAppKeyWarning: It is recommended to use web.AppKey instances for keys.
  https://docs.aiohttp.org/en/stable/web_advanced.html#application-s-config
    app['websockets'] = weakref.WeakSet()

_empty/tests/test_aiohttp.py::Test_UV_AioHTTP::test_aiohttp_graceful_shutdown
  /usr/lib64/python3.12/site-packages/aiohttp/web_runner.py:95: DeprecationWarning: shutdown_timeout should be set on BaseRunner
    super().__init__(

_empty/tests/test_executors.py::TestUVExecutors::test_executors_process_pool_01
_empty/tests/test_executors.py::TestAIOExecutors::test_executors_process_pool_01
_empty/tests/test_regr1.py::TestIssue39Regr::test_issue39_regression
  /usr/lib64/python3.12/multiprocessing/popen_fork.py:66: DeprecationWarning: This process (pid=800) is multi-threaded, use of fork() may lead to deadlocks in the child.
    self.pid = os.fork()

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_dns.py::Test_UV_DNS::test_getaddrinfo_8 - OSError: [Errno 2...
FAILED tests/test_dns.py::Test_UV_DNS::test_getaddrinfo_9 - OSError: [Errno 2...
====== 2 failed, 472 passed, 28 skipped, 5 warnings in 105.04s (0:01:45) =======

I've checked the tests and they call getaddrinfo('', 0). Our build runs offline.

socket exception:

>>> socket.getaddrinfo('', 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.12/socket.py", line 963, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
socket.gaierror: [Errno -2] Name or service not known

vanilla asyncio exception:

>>> async def f_asyncio():
...  loop = asyncio.get_running_loop()
...  return await loop.getaddrinfo('', 0)
... 
>>> asyncio.run(f_asyncio())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.12/asyncio/runners.py", line 194, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/asyncio/base_events.py", line 685, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "<stdin>", line 3, in f_asyncio
  File "/usr/lib64/python3.12/asyncio/base_events.py", line 899, in getaddrinfo
    return await self.run_in_executor(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/asyncio/base_events.py", line 882, in _getaddrinfo_debug
    addrinfo = socket.getaddrinfo(host, port, family, type, proto, flags)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/socket.py", line 963, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
socket.gaierror: [Errno -2] Name or service not known

uvloop exception:

>>> async def f_uvloop():
...  loop = uvloop.new_event_loop()
...  return await loop.getaddrinfo('', 0)
... 
>>> asyncio.run(f_uvloop())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.12/asyncio/runners.py", line 194, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/asyncio/base_events.py", line 685, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "<stdin>", line 3, in f_uvloop
  File "uvloop/loop.pyx", line 1527, in getaddrinfo
OSError: [Errno 22] Invalid argument

This is not isolated to using Cython 3 and #587

I've noticed libuv was upgraded in Fedora between the original successful build and the first time I saw the failure.

Downgrading libuv from 1.48.0 to 1.47.0 makes it pass.

Looks like libuv/libuv@3530bcc might cause this.

git bisect agrees

ancieg commented

Looks like libuv/libuv@3530bcc might cause this.

Looks like it is expected behavior for now, so I suggest simply skip these tests for libuv >= 1.48.0:

From 9373f66795862cb2879699ae3f83b09834697feb Mon Sep 17 00:00:00 2001
From: Anton Zhukharev <ancieg@altlinux.org>
Date: Fri, 12 Apr 2024 14:44:38 +0300
Subject: [PATCH] src: fix tests for libuv >= 1.48.0

---
 tests/test_dns.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tests/test_dns.py b/tests/test_dns.py
index f61b1e8..e304960 100644
--- a/tests/test_dns.py
+++ b/tests/test_dns.py
@@ -3,6 +3,7 @@ import socket
 import unittest

 from uvloop import _testbase as tb
+from uvloop.loop import libuv_get_version


 def patched_getaddrinfo(*args, **kwargs):
@@ -103,10 +104,12 @@ class BaseTestDNS:
         self._test_getaddrinfo(None, 0)
         self._test_getaddrinfo(None, 0, type=socket.SOCK_STREAM)

+    @unittest.skipIf(libuv_get_version() >= 0x013000, 'Must fail with libuv >= 1.48.0')
     def test_getaddrinfo_8(self):
         self._test_getaddrinfo('', 0)
         self._test_getaddrinfo('', 0, type=socket.SOCK_STREAM)

+    @unittest.skipIf(libuv_get_version() >= 0x013000, 'Must fail with libuv >= 1.48.0')
     def test_getaddrinfo_9(self):
         self._test_getaddrinfo(b'', 0)
         self._test_getaddrinfo(b'', 0, type=socket.SOCK_STREAM)
--
2.42.1