diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 61cf881f..345362fd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,7 @@ jobs: test: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] os: [ubuntu-latest, macos-latest] diff --git a/README.rst b/README.rst index fe6c000d..c279822e 100644 --- a/README.rst +++ b/README.rst @@ -109,7 +109,7 @@ To build uvloop, you'll need Python 3.8 or greater: .. code:: - $ python3.7 -m venv uvloop-dev + $ python3 -m venv uvloop-dev $ source uvloop-dev/bin/activate 3. Install development dependencies: diff --git a/setup.py b/setup.py index 8fdf0e56..e6aa5201 100644 --- a/setup.py +++ b/setup.py @@ -178,7 +178,11 @@ def build_libuv(self): cmd, cwd=LIBUV_BUILD_DIR, env=env, check=True) - j_flag = '-j{}'.format(os.cpu_count() or 1) + try: + njobs = len(os.sched_getaffinity(0)) + except AttributeError: + njobs = os.cpu_count() + j_flag = '-j{}'.format(njobs or 1) c_flag = "CFLAGS={}".format(env['CFLAGS']) subprocess.run( ['make', j_flag, c_flag], diff --git a/tests/test_dns.py b/tests/test_dns.py index f61b1e8a..66da026b 100644 --- a/tests/test_dns.py +++ b/tests/test_dns.py @@ -31,13 +31,13 @@ def _test_getaddrinfo(self, *args, _patch=False, **kwargs): a1 = patched_getaddrinfo(*args, **kwargs) else: a1 = socket.getaddrinfo(*args, **kwargs) - except socket.gaierror as ex: + except (socket.gaierror, UnicodeError) as ex: err = ex try: a2 = self.loop.run_until_complete( self.loop.getaddrinfo(*args, **kwargs)) - except socket.gaierror as ex: + except (socket.gaierror, UnicodeError) as ex: if err is not None: self.assertEqual(ex.args, err.args) else: @@ -187,6 +187,18 @@ def test_getaddrinfo_20(self): self._test_getaddrinfo('127.0.0.1', 80, type=socket.SOCK_STREAM, flags=socket.AI_CANONNAME, _patch=patch) + # https://github.com/libuv/libuv/security/advisories/GHSA-f74f-cvh7-c6q6 + # See also: https://github.com/MagicStack/uvloop/pull/600 + def test_getaddrinfo_21(self): + payload = f'0x{"0" * 246}7f000001.example.com'.encode('ascii') + self._test_getaddrinfo(payload, 80) + self._test_getaddrinfo(payload, 80, type=socket.SOCK_STREAM) + + def test_getaddrinfo_22(self): + payload = f'0x{"0" * 246}7f000001.example.com' + self._test_getaddrinfo(payload, 80) + self._test_getaddrinfo(payload, 80, type=socket.SOCK_STREAM) + ###### def test_getnameinfo_1(self): diff --git a/tests/test_tcp.py b/tests/test_tcp.py index 213e2d9f..f269406e 100644 --- a/tests/test_tcp.py +++ b/tests/test_tcp.py @@ -248,8 +248,9 @@ def test_create_server_4(self): addr = sock.getsockname() with self.assertRaisesRegex(OSError, - r"error while attempting.*\('127.*: " - r"address( already)? in use"): + r"error while attempting.*\('127.*:" + r"( \[errno \d+\])? address" + r"( already)? in use"): self.loop.run_until_complete( self.loop.create_server(object, *addr)) diff --git a/uvloop/_version.py b/uvloop/_version.py index 0667c6a0..3c67960c 100644 --- a/uvloop/_version.py +++ b/uvloop/_version.py @@ -10,4 +10,4 @@ # supported platforms, publish the packages on PyPI, merge the PR # to the target branch, create a Git tag pointing to the commit. -__version__ = '0.19.0' +__version__ = '0.20.0' diff --git a/uvloop/dns.pyx b/uvloop/dns.pyx index 09b92828..3ba81fa0 100644 --- a/uvloop/dns.pyx +++ b/uvloop/dns.pyx @@ -348,6 +348,11 @@ cdef class AddrInfoRequest(UVRequest): if host is None: chost = NULL + elif host == b'' and sys.platform == 'darwin': + # It seems `getaddrinfo("", ...)` on macOS is equivalent to + # `getaddrinfo("localhost", ...)`. This is inconsistent with + # libuv 1.48 which treats empty nodename as EINVAL. + chost = 'localhost' else: chost = host @@ -356,13 +361,6 @@ cdef class AddrInfoRequest(UVRequest): else: cport = port - if cport is NULL and chost is NULL: - self.on_done() - msg = system.gai_strerror(socket_EAI_NONAME).decode('utf-8') - ex = socket_gaierror(socket_EAI_NONAME, msg) - callback(ex) - return - memset(&self.hints, 0, sizeof(system.addrinfo)) self.hints.ai_flags = flags self.hints.ai_family = family @@ -382,7 +380,17 @@ cdef class AddrInfoRequest(UVRequest): if err < 0: self.on_done() - callback(convert_error(err)) + try: + if err == uv.UV_EINVAL: + # Convert UV_EINVAL to EAI_NONAME to match libc behavior + msg = system.gai_strerror(socket_EAI_NONAME).decode('utf-8') + ex = socket_gaierror(socket_EAI_NONAME, msg) + else: + ex = convert_error(err) + except Exception as ex: + callback(ex) + else: + callback(ex) cdef class NameInfoRequest(UVRequest): diff --git a/uvloop/includes/compat.h b/uvloop/includes/compat.h index 7ae39e73..0c408c9e 100644 --- a/uvloop/includes/compat.h +++ b/uvloop/includes/compat.h @@ -1,5 +1,6 @@ #include #include +#include #include #include #include "Python.h" @@ -83,3 +84,22 @@ int Context_Exit(PyObject *ctx) { } #endif + +/* inlined from cpython/Modules/signalmodule.c + * https://github.com/python/cpython/blob/v3.13.0a6/Modules/signalmodule.c#L1931-L1951 + * private _Py_RestoreSignals has been moved to CPython internals in Python 3.13 + * https://github.com/python/cpython/pull/106400 */ + +void +_Py_RestoreSignals(void) +{ +#ifdef SIGPIPE + PyOS_setsig(SIGPIPE, SIG_DFL); +#endif +#ifdef SIGXFZ + PyOS_setsig(SIGXFZ, SIG_DFL); +#endif +#ifdef SIGXFSZ + PyOS_setsig(SIGXFSZ, SIG_DFL); +#endif +} diff --git a/uvloop/includes/python.pxd b/uvloop/includes/python.pxd index 454d5c77..94007e53 100644 --- a/uvloop/includes/python.pxd +++ b/uvloop/includes/python.pxd @@ -11,8 +11,6 @@ cdef extern from "Python.h": object PyUnicode_EncodeFSDefault(object) void PyErr_SetInterrupt() nogil - void _Py_RestoreSignals() - object PyMemoryView_FromMemory(char *mem, ssize_t size, int flags) object PyMemoryView_FromObject(object obj) int PyMemoryView_Check(object obj) @@ -29,3 +27,5 @@ cdef extern from "includes/compat.h": void PyOS_BeforeFork() void PyOS_AfterFork_Parent() void PyOS_AfterFork_Child() + + void _Py_RestoreSignals() diff --git a/uvloop/includes/uv.pxd b/uvloop/includes/uv.pxd index ddd9738c..510b1498 100644 --- a/uvloop/includes/uv.pxd +++ b/uvloop/includes/uv.pxd @@ -57,7 +57,7 @@ cdef extern from "uv.h" nogil: cdef int SOL_SOCKET cdef int SO_ERROR cdef int SO_REUSEADDR - cdef int SO_REUSEPORT + # use has_SO_REUSEPORT and SO_REUSEPORT in stdlib.pxi instead cdef int AF_INET cdef int AF_INET6 cdef int AF_UNIX diff --git a/uvloop/loop.pyx b/uvloop/loop.pyx index a3eeb605..00a635ff 100644 --- a/uvloop/loop.pyx +++ b/uvloop/loop.pyx @@ -1774,7 +1774,7 @@ cdef class Loop: if reuse_address: sock.setsockopt(uv.SOL_SOCKET, uv.SO_REUSEADDR, 1) if reuse_port: - sock.setsockopt(uv.SOL_SOCKET, uv.SO_REUSEPORT, 1) + sock.setsockopt(uv.SOL_SOCKET, SO_REUSEPORT, 1) # Disable IPv4/IPv6 dual stack support (enabled by # default on Linux) which makes a single socket # listen on both address families. diff --git a/vendor/libuv b/vendor/libuv index f0bb7e40..e9f29cb9 160000 --- a/vendor/libuv +++ b/vendor/libuv @@ -1 +1 @@ -Subproject commit f0bb7e40f0508bedf6fad33769b3f87bb8aedfa6 +Subproject commit e9f29cb984231524e3931aa0ae2c5dae1a32884e