diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9a467a1d..c67083625 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -362,7 +362,7 @@ jobs: - build runs-on: ubuntu-latest - container: alpine + container: alpine:3 steps: - name: Install necessary packages # can't use setup-python because that python doesn't seem to work; diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5caeecb07..007964cb6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,11 +20,11 @@ repos: - id: sort-simple-yaml files: .pre-commit-config.yaml - repo: https://github.com/psf/black-pre-commit-mirror - rev: 26.3.1 + rev: 26.5.0 hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.12 + rev: v0.15.13 hooks: - id: ruff-check types: [file] @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.46.1 + rev: v1.46.2 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint @@ -46,7 +46,7 @@ repos: hooks: - id: sphinx-lint - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.24.1 + rev: v1.25.2 hooks: - id: zizmor - repo: local @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.11.13 + rev: 0.11.14 hooks: # Compile requirements - id: pip-compile diff --git a/docs-requirements.txt b/docs-requirements.txt index 8d4987537..5964c6204 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -18,13 +18,13 @@ cffi==2.0.0 ; os_name == 'nt' or platform_python_implementation != 'PyPy' # cryptography charset-normalizer==3.4.7 # via requests -click==8.3.3 +click==8.4.0 # via towncrier colorama==0.4.6 ; sys_platform == 'win32' # via # click # sphinx -cryptography==47.0.0 +cryptography==48.0.0 # via pyopenssl docutils==0.22.4 # via @@ -32,7 +32,7 @@ docutils==0.22.4 # sphinx-rtd-theme exceptiongroup==1.3.1 # via -r docs-requirements.in -idna==3.13 +idna==3.15 # via # -r docs-requirements.in # requests @@ -55,9 +55,9 @@ pycparser==3.0 ; (implementation_name != 'PyPy' and os_name == 'nt') or (impleme # via cffi pygments==2.20.0 # via sphinx -pyopenssl==26.1.0 +pyopenssl==26.2.0 # via -r docs-requirements.in -requests==2.33.1 +requests==2.34.2 # via sphinx roman-numerals==4.1.0 # via sphinx @@ -112,5 +112,5 @@ typing-extensions==4.15.0 # beautifulsoup4 # exceptiongroup # pyopenssl -urllib3==2.6.3 +urllib3==2.7.0 # via requests diff --git a/src/trio/_highlevel_generic.py b/src/trio/_highlevel_generic.py index b2f872338..a4a1024da 100644 --- a/src/trio/_highlevel_generic.py +++ b/src/trio/_highlevel_generic.py @@ -113,7 +113,7 @@ async def send_eof(self) -> None: return await stream.aclose() # we intentionally accept more types from the caller than we support returning - async def receive_some(self, max_bytes: int | None = None) -> bytes: + async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray: """Calls ``self.receive_stream.receive_some``.""" return await self.receive_stream.receive_some(max_bytes) diff --git a/src/trio/_tests/test_dtls.py b/src/trio/_tests/test_dtls.py index 6363be2e9..fd51dbc67 100644 --- a/src/trio/_tests/test_dtls.py +++ b/src/trio/_tests/test_dtls.py @@ -486,7 +486,7 @@ async def test_server_socket_doesnt_crash_on_garbage( async with dtls_echo_server(server_ctx=server_ctx) as (_, address): with trio.socket.socket(type=trio.socket.SOCK_DGRAM) as sock: - for bad_packet in [ + bad_packets: list[bytes | bytearray | memoryview[int]] = [ b"", b"xyz", client_hello_extended, @@ -497,7 +497,9 @@ async def test_server_socket_doesnt_crash_on_garbage( client_hello_trailing_data_in_record, handshake_empty, client_hello_truncated_in_cookie, - ]: + ] + + for bad_packet in bad_packets: await sock.sendto(bad_packet, address) await trio.sleep(1) @@ -532,7 +534,7 @@ def route_packet(packet: UDPPacket) -> None: offset = len(payload) - 1 cscope.cancel() payload[offset] ^= 0x01 - packet = attrs.evolve(packet, payload=payload) + packet = attrs.evolve(packet, payload=bytes(payload)) fn.deliver_packet(packet) diff --git a/src/trio/_tests/test_exports.py b/src/trio/_tests/test_exports.py index 0e978b4dc..17ca27aa5 100644 --- a/src/trio/_tests/test_exports.py +++ b/src/trio/_tests/test_exports.py @@ -417,18 +417,7 @@ def lookup_symbol(symbol: str) -> dict[str, Any]: # type: ignore[misc, explicit # using .remove() instead of .delete() to get an error in case they start not # being missing - if ( - tool == "jedi" - and BaseException in class_.__mro__ - and sys.version_info >= (3, 11) - ): - missing.remove("add_note") - - if ( - tool == "mypy" - and BaseException in class_.__mro__ - and sys.version_info >= (3, 11) - ): + if BaseException in class_.__mro__ and sys.version_info >= (3, 11): extra.remove("__notes__") if tool == "mypy" and attrs.has(class_): @@ -443,8 +432,7 @@ def lookup_symbol(symbol: str) -> dict[str, Any]: # type: ignore[misc, explicit # dir does not see `__signature__` on enums until 3.14 if ( - tool == "mypy" - and enum.Enum in class_.__mro__ + enum.Enum in class_.__mro__ and sys.version_info >= (3, 12) and sys.version_info < (3, 14) ): @@ -508,16 +496,8 @@ def lookup_symbol(symbol: str) -> dict[str, Any]: # type: ignore[misc, explicit if tool == "jedi" and sys.platform == "win32": extra -= {"owner", "is_mount", "group"} - # not sure why jedi in particular ignores this (static?) method in 3.13 - if ( - tool == "jedi" - and sys.version_info[:2] == (3, 13) - and class_ in (trio.Path, trio.WindowsPath, trio.PosixPath) - ): - missing.remove("with_segments") - # tuple subclasses are weird - if issubclass(class_, tuple): + if tool == "mypy" and issubclass(class_, tuple): extra.remove("__reversed__") missing.remove("__getnewargs__") @@ -530,6 +510,22 @@ def lookup_symbol(symbol: str) -> dict[str, Any]: # type: ignore[misc, explicit missing.discard("__annotate_func__") missing.discard("__annotations_cache__") + if tool == "jedi" and class_ == trio.open_memory_channel: + # something is seriously wrong with jedi's understanding of open_memory_channel... + for attrib in ( + "__add__", + "__contains__", + "__getitem__", + "__getnewargs__", + "__iter__", + "__len__", + "__mul__", + "__rmul__", + "count", + "index", + ): + missing.remove(attrib) + if missing or extra: # pragma: no cover errors[f"{module_name}.{class_name}"] = { "missing": missing, diff --git a/src/trio/_tests/test_ssl.py b/src/trio/_tests/test_ssl.py index 085ce8f1d..8eb065927 100644 --- a/src/trio/_tests/test_ssl.py +++ b/src/trio/_tests/test_ssl.py @@ -252,17 +252,18 @@ async def wait_send_all_might_not_block(self) -> None: await _core.checkpoint() await self.sleeper("wait_send_all_might_not_block") - async def send_all(self, data: bytes) -> None: + async def send_all(self, data: bytes | bytearray | memoryview[int]) -> None: print(" --> transport_stream.send_all") + data_ = bytes(data) with self._send_all_conflict_detector: await _core.checkpoint() await _core.checkpoint() await self.sleeper("send_all") - self._conn.bio_write(data) + self._conn.bio_write(data_) while True: await self.sleeper("send_all") try: - data = self._conn.recv(1) + data_ = self._conn.recv(1) except SSL.ZeroReturnError: self._conn.shutdown() print("renegotiations:", self._conn.total_renegotiations()) @@ -270,7 +271,7 @@ async def send_all(self, data: bytes) -> None: except SSL.WantReadError: break else: - self._pending_cleartext += data + self._pending_cleartext += data_ self._lot.unpark_all() await self.sleeper("send_all") print(" <-- transport_stream.send_all finished") diff --git a/src/trio/_tests/test_testing.py b/src/trio/_tests/test_testing.py index ca67b8bef..7093a6187 100644 --- a/src/trio/_tests/test_testing.py +++ b/src/trio/_tests/test_testing.py @@ -416,7 +416,7 @@ def close_hook() -> None: async def test_MemoryReceiveStream() -> None: mrs = MemoryReceiveStream() - async def do_receive_some(max_bytes: int | None) -> bytes: + async def do_receive_some(max_bytes: int | None) -> bytes | bytearray: with assert_checkpoints(): return await mrs.receive_some(max_bytes) diff --git a/src/trio/_unix_pipes.py b/src/trio/_unix_pipes.py index 9bfd5dc18..598feb61e 100644 --- a/src/trio/_unix_pipes.py +++ b/src/trio/_unix_pipes.py @@ -127,7 +127,7 @@ def __init__(self, fd: int) -> None: "another task is using this stream for receive", ) - async def send_all(self, data: bytes) -> None: + async def send_all(self, data: bytes | bytearray | memoryview) -> None: with self._send_conflict_detector: # have to check up front, because send_all(b"") on a closed pipe # should raise diff --git a/src/trio/_windows_pipes.py b/src/trio/_windows_pipes.py index e1eea1e72..67537fc4e 100644 --- a/src/trio/_windows_pipes.py +++ b/src/trio/_windows_pipes.py @@ -52,7 +52,7 @@ def __init__(self, handle: int) -> None: "another task is currently using this pipe", ) - async def send_all(self, data: bytes) -> None: + async def send_all(self, data: bytes | bytearray | memoryview[int]) -> None: with self._conflict_detector: if self._handle_holder.closed: raise _core.ClosedResourceError("this pipe is already closed") @@ -96,7 +96,7 @@ def __init__(self, handle: int) -> None: "another task is currently using this pipe", ) - async def receive_some(self, max_bytes: int | None = None) -> bytes: + async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray: with self._conflict_detector: if self._handle_holder.closed: raise _core.ClosedResourceError("this pipe is already closed") diff --git a/test-requirements.txt b/test-requirements.txt index 8a61e2c12..6071e0f4d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -2,6 +2,8 @@ # uv pip compile --universal --python-version=3.10 test-requirements.in -o test-requirements.txt alabaster==1.0.0 # via sphinx +ast-serialize==0.5.0 ; implementation_name == 'cpython' + # via mypy astor==0.8.1 # via -r test-requirements.in astroid==4.0.4 @@ -14,7 +16,7 @@ attrs==26.1.0 # outcome babel==2.18.0 # via sphinx -black==26.3.1 ; implementation_name == 'cpython' +black==26.5.0 ; implementation_name == 'cpython' # via -r test-requirements.in certifi==2026.4.22 # via requests @@ -26,7 +28,7 @@ cfgv==3.5.0 # via pre-commit charset-normalizer==3.4.7 # via requests -click==8.3.3 ; implementation_name == 'cpython' +click==8.4.0 ; implementation_name == 'cpython' # via black codespell==2.4.2 # via -r test-requirements.in @@ -36,9 +38,9 @@ colorama==0.4.6 ; sys_platform == 'win32' # pylint # pytest # sphinx -coverage==7.13.5 +coverage==7.14.0 # via -r test-requirements.in -cryptography==47.0.0 +cryptography==48.0.0 # via # -r test-requirements.in # pyopenssl @@ -62,7 +64,7 @@ filelock==3.29.0 # virtualenv identify==2.6.19 # via pre-commit -idna==3.13 +idna==3.15 # via # -r test-requirements.in # requests @@ -73,17 +75,17 @@ iniconfig==2.3.0 # via pytest isort==8.0.1 # via pylint -jedi==0.19.2 ; implementation_name == 'cpython' +jedi==0.20.0 ; implementation_name == 'cpython' # via -r test-requirements.in jinja2==3.1.6 # via sphinx -librt==0.9.0 ; implementation_name == 'cpython' and platform_python_implementation != 'PyPy' +librt==0.11.0 ; implementation_name == 'cpython' and platform_python_implementation != 'PyPy' # via mypy markupsafe==3.0.3 # via jinja2 mccabe==0.7.0 # via pylint -mypy==1.20.2 ; implementation_name == 'cpython' +mypy==2.1.0 ; implementation_name == 'cpython' # via -r test-requirements.in mypy-extensions==1.1.0 # via @@ -101,7 +103,7 @@ packaging==26.2 # black # pytest # sphinx -parso==0.8.6 ; implementation_name == 'cpython' +parso==0.8.7 ; implementation_name == 'cpython' # via jedi pathspec==1.1.1 ; implementation_name == 'cpython' # via @@ -125,23 +127,23 @@ pygments==2.20.0 # sphinx pylint==4.0.5 # via -r test-requirements.in -pyopenssl==26.1.0 +pyopenssl==26.2.0 # via -r test-requirements.in pyright==1.1.409 # via -r test-requirements.in pytest==9.0.3 # via -r test-requirements.in -python-discovery==1.2.2 +python-discovery==1.3.1 # via virtualenv pytokens==0.4.1 ; implementation_name == 'cpython' # via black pyyaml==6.0.3 # via pre-commit -requests==2.33.1 +requests==2.34.2 # via sphinx roman-numerals==4.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.15.12 +ruff==0.15.13 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -174,21 +176,21 @@ tomli==2.4.1 ; python_full_version < '3.11' # pylint # pytest # sphinx -tomlkit==0.14.0 +tomlkit==0.15.0 # via pylint trustme==1.2.1 # via -r test-requirements.in -types-cffi==2.0.0.20260429 +types-cffi==2.0.0.20260508 # via # -r test-requirements.in # types-pyopenssl -types-docutils==0.22.3.20260408 +types-docutils==0.22.3.20260508 # via -r test-requirements.in types-pyopenssl==24.1.0.20240722 # via -r test-requirements.in -types-pyyaml==6.0.12.20260408 +types-pyyaml==6.0.12.20260510 # via -r test-requirements.in -types-setuptools==82.0.0.20260408 +types-setuptools==82.0.0.20260508 # via types-cffi typing-extensions==4.15.0 # via @@ -201,9 +203,9 @@ typing-extensions==4.15.0 # pyopenssl # pyright # virtualenv -urllib3==2.6.3 +urllib3==2.7.0 # via requests -uv==0.11.13 +uv==0.11.14 # via -r test-requirements.in -virtualenv==21.3.0 +virtualenv==21.3.3 # via pre-commit