diff --git a/.github/workflows/autodeps.yml b/.github/workflows/autodeps.yml index 0e28de2687..1182d38782 100644 --- a/.github/workflows/autodeps.yml +++ b/.github/workflows/autodeps.yml @@ -24,13 +24,13 @@ jobs: - name: Setup python uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: "3.9" - name: Bump dependencies run: | python -m pip install -U pip pre-commit python -m pip install -r test-requirements.txt - uv pip compile --universal --python-version=3.8 --upgrade test-requirements.in -o test-requirements.txt + uv pip compile --universal --python-version=3.9 --upgrade test-requirements.in -o test-requirements.txt uv pip compile --universal --python-version=3.11 --upgrade docs-requirements.in -o docs-requirements.txt pre-commit autoupdate --jobs 0 @@ -43,7 +43,7 @@ jobs: - name: uv run: | - uv pip compile --universal --python-version=3.8 test-requirements.in -o test-requirements.txt + uv pip compile --universal --python-version=3.9 test-requirements.in -o test-requirements.txt uv pip compile --universal --python-version=3.11 docs-requirements.in -o docs-requirements.txt - name: Commit changes and create automerge PR diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 074943ec63..10965e132b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,23 +18,23 @@ jobs: strategy: fail-fast: false matrix: - python: ['pypy-3.10', '3.8', '3.9', '3.10', '3.11', '3.12'] + python: ['pypy-3.10', '3.9', '3.10', '3.11', '3.12', '3.13'] arch: ['x86', 'x64'] lsp: [''] lsp_extract_file: [''] extra_name: [''] include: - - python: '3.8' + - python: '3.9' arch: 'x64' lsp: 'https://raw.githubusercontent.com/python-trio/trio-ci-assets/master/komodia-based-vpn-setup.zip' lsp_extract_file: 'komodia-based-vpn-setup.exe' extra_name: ', with Komodia LSP' - - python: '3.8' + - python: '3.9' arch: 'x64' lsp: 'https://www.proxifier.com/download/legacy/ProxifierSetup342.exe' lsp_extract_file: '' extra_name: ', with IFS LSP' - #- python: '3.8' + #- python: '3.9' # arch: 'x64' # lsp: 'http://download.pctools.com/mirror/updates/9.0.0.2308-SDavfree-lite_en.exe' # lsp_extract_file: '' @@ -87,16 +87,16 @@ jobs: strategy: fail-fast: false matrix: - python: ['pypy-3.10', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] + python: ['pypy-3.10', '3.9', '3.10', '3.11', '3.12', '3.13'] check_formatting: ['0'] no_test_requirements: ['0'] extra_name: [''] include: - - python: '3.12' + - python: '3.13' check_formatting: '1' extra_name: ', check formatting' # separate test run that doesn't install test-requirements.txt - - python: '3.8' + - python: '3.9' no_test_requirements: '1' extra_name: ', no test-requirements' continue-on-error: >- @@ -143,7 +143,7 @@ jobs: strategy: fail-fast: false matrix: - python: ['pypy-3.10', '3.8', '3.9', '3.10', '3.11', '3.12'] + python: ['pypy-3.10', '3.9', '3.10', '3.11', '3.12', '3.13'] continue-on-error: >- ${{ ( @@ -203,7 +203,15 @@ jobs: strategy: fail-fast: false matrix: - python: ['3.8', '3.12'] + include: + - python: '3.9' # We support running on cython 2 and 3 for 3.9 + cython: '<3' # cython 2 + - python: '3.9' + cython: '>=3' # cython 3 (or greater) + - python: '3.11' # 3.11 is the last version Cy2 supports + cython: '<3' # cython 2 + - python: '3.13' # We support running cython3 on 3.13 + cython: '>=3' # cython 3 (or greater) steps: - name: Checkout uses: actions/checkout@v4 @@ -216,18 +224,11 @@ jobs: - name: install trio and setuptools run: python -m pip install --upgrade pip . setuptools - - name: install cython<3 - run: python -m pip install "cython<3" - - name: compile pyx file - run: cythonize -i tests/cython/test_cython.pyx - - name: import & run module - run: python -c 'import tests.cython.test_cython' + - name: install cython & compile pyx file + run: | + python -m pip install "cython${{ matrix.cython }}" + cythonize --inplace tests/cython/test_cython.pyx - - name: install cython>=3 - run: python -m pip install "cython>=3" - - name: compile pyx file - # different cython version should trigger a re-compile, but --force just in case - run: cythonize --inplace --force tests/cython/test_cython.pyx - name: import & run module run: python -c 'import tests.cython.test_cython' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4997b1f675..a40da4c3af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: "3.9" - run: python -m pip install build - run: python -m build diff --git a/README.rst b/README.rst index 65f6df8946..e3620546a0 100644 --- a/README.rst +++ b/README.rst @@ -92,7 +92,7 @@ demonstration of implementing the "Happy Eyeballs" algorithm in an older library versus Trio. **Cool, but will it work on my system?** Probably! As long as you have -some kind of Python 3.8-or-better (CPython or `currently maintained versions of +some kind of Python 3.9-or-better (CPython or `currently maintained versions of PyPy3 `__ are both fine), and are using Linux, macOS, Windows, or FreeBSD, then Trio will work. Other environments might work too, but those diff --git a/check.sh b/check.sh index a4fb2e4e2a..b659675968 100755 --- a/check.sh +++ b/check.sh @@ -78,7 +78,7 @@ fi # Check pip compile is consistent echo "::group::Pip Compile - Tests" -uv pip compile --universal --python-version=3.8 test-requirements.in -o test-requirements.txt +uv pip compile --universal --python-version=3.9 test-requirements.in -o test-requirements.txt echo "::endgroup::" echo "::group::Pip Compile - Docs" uv pip compile --universal --python-version=3.11 docs-requirements.in -o docs-requirements.txt diff --git a/docs/source/index.rst b/docs/source/index.rst index 1caf5c043b..b89bc4533c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -45,7 +45,7 @@ Vital statistics: * Supported environments: We test on - - Python: 3.8+ (CPython and PyPy) + - Python: 3.9+ (CPython and PyPy) - Windows, macOS, Linux (glibc and musl), FreeBSD Other environments might also work; give it a try and see. diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index c7218d873b..3ae9bb4597 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -88,7 +88,7 @@ Okay, ready? Let's get started. Before you begin ---------------- -1. Make sure you're using Python 3.8 or newer. +1. Make sure you're using Python 3.9 or newer. 2. ``python3 -m pip install --upgrade trio`` (or on Windows, maybe ``py -3 -m pip install --upgrade trio`` – `details diff --git a/newsfragments/3106.removal.rst b/newsfragments/3106.removal.rst new file mode 100644 index 0000000000..ee023242d0 --- /dev/null +++ b/newsfragments/3106.removal.rst @@ -0,0 +1 @@ +Drop support for Python 3.8. (`#3104 `__) diff --git a/pyproject.toml b/pyproject.toml index f773d4c703..adf2ccbe9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,6 @@ classifiers = [ "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -35,7 +34,7 @@ classifiers = [ "Topic :: System :: Networking", "Typing :: Typed", ] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ # attrs 19.2.0 adds `eq` option to decorators # attrs 20.1.0 adds @frozen @@ -75,7 +74,6 @@ include-package-data = true version = {attr = "trio._version.__version__"} [tool.black] -target-version = ['py38'] force-exclude = ''' ( ^/docs/source/reference-.* @@ -170,7 +168,7 @@ combine-as-imports = true fixture-parentheses = false [tool.mypy] -python_version = "3.8" +python_version = "3.9" files = ["src/trio/", "docs/source/*.py"] # Be flexible about dependencies that don't have stubs yet (like pytest) @@ -195,7 +193,7 @@ disallow_untyped_defs = true check_untyped_defs = true [tool.pyright] -pythonVersion = "3.8" +pythonVersion = "3.9" reportUnnecessaryTypeIgnoreComment = true typeCheckingMode = "strict" diff --git a/src/trio/_channel.py b/src/trio/_channel.py index 41a7fc6824..cb7df95ff6 100644 --- a/src/trio/_channel.py +++ b/src/trio/_channel.py @@ -5,7 +5,6 @@ from typing import ( TYPE_CHECKING, Generic, - Tuple, # only needed for typechecking on <3.9 ) import attrs @@ -93,8 +92,7 @@ def _open_memory_channel( # it could replace the normal function header if TYPE_CHECKING: # written as a class so you can say open_memory_channel[int](5) - # Need to use Tuple instead of tuple due to CI check running on 3.8 - class open_memory_channel(Tuple["MemorySendChannel[T]", "MemoryReceiveChannel[T]"]): + class open_memory_channel(tuple["MemorySendChannel[T]", "MemoryReceiveChannel[T]"]): def __new__( # type: ignore[misc] # "must return a subtype" cls, max_buffer_size: int | float, # noqa: PYI041 diff --git a/src/trio/_core/_asyncgens.py b/src/trio/_core/_asyncgens.py index 77f1c7eced..21102ea7d8 100644 --- a/src/trio/_core/_asyncgens.py +++ b/src/trio/_core/_asyncgens.py @@ -17,10 +17,9 @@ if TYPE_CHECKING: from types import AsyncGeneratorType - from typing import Set _WEAK_ASYNC_GEN_SET = weakref.WeakSet[AsyncGeneratorType[object, NoReturn]] - _ASYNC_GEN_SET = Set[AsyncGeneratorType[object, NoReturn]] + _ASYNC_GEN_SET = set[AsyncGeneratorType[object, NoReturn]] else: _WEAK_ASYNC_GEN_SET = weakref.WeakSet _ASYNC_GEN_SET = set diff --git a/src/trio/_core/_entry_queue.py b/src/trio/_core/_entry_queue.py index 20a2b165a4..332441a3a0 100644 --- a/src/trio/_core/_entry_queue.py +++ b/src/trio/_core/_entry_queue.py @@ -2,7 +2,8 @@ import threading from collections import deque -from typing import TYPE_CHECKING, Callable, NoReturn, Tuple +from collections.abc import Callable +from typing import TYPE_CHECKING, NoReturn import attrs @@ -16,7 +17,7 @@ PosArgsT = TypeVarTuple("PosArgsT") Function = Callable[..., object] -Job = Tuple[Function, Tuple[object, ...]] +Job = tuple[Function, tuple[object, ...]] @attrs.define diff --git a/src/trio/_core/_generated_io_kqueue.py b/src/trio/_core/_generated_io_kqueue.py index eb230597b3..b2bdfc5763 100644 --- a/src/trio/_core/_generated_io_kqueue.py +++ b/src/trio/_core/_generated_io_kqueue.py @@ -4,13 +4,15 @@ from __future__ import annotations import sys -from typing import TYPE_CHECKING, Callable, ContextManager +from typing import TYPE_CHECKING from ._ki import LOCALS_KEY_KI_PROTECTION_ENABLED from ._run import GLOBAL_RUN_CONTEXT if TYPE_CHECKING: import select + from collections.abc import Callable + from contextlib import AbstractContextManager from .. import _core from .._file_io import _HasFileNo @@ -44,7 +46,7 @@ def current_kqueue() -> select.kqueue: def monitor_kevent( ident: int, filter: int, -) -> ContextManager[_core.UnboundedQueue[select.kevent]]: +) -> AbstractContextManager[_core.UnboundedQueue[select.kevent]]: """TODO: these are implemented, but are currently more of a sketch than anything real. See `#26 `__. diff --git a/src/trio/_core/_generated_io_windows.py b/src/trio/_core/_generated_io_windows.py index 1cad305a03..d06bb19e0e 100644 --- a/src/trio/_core/_generated_io_windows.py +++ b/src/trio/_core/_generated_io_windows.py @@ -4,12 +4,14 @@ from __future__ import annotations import sys -from typing import TYPE_CHECKING, ContextManager +from typing import TYPE_CHECKING from ._ki import LOCALS_KEY_KI_PROTECTION_ENABLED from ._run import GLOBAL_RUN_CONTEXT if TYPE_CHECKING: + from contextlib import AbstractContextManager + from typing_extensions import Buffer from .._file_io import _HasFileNo @@ -196,7 +198,9 @@ def current_iocp() -> int: raise RuntimeError("must be called from async context") from None -def monitor_completion_key() -> ContextManager[tuple[int, UnboundedQueue[object]]]: +def monitor_completion_key() -> ( + AbstractContextManager[tuple[int, UnboundedQueue[object]]] +): """TODO: these are implemented, but are currently more of a sketch than anything real. See `#26 `__ and `#52 diff --git a/src/trio/_core/_instrumentation.py b/src/trio/_core/_instrumentation.py index c1063b0e3e..905e81c37a 100644 --- a/src/trio/_core/_instrumentation.py +++ b/src/trio/_core/_instrumentation.py @@ -1,6 +1,9 @@ +from __future__ import annotations + import logging import types -from typing import Any, Callable, Dict, Sequence, TypeVar +from collections.abc import Callable, Sequence +from typing import Any, TypeVar from .._abc import Instrument @@ -17,7 +20,7 @@ def _public(fn: F) -> F: return fn -class Instruments(Dict[str, Dict[Instrument, None]]): +class Instruments(dict[str, dict[Instrument, None]]): """A collection of `trio.abc.Instrument` organized by hook. Instrumentation calls are rather expensive, and we don't want a diff --git a/src/trio/_core/_io_kqueue.py b/src/trio/_core/_io_kqueue.py index 13f95b22b2..6c440920d3 100644 --- a/src/trio/_core/_io_kqueue.py +++ b/src/trio/_core/_io_kqueue.py @@ -4,7 +4,7 @@ import select import sys from contextlib import contextmanager -from typing import TYPE_CHECKING, Callable, Iterator, Literal +from typing import TYPE_CHECKING, Literal import attrs import outcome @@ -14,6 +14,8 @@ from ._wakeup_socketpair import WakeupSocketpair if TYPE_CHECKING: + from collections.abc import Callable, Iterator + from typing_extensions import TypeAlias from .._core import Abort, RaiseCancelT, Task, UnboundedQueue diff --git a/src/trio/_core/_io_windows.py b/src/trio/_core/_io_windows.py index 0bc3b33378..80b62d4777 100644 --- a/src/trio/_core/_io_windows.py +++ b/src/trio/_core/_io_windows.py @@ -8,8 +8,6 @@ from typing import ( TYPE_CHECKING, Any, - Callable, - Iterator, Literal, TypeVar, cast, @@ -41,6 +39,8 @@ ) if TYPE_CHECKING: + from collections.abc import Callable, Iterator + from typing_extensions import Buffer, TypeAlias from .._file_io import _HasFileNo diff --git a/src/trio/_core/_tests/test_guest_mode.py b/src/trio/_core/_tests/test_guest_mode.py index 1a8b230e78..5beee4b22e 100644 --- a/src/trio/_core/_tests/test_guest_mode.py +++ b/src/trio/_core/_tests/test_guest_mode.py @@ -11,14 +11,12 @@ import time import traceback import warnings +from collections.abc import AsyncGenerator, Awaitable, Callable from functools import partial from math import inf from typing import ( TYPE_CHECKING, Any, - AsyncGenerator, - Awaitable, - Callable, NoReturn, TypeVar, ) diff --git a/src/trio/_core/_tests/test_instrumentation.py b/src/trio/_core/_tests/test_instrumentation.py index 3237571fef..ff29ab3acb 100644 --- a/src/trio/_core/_tests/test_instrumentation.py +++ b/src/trio/_core/_tests/test_instrumentation.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Container, Iterable, NoReturn +from typing import TYPE_CHECKING, NoReturn import attrs import pytest @@ -9,6 +9,8 @@ from .tutil import check_sequence_matches if TYPE_CHECKING: + from collections.abc import Container, Iterable + from ...lowlevel import Task diff --git a/src/trio/_core/_tests/test_io.py b/src/trio/_core/_tests/test_io.py index 54ffa3e0c2..48b99d387d 100644 --- a/src/trio/_core/_tests/test_io.py +++ b/src/trio/_core/_tests/test_io.py @@ -2,8 +2,9 @@ import random import socket as stdlib_socket +from collections.abc import Awaitable, Callable from contextlib import suppress -from typing import TYPE_CHECKING, Awaitable, Callable, Tuple, TypeVar +from typing import TYPE_CHECKING, TypeVar import pytest @@ -39,7 +40,7 @@ def drain_socket(sock: stdlib_socket.socket) -> None: WaitSocket = Callable[[stdlib_socket.socket], Awaitable[object]] -SocketPair = Tuple[stdlib_socket.socket, stdlib_socket.socket] +SocketPair = tuple[stdlib_socket.socket, stdlib_socket.socket] RetT = TypeVar("RetT") diff --git a/src/trio/_core/_tests/test_ki.py b/src/trio/_core/_tests/test_ki.py index e4241fc762..8582cc0b21 100644 --- a/src/trio/_core/_tests/test_ki.py +++ b/src/trio/_core/_tests/test_ki.py @@ -4,7 +4,7 @@ import inspect import signal import threading -from typing import TYPE_CHECKING, AsyncIterator, Callable, Iterator +from typing import TYPE_CHECKING import outcome import pytest @@ -23,6 +23,8 @@ from ...testing import wait_all_tasks_blocked if TYPE_CHECKING: + from collections.abc import AsyncIterator, Callable, Iterator + from ..._core import Abort, RaiseCancelT diff --git a/src/trio/_core/_tests/test_run.py b/src/trio/_core/_tests/test_run.py index c59ff3c2f3..d87bd21022 100644 --- a/src/trio/_core/_tests/test_run.py +++ b/src/trio/_core/_tests/test_run.py @@ -2505,9 +2505,13 @@ async def crasher() -> NoReturn: old_flags = gc.get_debug() try: + # fmt: off + # Remove after 3.9 unsupported, black formats in a way that breaks if + # you do `-X oldparser` with RaisesGroup( Matcher(ValueError, "^this is a crash$"), ), _core.CancelScope() as outer: + # fmt: on async with _core.open_nursery() as nursery: gc.collect() gc.set_debug(gc.DEBUG_SAVEALL) diff --git a/src/trio/_core/_tests/test_thread_cache.py b/src/trio/_core/_tests/test_thread_cache.py index ee301d17fd..1e3841ee0d 100644 --- a/src/trio/_core/_tests/test_thread_cache.py +++ b/src/trio/_core/_tests/test_thread_cache.py @@ -4,7 +4,7 @@ import time from contextlib import contextmanager from queue import Queue -from typing import TYPE_CHECKING, Iterator, NoReturn +from typing import TYPE_CHECKING, NoReturn import pytest @@ -13,6 +13,8 @@ from .tutil import gc_collect_harder, slow if TYPE_CHECKING: + from collections.abc import Iterator + from outcome import Outcome diff --git a/src/trio/_core/_tests/type_tests/nursery_start.py b/src/trio/_core/_tests/type_tests/nursery_start.py index b286b76ff2..4ce03b2721 100644 --- a/src/trio/_core/_tests/type_tests/nursery_start.py +++ b/src/trio/_core/_tests/type_tests/nursery_start.py @@ -1,9 +1,12 @@ """Test variadic generic typing for Nursery.start[_soon]().""" -from typing import Awaitable, Callable +from typing import TYPE_CHECKING from trio import TASK_STATUS_IGNORED, Nursery, TaskStatus +if TYPE_CHECKING: + from collections.abc import Awaitable, Callable + async def task_0() -> None: ... diff --git a/src/trio/_core/_tests/type_tests/run.py b/src/trio/_core/_tests/type_tests/run.py index 6d2124102b..5c51b91496 100644 --- a/src/trio/_core/_tests/type_tests/run.py +++ b/src/trio/_core/_tests/type_tests/run.py @@ -1,10 +1,13 @@ from __future__ import annotations -from typing import Sequence, overload +from typing import TYPE_CHECKING, overload import trio from typing_extensions import assert_type +if TYPE_CHECKING: + from collections.abc import Sequence + async def sleep_sort(values: Sequence[float]) -> list[float]: return [1] diff --git a/src/trio/_dtls.py b/src/trio/_dtls.py index 2dbbbc30d6..c13bbd4e55 100644 --- a/src/trio/_dtls.py +++ b/src/trio/_dtls.py @@ -20,11 +20,7 @@ from typing import ( TYPE_CHECKING, Any, - Awaitable, - Callable, Generic, - Iterable, - Iterator, TypeVar, Union, ) @@ -37,6 +33,7 @@ from ._util import NoPublicConstructor, final if TYPE_CHECKING: + from collections.abc import Awaitable, Callable, Iterable, Iterator from types import TracebackType # See DTLSEndpoint.__init__ for why this is imported here diff --git a/src/trio/_file_io.py b/src/trio/_file_io.py index 8038ff6234..76c17086dd 100644 --- a/src/trio/_file_io.py +++ b/src/trio/_file_io.py @@ -1,6 +1,7 @@ from __future__ import annotations import io +from collections.abc import Callable, Iterable from functools import partial from typing import ( IO, @@ -8,9 +9,7 @@ Any, AnyStr, BinaryIO, - Callable, Generic, - Iterable, TypeVar, Union, overload, diff --git a/src/trio/_highlevel_serve_listeners.py b/src/trio/_highlevel_serve_listeners.py index 71521546d3..0a85c8ecb0 100644 --- a/src/trio/_highlevel_serve_listeners.py +++ b/src/trio/_highlevel_serve_listeners.py @@ -3,7 +3,8 @@ import errno import logging import os -from typing import Any, Awaitable, Callable, NoReturn, TypeVar +from collections.abc import Awaitable, Callable +from typing import Any, NoReturn, TypeVar import trio diff --git a/src/trio/_path.py b/src/trio/_path.py index 3663831a23..2c9dfff292 100644 --- a/src/trio/_path.py +++ b/src/trio/_path.py @@ -222,8 +222,7 @@ def __repr__(self) -> str: group = _wrap_method(pathlib.Path.group) if sys.platform != "win32" or sys.version_info >= (3, 12): is_mount = _wrap_method(pathlib.Path.is_mount) - if sys.version_info >= (3, 9): - readlink = _wrap_method_path(pathlib.Path.readlink) + readlink = _wrap_method_path(pathlib.Path.readlink) rename = _wrap_method_path(pathlib.Path.rename) replace = _wrap_method_path(pathlib.Path.replace) resolve = _wrap_method_path(pathlib.Path.resolve) diff --git a/src/trio/_socket.py b/src/trio/_socket.py index 9a01009747..b137a4e028 100644 --- a/src/trio/_socket.py +++ b/src/trio/_socket.py @@ -12,8 +12,6 @@ from typing import ( TYPE_CHECKING, Any, - Awaitable, - Callable, Literal, SupportsIndex, TypeVar, @@ -29,7 +27,7 @@ from . import _core if TYPE_CHECKING: - from collections.abc import Iterable + from collections.abc import Awaitable, Callable, Iterable from types import TracebackType from typing_extensions import Buffer, Concatenate, ParamSpec, Self, TypeAlias diff --git a/src/trio/_subprocess.py b/src/trio/_subprocess.py index 5abefd0615..ff5cc8d393 100644 --- a/src/trio/_subprocess.py +++ b/src/trio/_subprocess.py @@ -32,15 +32,7 @@ # Sphinx cannot parse the stringified version -if sys.version_info >= (3, 9): - StrOrBytesPath: TypeAlias = Union[str, bytes, os.PathLike[str], os.PathLike[bytes]] -else: - StrOrBytesPath: TypeAlias = Union[ - str, - bytes, - "os.PathLike[str]", - "os.PathLike[bytes]", - ] +StrOrBytesPath: TypeAlias = Union[str, bytes, os.PathLike[str], os.PathLike[bytes]] # Linux-specific, but has complex lifetime management stuff so we hard-code it diff --git a/src/trio/_tests/check_type_completeness.py b/src/trio/_tests/check_type_completeness.py index 48e4df0bd9..ea53aa0e37 100755 --- a/src/trio/_tests/check_type_completeness.py +++ b/src/trio/_tests/check_type_completeness.py @@ -32,7 +32,7 @@ def run_pyright(platform: str) -> subprocess.CompletedProcess[bytes]: "pyright", # Specify a platform and version to keep imported modules consistent. f"--pythonplatform={platform}", - "--pythonversion=3.8", + "--pythonversion=3.9", "--verifytypes=trio", "--outputjson", "--ignoreexternal", diff --git a/src/trio/_tests/test_deprecate_strict_exception_groups_false.py b/src/trio/_tests/test_deprecate_strict_exception_groups_false.py index ccf34d72ed..7e575aa92e 100644 --- a/src/trio/_tests/test_deprecate_strict_exception_groups_false.py +++ b/src/trio/_tests/test_deprecate_strict_exception_groups_false.py @@ -1,4 +1,4 @@ -from typing import Awaitable, Callable +from collections.abc import Awaitable, Callable import pytest diff --git a/src/trio/_tests/test_highlevel_open_tcp_listeners.py b/src/trio/_tests/test_highlevel_open_tcp_listeners.py index c6410dbb0b..30596aa4fb 100644 --- a/src/trio/_tests/test_highlevel_open_tcp_listeners.py +++ b/src/trio/_tests/test_highlevel_open_tcp_listeners.py @@ -4,7 +4,7 @@ import socket as stdlib_socket import sys from socket import AddressFamily, SocketKind -from typing import TYPE_CHECKING, Any, Sequence, overload +from typing import TYPE_CHECKING, Any, overload import attrs import pytest @@ -26,6 +26,8 @@ from exceptiongroup import BaseExceptionGroup if TYPE_CHECKING: + from collections.abc import Sequence + from typing_extensions import Buffer diff --git a/src/trio/_tests/test_highlevel_open_tcp_stream.py b/src/trio/_tests/test_highlevel_open_tcp_stream.py index 066c8f5907..0032a551dc 100644 --- a/src/trio/_tests/test_highlevel_open_tcp_stream.py +++ b/src/trio/_tests/test_highlevel_open_tcp_stream.py @@ -3,7 +3,7 @@ import socket import sys from socket import AddressFamily, SocketKind -from typing import TYPE_CHECKING, Any, Sequence +from typing import TYPE_CHECKING, Any import attrs import pytest @@ -19,6 +19,8 @@ from trio.testing import Matcher, RaisesGroup if TYPE_CHECKING: + from collections.abc import Sequence + from trio.testing import MockClock if sys.version_info < (3, 11): diff --git a/src/trio/_tests/test_highlevel_serve_listeners.py b/src/trio/_tests/test_highlevel_serve_listeners.py index 6ecd42694f..0ce82e7846 100644 --- a/src/trio/_tests/test_highlevel_serve_listeners.py +++ b/src/trio/_tests/test_highlevel_serve_listeners.py @@ -2,7 +2,7 @@ import errno from functools import partial -from typing import TYPE_CHECKING, Awaitable, Callable, NoReturn +from typing import TYPE_CHECKING, NoReturn import attrs @@ -19,6 +19,8 @@ ) if TYPE_CHECKING: + from collections.abc import Awaitable, Callable + import pytest from trio._channel import MemoryReceiveChannel, MemorySendChannel diff --git a/src/trio/_tests/test_highlevel_socket.py b/src/trio/_tests/test_highlevel_socket.py index c1c99c573b..7e9d352450 100644 --- a/src/trio/_tests/test_highlevel_socket.py +++ b/src/trio/_tests/test_highlevel_socket.py @@ -3,7 +3,7 @@ import errno import socket as stdlib_socket import sys -from typing import Sequence +from typing import TYPE_CHECKING import pytest @@ -16,6 +16,9 @@ ) from .test_socket import setsockopt_tests +if TYPE_CHECKING: + from collections.abc import Sequence + async def test_SocketStream_basics() -> None: # stdlib socket bad (even if connected) diff --git a/src/trio/_tests/test_path.py b/src/trio/_tests/test_path.py index 7be8da1399..aa383e7255 100644 --- a/src/trio/_tests/test_path.py +++ b/src/trio/_tests/test_path.py @@ -2,7 +2,7 @@ import os import pathlib -from typing import TYPE_CHECKING, Type, Union +from typing import TYPE_CHECKING, Union import pytest @@ -51,8 +51,8 @@ def test_magic() -> None: assert bytes(path) == b"test" -EitherPathType = Union[Type[trio.Path], Type[pathlib.Path]] -PathOrStrType = Union[EitherPathType, Type[str]] +EitherPathType = Union[type[trio.Path], type[pathlib.Path]] +PathOrStrType = Union[EitherPathType, type[str]] cls_pairs: list[tuple[EitherPathType, EitherPathType]] = [ (trio.Path, pathlib.Path), (pathlib.Path, trio.Path), diff --git a/src/trio/_tests/test_repl.py b/src/trio/_tests/test_repl.py index 40f38a77d4..eba166d7d5 100644 --- a/src/trio/_tests/test_repl.py +++ b/src/trio/_tests/test_repl.py @@ -42,7 +42,7 @@ def test_build_raw_input() -> None: # In 3.10 or later, types.FunctionType (used internally) will automatically # attach __builtins__ to the function objects. However we need to explicitly -# include it for 3.8 & 3.9 +# include it for 3.9 support def build_locals() -> dict[str, object]: return {"__builtins__": __builtins__} diff --git a/src/trio/_tests/test_socket.py b/src/trio/_tests/test_socket.py index baef293d47..68040cebf8 100644 --- a/src/trio/_tests/test_socket.py +++ b/src/trio/_tests/test_socket.py @@ -8,7 +8,7 @@ import tempfile from pathlib import Path from socket import AddressFamily, SocketKind -from typing import TYPE_CHECKING, Any, Callable, List, Tuple, Union +from typing import TYPE_CHECKING, Any, Callable, Union import attrs import pytest @@ -23,14 +23,14 @@ from .._highlevel_socket import SocketStream - GaiTuple: TypeAlias = Tuple[ + GaiTuple: TypeAlias = tuple[ AddressFamily, SocketKind, int, str, - Union[Tuple[str, int], Tuple[str, int, int, int]], + Union[tuple[str, int], tuple[str, int, int, int]], ] - GetAddrInfoResponse: TypeAlias = List[GaiTuple] + GetAddrInfoResponse: TypeAlias = list[GaiTuple] else: GaiTuple: object GetAddrInfoResponse = object diff --git a/src/trio/_tests/test_ssl.py b/src/trio/_tests/test_ssl.py index 881bf321a6..9926abff0f 100644 --- a/src/trio/_tests/test_ssl.py +++ b/src/trio/_tests/test_ssl.py @@ -11,10 +11,6 @@ from typing import ( TYPE_CHECKING, Any, - AsyncIterator, - Awaitable, - Callable, - Iterator, NoReturn, ) @@ -56,6 +52,8 @@ ) if TYPE_CHECKING: + from collections.abc import AsyncIterator, Awaitable, Callable, Iterator + from typing_extensions import TypeAlias from trio._core import MockClock diff --git a/src/trio/_tests/test_subprocess.py b/src/trio/_tests/test_subprocess.py index d65a07614a..4a870ed7a5 100644 --- a/src/trio/_tests/test_subprocess.py +++ b/src/trio/_tests/test_subprocess.py @@ -6,16 +6,14 @@ import signal import subprocess import sys -from contextlib import asynccontextmanager +from collections.abc import AsyncIterator, Callable +from contextlib import AbstractAsyncContextManager, asynccontextmanager from functools import partial from pathlib import Path as SyncPath from signal import Signals from typing import ( TYPE_CHECKING, Any, - AsyncContextManager, - AsyncIterator, - Callable, NoReturn, ) @@ -115,7 +113,7 @@ async def run_process_in_nursery(*args: Any, **kwargs: Any) -> AsyncIterator[Pro ids=["open_process", "run_process in nursery"], ) -BackgroundProcessType: TypeAlias = Callable[..., AsyncContextManager[Process]] +BackgroundProcessType: TypeAlias = Callable[..., AbstractAsyncContextManager[Process]] @background_process_param diff --git a/src/trio/_tests/test_threads.py b/src/trio/_tests/test_threads.py index 7926d334ad..df9d2e74a3 100644 --- a/src/trio/_tests/test_threads.py +++ b/src/trio/_tests/test_threads.py @@ -10,13 +10,7 @@ from functools import partial from typing import ( TYPE_CHECKING, - AsyncGenerator, - Awaitable, - Callable, - List, NoReturn, - Tuple, - Type, TypeVar, Union, ) @@ -48,11 +42,13 @@ from ..testing import wait_all_tasks_blocked if TYPE_CHECKING: + from collections.abc import AsyncGenerator, Awaitable, Callable + from outcome import Outcome from ..lowlevel import Task -RecordType = List[Tuple[str, Union[threading.Thread, Type[BaseException]]]] +RecordType = list[tuple[str, Union[threading.Thread, type[BaseException]]]] T = TypeVar("T") diff --git a/src/trio/_tests/test_timeouts.py b/src/trio/_tests/test_timeouts.py index 574e1db1a4..bad439530c 100644 --- a/src/trio/_tests/test_timeouts.py +++ b/src/trio/_tests/test_timeouts.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import time -from typing import Awaitable, Callable, Protocol, TypeVar +from typing import TYPE_CHECKING, Protocol, TypeVar import outcome import pytest @@ -20,6 +22,9 @@ ) from ..testing import assert_checkpoints +if TYPE_CHECKING: + from collections.abc import Awaitable, Callable + T = TypeVar("T") @@ -132,10 +137,14 @@ async def task() -> None: @slow async def test_fail_after_fails_even_if_shielded() -> None: async def task() -> None: + # fmt: off + # Remove after 3.9 unsupported, black formats in a way that breaks if + # you do `-X oldparser` with pytest.raises(TooSlowError), _core.CancelScope() as outer, fail_after( TARGET, shield=True, ): + # fmt: on outer.cancel() # The outer scope is cancelled, but this task is protected by the # shield, so it manages to get to sleep until deadline is met diff --git a/src/trio/_tests/test_tracing.py b/src/trio/_tests/test_tracing.py index 5cf758c6b6..52ea9bfa40 100644 --- a/src/trio/_tests/test_tracing.py +++ b/src/trio/_tests/test_tracing.py @@ -1,7 +1,12 @@ -from typing import AsyncGenerator +from __future__ import annotations + +from typing import TYPE_CHECKING import trio +if TYPE_CHECKING: + from collections.abc import AsyncGenerator + async def coro1(event: trio.Event) -> None: event.set() diff --git a/src/trio/_tests/type_tests/path.py b/src/trio/_tests/type_tests/path.py index 5c7300fab5..6ea6717a6e 100644 --- a/src/trio/_tests/type_tests/path.py +++ b/src/trio/_tests/type_tests/path.py @@ -4,7 +4,7 @@ import os import pathlib import sys -from typing import IO, Any, BinaryIO, List, Tuple +from typing import IO, Any, BinaryIO import trio from trio._file_io import AsyncIOWrapper @@ -35,7 +35,7 @@ def operator_checks(text: str, tpath: trio.Path, ppath: pathlib.Path) -> None: def sync_attrs(path: trio.Path) -> None: - assert_type(path.parts, Tuple[str, ...]) + assert_type(path.parts, tuple[str, ...]) assert_type(path.drive, str) assert_type(path.root, str) assert_type(path.anchor, str) @@ -43,7 +43,7 @@ def sync_attrs(path: trio.Path) -> None: assert_type(path.parent, trio.Path) assert_type(path.name, str) assert_type(path.suffix, str) - assert_type(path.suffixes, List[str]) + assert_type(path.suffixes, list[str]) assert_type(path.stem, str) assert_type(path.as_posix(), str) assert_type(path.as_uri(), str) @@ -141,4 +141,4 @@ async def open_results(path: trio.Path, some_int: int, some_str: str) -> None: assert_type(await file_text.read(), str) assert_type(await file_text.write("test"), int) # TODO: report mypy bug: equiv to https://github.com/microsoft/pyright/issues/6833 - assert_type(await file_text.readlines(), List[str]) + assert_type(await file_text.readlines(), list[str]) diff --git a/src/trio/_tools/gen_exports.py b/src/trio/_tools/gen_exports.py index bfd9ca8489..c762ee138a 100755 --- a/src/trio/_tools/gen_exports.py +++ b/src/trio/_tools/gen_exports.py @@ -253,7 +253,7 @@ def gen_public_wrappers_source(file: File) -> str: func = astor.to_source(method, indent_with=" " * 4) if is_cm: # pragma: no cover - func = func.replace("->Iterator", "->ContextManager") + func = func.replace("->Iterator", "->AbstractContextManager") # Create export function body template = TEMPLATE.format( @@ -385,25 +385,29 @@ def main() -> None: # pragma: no cover """ IMPORTS_KQUEUE = """\ -from typing import Callable, ContextManager, TYPE_CHECKING +from typing import TYPE_CHECKING if TYPE_CHECKING: import select + from collections.abc import Callable + from contextlib import AbstractContextManager from .. import _core - from ._traps import Abort, RaiseCancelT from .._file_io import _HasFileNo + from ._traps import Abort, RaiseCancelT """ IMPORTS_WINDOWS = """\ -from typing import TYPE_CHECKING, ContextManager +from typing import TYPE_CHECKING if TYPE_CHECKING: - from .._file_io import _HasFileNo - from ._windows_cffi import Handle, CData + from contextlib import AbstractContextManager + from typing_extensions import Buffer + from .._file_io import _HasFileNo from ._unbounded_queue import UnboundedQueue + from ._windows_cffi import Handle, CData """ diff --git a/src/trio/_util.py b/src/trio/_util.py index 3af8b5cfde..e7c15b5da0 100644 --- a/src/trio/_util.py +++ b/src/trio/_util.py @@ -7,15 +7,13 @@ import signal import threading from abc import ABCMeta +from collections.abc import Awaitable, Callable, Sequence from functools import update_wrapper from typing import ( TYPE_CHECKING, Any, - Awaitable, - Callable, Generic, NoReturn, - Sequence, TypeVar, final as std_final, ) diff --git a/src/trio/testing/_check_streams.py b/src/trio/testing/_check_streams.py index 335c9c1ea5..3bf5442814 100644 --- a/src/trio/testing/_check_streams.py +++ b/src/trio/testing/_check_streams.py @@ -3,14 +3,11 @@ import random import sys +from collections.abc import Awaitable, Callable, Generator from contextlib import contextmanager, suppress from typing import ( TYPE_CHECKING, - Awaitable, - Callable, - Generator, Generic, - Tuple, TypeVar, ) @@ -31,7 +28,7 @@ Res1 = TypeVar("Res1", bound=AsyncResource) Res2 = TypeVar("Res2", bound=AsyncResource) -StreamMaker: TypeAlias = Callable[[], Awaitable[Tuple[Res1, Res2]]] +StreamMaker: TypeAlias = Callable[[], Awaitable[tuple[Res1, Res2]]] class _ForceCloseBoth(Generic[Res1, Res2]): diff --git a/src/trio/testing/_fake_net.py b/src/trio/testing/_fake_net.py index 541872826f..749bedf8d0 100644 --- a/src/trio/testing/_fake_net.py +++ b/src/trio/testing/_fake_net.py @@ -17,7 +17,6 @@ from typing import ( TYPE_CHECKING, Any, - Iterable, NoReturn, TypeVar, Union, @@ -31,6 +30,7 @@ if TYPE_CHECKING: import builtins + from collections.abc import Iterable from socket import AddressFamily, SocketKind from types import TracebackType diff --git a/src/trio/testing/_memory_streams.py b/src/trio/testing/_memory_streams.py index 14f8505678..40e85efaa3 100644 --- a/src/trio/testing/_memory_streams.py +++ b/src/trio/testing/_memory_streams.py @@ -1,7 +1,8 @@ from __future__ import annotations import operator -from typing import TYPE_CHECKING, Awaitable, Callable, TypeVar +from collections.abc import Awaitable, Callable +from typing import TYPE_CHECKING, TypeVar from .. import _core, _util from .._highlevel_generic import StapledStream diff --git a/src/trio/testing/_raises_group.py b/src/trio/testing/_raises_group.py index 551398a302..e93e0bf3bd 100644 --- a/src/trio/testing/_raises_group.py +++ b/src/trio/testing/_raises_group.py @@ -2,14 +2,12 @@ import re import sys +from contextlib import AbstractContextManager +from re import Pattern from typing import ( TYPE_CHECKING, - Callable, - ContextManager, Generic, Literal, - Pattern, - Sequence, cast, overload, ) @@ -22,6 +20,7 @@ # sphinx will *only* work if we use types.TracebackType, and import # *inside* TYPE_CHECKING. No other combination works..... import types + from collections.abc import Callable, Sequence from _pytest._code.code import ExceptionChainRepr, ReprExceptionInfo, Traceback from typing_extensions import TypeGuard, TypeVar @@ -277,7 +276,10 @@ def __str__(self) -> str: @final -class RaisesGroup(ContextManager[ExceptionInfo[BaseExceptionGroup[E]]], SuperClass[E]): +class RaisesGroup( + AbstractContextManager[ExceptionInfo[BaseExceptionGroup[E]]], + SuperClass[E], +): """Contextmanager for checking for an expected `ExceptionGroup`. This works similar to ``pytest.raises``, and a version of it will hopefully be added upstream, after which this can be deprecated and removed. See https://github.com/pytest-dev/pytest/issues/11538 diff --git a/test-requirements.txt b/test-requirements.txt index a544456455..314eaf4b94 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv pip compile --universal --python-version=3.8 test-requirements.in -o test-requirements.txt +# uv pip compile --universal --python-version=3.9 test-requirements.in -o test-requirements.txt alabaster==0.7.13 # via sphinx astor==0.8.1 @@ -109,8 +109,6 @@ pyright==1.1.382.post1 # via -r test-requirements.in pytest==8.3.3 # via -r test-requirements.in -pytz==2024.2 ; python_full_version < '3.9' - # via babel requests==2.32.3 # via sphinx ruff==0.6.8