diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d0074b7b..cb46aaf3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,7 +41,7 @@ repos: - id: mypy additional_dependencies: - pytest - - trio >= 0.23 + - trio >= 0.26 - packaging - repo: https://github.com/pre-commit/pygrep-hooks diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index 37556166..0b4c8314 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -19,10 +19,15 @@ This library adheres to `Semantic Versioning 2.0 `_. ``anyio.open_process()``, ``asyncio.create_subprocess_…()``, ``trio.run_process()``, and ``subprocess.run()`` already accept (PR by @jmehnle) - Added the ``info`` property to ``anyio.Path`` on Python 3.14 +- Changed ``anyio.getaddrinfo()`` to ignore (invalid) IPv6 name resolution results when + IPv6 support is disabled in Python - Fixed traceback formatting growing quadratically with level of ``TaskGroup`` nesting on asyncio due to exception chaining when raising ``ExceptionGroups`` in ``TaskGroup.__aexit__`` (`#863 `_; PR by @tapetersen) +- Fixed ``anyio.Path.iterdir()`` making a blocking call in Python 3.13 + (`#873 `_; PR by @cbornet and + @agronholm) **4.8.0** diff --git a/src/anyio/_backends/_asyncio.py b/src/anyio/_backends/_asyncio.py index f1993307..1ef4ce18 100644 --- a/src/anyio/_backends/_asyncio.py +++ b/src/anyio/_backends/_asyncio.py @@ -2674,13 +2674,13 @@ async def getaddrinfo( type: int | SocketKind = 0, proto: int = 0, flags: int = 0, - ) -> list[ + ) -> Sequence[ tuple[ AddressFamily, SocketKind, int, str, - tuple[str, int] | tuple[str, int, int, int], + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], ] ]: return await get_running_loop().getaddrinfo( diff --git a/src/anyio/_backends/_trio.py b/src/anyio/_backends/_trio.py index 32ae8ace..b80cc04f 100644 --- a/src/anyio/_backends/_trio.py +++ b/src/anyio/_backends/_trio.py @@ -1246,13 +1246,13 @@ async def getaddrinfo( type: int | SocketKind = 0, proto: int = 0, flags: int = 0, - ) -> list[ + ) -> Sequence[ tuple[ AddressFamily, SocketKind, int, str, - tuple[str, int] | tuple[str, int, int, int], + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], ] ]: return await trio.socket.getaddrinfo(host, port, family, type, proto, flags) diff --git a/src/anyio/_core/_fileio.py b/src/anyio/_core/_fileio.py index 350a873a..a0d61984 100644 --- a/src/anyio/_core/_fileio.py +++ b/src/anyio/_core/_fileio.py @@ -544,9 +544,14 @@ async def is_socket(self) -> bool: async def is_symlink(self) -> bool: return await to_thread.run_sync(self._path.is_symlink, abandon_on_cancel=True) - def iterdir(self) -> AsyncIterator[Path]: - gen = self._path.iterdir() - return _PathIterator(gen) + async def iterdir(self) -> AsyncIterator[Path]: + gen = ( + self._path.iterdir() + if sys.version_info < (3, 13) + else await to_thread.run_sync(self._path.iterdir, abandon_on_cancel=True) + ) + async for path in _PathIterator(gen): + yield path def joinpath(self, *args: str | PathLike[str]) -> Path: return Path(self._path.joinpath(*args)) diff --git a/src/anyio/_core/_sockets.py b/src/anyio/_core/_sockets.py index a822d060..67b1da52 100644 --- a/src/anyio/_core/_sockets.py +++ b/src/anyio/_core/_sockets.py @@ -584,6 +584,8 @@ async def getaddrinfo( return [ (family, type, proto, canonname, convert_ipv6_sockaddr(sockaddr)) for family, type, proto, canonname, sockaddr in gai_res + # filter out IPv6 results when IPv6 is disabled + if not isinstance(sockaddr[0], int) ] diff --git a/src/anyio/abc/_eventloop.py b/src/anyio/abc/_eventloop.py index 2bfdf286..4cfce836 100644 --- a/src/anyio/abc/_eventloop.py +++ b/src/anyio/abc/_eventloop.py @@ -315,13 +315,13 @@ async def getaddrinfo( type: int | SocketKind = 0, proto: int = 0, flags: int = 0, - ) -> list[ + ) -> Sequence[ tuple[ AddressFamily, SocketKind, int, str, - tuple[str, int] | tuple[str, int, int, int], + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], ] ]: pass diff --git a/tests/test_sockets.py b/tests/test_sockets.py index d6063875..9b420af9 100644 --- a/tests/test_sockets.py +++ b/tests/test_sockets.py @@ -18,6 +18,7 @@ from ssl import SSLContext, SSLError from threading import Thread from typing import TYPE_CHECKING, Any, Literal, NoReturn, TypeVar, cast +from unittest import mock import psutil import pytest @@ -52,6 +53,7 @@ wait_socket_writable, wait_writable, ) +from anyio._core._eventloop import get_async_backend from anyio.abc import ( IPSockAddrType, Listener, @@ -1846,6 +1848,14 @@ async def test_getaddrinfo_ipv6addr( ] +async def test_getaddrinfo_ipv6_disabled() -> None: + gai_result = [ + (AddressFamily.AF_INET6, socket.SocketKind.SOCK_STREAM, 6, "", (1, b"")) + ] + with mock.patch.object(get_async_backend(), "getaddrinfo", return_value=gai_result): + assert await getaddrinfo("::1", 0) == [] + + async def test_getnameinfo() -> None: expected_result = socket.getnameinfo(("127.0.0.1", 6666), 0) result = await getnameinfo(("127.0.0.1", 6666))