diff --git a/lib/ts_utils/metadata.py b/lib/ts_utils/metadata.py index 40bc42354..f851ce536 100644 --- a/lib/ts_utils/metadata.py +++ b/lib/ts_utils/metadata.py @@ -5,6 +5,7 @@ from __future__ import annotations +import functools import re import urllib.parse from collections.abc import Mapping @@ -19,7 +20,6 @@ from packaging.requirements import Requirement from packaging.specifiers import Specifier from .paths import PYPROJECT_PATH, STUBS_PATH, distribution_path -from .utils import cache __all__ = [ "NoSuchStubError", @@ -42,7 +42,7 @@ def _is_list_of_strings(obj: object) -> TypeGuard[list[str]]: return isinstance(obj, list) and all(isinstance(item, str) for item in obj) -@cache +@functools.cache def _get_oldest_supported_python() -> str: with PYPROJECT_PATH.open("rb") as config: val = tomli.load(config)["tool"]["typeshed"]["oldest_supported_python"] @@ -79,7 +79,7 @@ class StubtestSettings: return ret -@cache +@functools.cache def read_stubtest_settings(distribution: str) -> StubtestSettings: """Return an object describing the stubtest settings for a single stubs distribution.""" with metadata_path(distribution).open("rb") as f: @@ -187,7 +187,7 @@ class NoSuchStubError(ValueError): """Raise NoSuchStubError to indicate that a stubs/{distribution} directory doesn't exist.""" -@cache +@functools.cache def read_metadata(distribution: str) -> StubMetadata: """Return an object describing the metadata of a stub as given in the METADATA.toml file. @@ -328,12 +328,12 @@ class PackageDependencies(NamedTuple): external_pkgs: tuple[Requirement, ...] -@cache +@functools.cache def get_pypi_name_to_typeshed_name_mapping() -> Mapping[str, str]: return {read_metadata(stub_dir.name).stub_distribution: stub_dir.name for stub_dir in STUBS_PATH.iterdir()} -@cache +@functools.cache def read_dependencies(distribution: str) -> PackageDependencies: """Read the dependencies listed in a METADATA.toml file for a stubs package. @@ -360,7 +360,7 @@ def read_dependencies(distribution: str) -> PackageDependencies: return PackageDependencies(tuple(typeshed), tuple(external)) -@cache +@functools.cache def get_recursive_requirements(package_name: str) -> PackageDependencies: """Recursively gather dependencies for a single stubs package. diff --git a/lib/ts_utils/utils.py b/lib/ts_utils/utils.py index 50b18df69..4df06665e 100644 --- a/lib/ts_utils/utils.py +++ b/lib/ts_utils/utils.py @@ -2,10 +2,10 @@ from __future__ import annotations +import functools import re import sys from collections.abc import Iterable, Mapping -from functools import lru_cache from pathlib import Path from typing import Any, Final, NamedTuple from typing_extensions import TypeAlias @@ -26,11 +26,6 @@ from .paths import REQUIREMENTS_PATH, STDLIB_PATH, STUBS_PATH, TEST_CASES_DIR, a PYTHON_VERSION: Final = f"{sys.version_info.major}.{sys.version_info.minor}" -# A backport of functools.cache for Python <3.9 -# This module is imported by mypy_test.py, which needs to run on 3.8 in CI -cache = lru_cache(None) - - def strip_comments(text: str) -> str: return text.split("#")[0].strip() @@ -81,7 +76,7 @@ def print_time(t: float) -> None: # ==================================================================== -@cache +@functools.cache def venv_python(venv_dir: Path) -> Path: if sys.platform == "win32": return venv_dir / "Scripts" / "python.exe" @@ -93,7 +88,7 @@ def venv_python(venv_dir: Path) -> Path: # ==================================================================== -@cache +@functools.cache def parse_requirements() -> Mapping[str, Requirement]: """Return a dictionary of requirements from the requirements file.""" with REQUIREMENTS_PATH.open(encoding="UTF-8") as requirements_file: @@ -206,7 +201,7 @@ def allowlists(distribution_name: str) -> list[str]: # ==================================================================== -@cache +@functools.cache def get_gitignore_spec() -> pathspec.PathSpec: with open(".gitignore", encoding="UTF-8") as f: return pathspec.PathSpec.from_lines("gitwildmatch", f.readlines()) diff --git a/pyproject.toml b/pyproject.toml index 72d0a4df4..63e187ac1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ force-exclude = ".*_pb2.pyi" [tool.ruff] line-length = 130 # Oldest supported Python version -target-version = "py38" +target-version = "py39" fix = true exclude = [ # virtual environment diff --git a/scripts/stubsabot.py b/scripts/stubsabot.py index 87f0fd18d..1ce147b0b 100755 --- a/scripts/stubsabot.py +++ b/scripts/stubsabot.py @@ -730,8 +730,6 @@ async def suggest_typeshed_obsolete(obsolete: Obsolete, session: aiohttp.ClientS async def main() -> None: - assert sys.version_info >= (3, 9) - parser = argparse.ArgumentParser() parser.add_argument( "--action-level", diff --git a/stdlib/@tests/stubtest_allowlists/darwin-py39.txt b/stdlib/@tests/stubtest_allowlists/darwin-py39.txt index ad9ecc301..8deeed142 100644 --- a/stdlib/@tests/stubtest_allowlists/darwin-py39.txt +++ b/stdlib/@tests/stubtest_allowlists/darwin-py39.txt @@ -10,9 +10,9 @@ email.utils.getaddresses email.utils.parseaddr -# ====== -# <= 3.9 -# ====== +# ======== +# 3.9 only +# ======== # Added in Python 3.9.14 sys.set_int_max_str_digits diff --git a/stdlib/@tests/stubtest_allowlists/linux-py39.txt b/stdlib/@tests/stubtest_allowlists/linux-py39.txt index 21522122b..6c3feb290 100644 --- a/stdlib/@tests/stubtest_allowlists/linux-py39.txt +++ b/stdlib/@tests/stubtest_allowlists/linux-py39.txt @@ -1,6 +1,6 @@ -# ====== -# <= 3.9 -# ====== +# ======== +# 3.9 only +# ======== # `eventmask` argument exists at runtime, but is not correctly recognized # while being inspected by stubtest. Fixed in Python 3.10. diff --git a/stdlib/@tests/stubtest_allowlists/py39.txt b/stdlib/@tests/stubtest_allowlists/py39.txt index b52ad6594..e84afe3de 100644 --- a/stdlib/@tests/stubtest_allowlists/py39.txt +++ b/stdlib/@tests/stubtest_allowlists/py39.txt @@ -1,8 +1,3 @@ -# ======================== -# New errors in Python 3.9 -# ======================== - - # ======== # 3.9 only # ======== @@ -16,24 +11,7 @@ collections.MappingView.__class_getitem__ hmac.HMAC.digest_cons hmac.HMAC.inner hmac.HMAC.outer - - -# =========== -# 3.9 to 3.10 -# =========== - -builtins.float.__setformat__ # Internal method for CPython test suite -typing._SpecialForm.__mro_entries__ # Exists at runtime, but missing from stubs - - -# =================================== -# Pre-existing errors from Python 3.8 -# =================================== - - -# ====== -# <= 3.9 -# ====== +xxsubtype # module missing from the stubs builtins.input # Incorrect default value in text signature, fixed in 3.10 collections.AsyncGenerator.__anext__ # async at runtime, deliberately not in the stub, see #7491 @@ -44,13 +22,13 @@ collections.ByteString # see comments in py3_common.txt collections.Callable collections.Mapping.get # Adding None to the Union messed up mypy collections.Sequence.index # Supporting None in end is not mandatory -xxsubtype # module missing from the stubs - -# ======= +# =========== # <= 3.10 -# ======= +# =========== +builtins.float.__setformat__ # Internal method for CPython test suite +typing._SpecialForm.__mro_entries__ # Exists at runtime, but missing from stubs email.contentmanager.typ gettext.install # codeset default value is ['unspecified'] so can't be specified gettext.translation # codeset default value is ['unspecified'] so can't be specified diff --git a/stdlib/@tests/stubtest_allowlists/win32-py39.txt b/stdlib/@tests/stubtest_allowlists/win32-py39.txt index 40a525566..3ab6c98d4 100644 --- a/stdlib/@tests/stubtest_allowlists/win32-py39.txt +++ b/stdlib/@tests/stubtest_allowlists/win32-py39.txt @@ -11,9 +11,9 @@ email.utils.getaddresses email.utils.parseaddr -# ====== -# <= 3.9 -# ====== +# ======== +# 3.9 only +# ======== # Added in Python 3.9.14 sys.set_int_max_str_digits diff --git a/stdlib/VERSIONS b/stdlib/VERSIONS index 7a8b950b0..fec56ce59 100644 --- a/stdlib/VERSIONS +++ b/stdlib/VERSIONS @@ -145,7 +145,6 @@ encodings.cp273: 3.4- encodings.cp858: 3.2- encodings.koi8_t: 3.5- encodings.kz1048: 3.5- -encodings.mac_centeuro: 3.0-3.8 ensurepip: 3.0- enum: 3.4- errno: 3.0- diff --git a/stdlib/_collections_abc.pyi b/stdlib/_collections_abc.pyi index 8bac0ce1d..b099bdd98 100644 --- a/stdlib/_collections_abc.pyi +++ b/stdlib/_collections_abc.pyi @@ -1,7 +1,7 @@ import sys from abc import abstractmethod from types import MappingProxyType -from typing import ( # noqa: Y022,Y038 +from typing import ( # noqa: Y022,Y038,UP035 AbstractSet as Set, AsyncGenerator as AsyncGenerator, AsyncIterable as AsyncIterable, @@ -61,7 +61,7 @@ __all__ = [ "MutableSequence", ] if sys.version_info < (3, 14): - from typing import ByteString as ByteString # noqa: Y057 + from typing import ByteString as ByteString # noqa: Y057,UP035 __all__ += ["ByteString"] diff --git a/stdlib/array.pyi b/stdlib/array.pyi index b26336f3e..bd96c9bc2 100644 --- a/stdlib/array.pyi +++ b/stdlib/array.pyi @@ -1,14 +1,10 @@ import sys from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite -from collections.abc import Iterable - -# pytype crashes if array inherits from collections.abc.MutableSequence instead of typing.MutableSequence -from typing import Any, ClassVar, Literal, MutableSequence, SupportsIndex, TypeVar, overload # noqa: Y022 +from collections.abc import Iterable, MutableSequence +from types import GenericAlias +from typing import Any, ClassVar, Literal, SupportsIndex, TypeVar, overload from typing_extensions import Self, TypeAlias -if sys.version_info >= (3, 12): - from types import GenericAlias - _IntTypeCode: TypeAlias = Literal["b", "B", "h", "H", "i", "I", "l", "L", "q", "Q"] _FloatTypeCode: TypeAlias = Literal["f", "d"] _UnicodeTypeCode: TypeAlias = Literal["u"] diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index 9129c0cba..b75250aad 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -36,7 +36,7 @@ from types import CellType, CodeType, GenericAlias, TracebackType # mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping} # are imported from collections.abc in builtins.pyi -from typing import ( # noqa: Y022 +from typing import ( # noqa: Y022,UP035 IO, Any, BinaryIO, diff --git a/stdlib/csv.pyi b/stdlib/csv.pyi index 4a82de638..2c8e7109c 100644 --- a/stdlib/csv.pyi +++ b/stdlib/csv.pyi @@ -26,12 +26,10 @@ else: from _typeshed import SupportsWrite from collections.abc import Collection, Iterable, Mapping, Sequence +from types import GenericAlias from typing import Any, Generic, Literal, TypeVar, overload from typing_extensions import Self -if sys.version_info >= (3, 12): - from types import GenericAlias - __all__ = [ "QUOTE_MINIMAL", "QUOTE_ALL", diff --git a/stdlib/encodings/mac_centeuro.pyi b/stdlib/encodings/mac_centeuro.pyi deleted file mode 100644 index f62195662..000000000 --- a/stdlib/encodings/mac_centeuro.pyi +++ /dev/null @@ -1,21 +0,0 @@ -import codecs -from _codecs import _EncodingMap -from _typeshed import ReadableBuffer - -class Codec(codecs.Codec): - def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... - def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... - -class IncrementalEncoder(codecs.IncrementalEncoder): - def encode(self, input: str, final: bool = False) -> bytes: ... - -class IncrementalDecoder(codecs.IncrementalDecoder): - def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... - -class StreamWriter(Codec, codecs.StreamWriter): ... -class StreamReader(Codec, codecs.StreamReader): ... - -def getregentry() -> codecs.CodecInfo: ... - -decoding_table: str -encoding_table: _EncodingMap diff --git a/stdlib/logging/__init__.pyi b/stdlib/logging/__init__.pyi index 1d6edb024..e555f74a8 100644 --- a/stdlib/logging/__init__.pyi +++ b/stdlib/logging/__init__.pyi @@ -6,13 +6,10 @@ from io import TextIOWrapper from re import Pattern from string import Template from time import struct_time -from types import FrameType, TracebackType +from types import FrameType, GenericAlias, TracebackType from typing import Any, ClassVar, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload from typing_extensions import Self, TypeAlias, deprecated -if sys.version_info >= (3, 11): - from types import GenericAlias - __all__ = [ "BASIC_FORMAT", "BufferingFormatter", diff --git a/stdlib/types.pyi b/stdlib/types.pyi index f89a992b7..537370487 100644 --- a/stdlib/types.pyi +++ b/stdlib/types.pyi @@ -11,13 +11,14 @@ from collections.abc import ( Iterable, Iterator, KeysView, + Mapping, # noqa: Y022 MutableSequence, ValuesView, ) from importlib.machinery import ModuleSpec # pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping -from typing import Any, ClassVar, Literal, Mapping, TypeVar, final, overload # noqa: Y022 +from typing import Any, ClassVar, Literal, TypeVar, final, overload from typing_extensions import ParamSpec, Self, TypeAliasType, TypeVarTuple, deprecated __all__ = [ diff --git a/stdlib/typing_extensions.pyi b/stdlib/typing_extensions.pyi index 234e32e30..3799f4e66 100644 --- a/stdlib/typing_extensions.pyi +++ b/stdlib/typing_extensions.pyi @@ -4,59 +4,60 @@ import sys import typing from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import IdentityFunction, Incomplete, Unused +from collections.abc import ( + AsyncGenerator as AsyncGenerator, + AsyncIterable as AsyncIterable, + AsyncIterator as AsyncIterator, + Awaitable as Awaitable, + Collection as Collection, + Container as Container, + Coroutine as Coroutine, + Generator as Generator, + Hashable as Hashable, + ItemsView as ItemsView, + Iterable as Iterable, + Iterator as Iterator, + KeysView as KeysView, + Mapping as Mapping, + MappingView as MappingView, + MutableMapping as MutableMapping, + MutableSequence as MutableSequence, + MutableSet as MutableSet, + Reversible as Reversible, + Sequence as Sequence, + Sized as Sized, + ValuesView as ValuesView, +) from contextlib import AbstractAsyncContextManager as AsyncContextManager, AbstractContextManager as ContextManager +from re import Match as Match, Pattern as Pattern from types import GenericAlias, ModuleType -from typing import ( # noqa: Y022,Y037,Y038,Y039 +from typing import ( # noqa: Y022,Y037,Y038,Y039,UP035 IO as IO, TYPE_CHECKING as TYPE_CHECKING, AbstractSet as AbstractSet, Any as Any, AnyStr as AnyStr, - AsyncGenerator as AsyncGenerator, - AsyncIterable as AsyncIterable, - AsyncIterator as AsyncIterator, - Awaitable as Awaitable, BinaryIO as BinaryIO, Callable as Callable, ChainMap as ChainMap, ClassVar as ClassVar, - Collection as Collection, - Container as Container, - Coroutine as Coroutine, Counter as Counter, DefaultDict as DefaultDict, Deque as Deque, Dict as Dict, ForwardRef as ForwardRef, FrozenSet as FrozenSet, - Generator as Generator, Generic as Generic, - Hashable as Hashable, - ItemsView as ItemsView, - Iterable as Iterable, - Iterator as Iterator, - KeysView as KeysView, List as List, - Mapping as Mapping, - MappingView as MappingView, - Match as Match, - MutableMapping as MutableMapping, - MutableSequence as MutableSequence, - MutableSet as MutableSet, NoReturn as NoReturn, Optional as Optional, - Pattern as Pattern, - Reversible as Reversible, - Sequence as Sequence, Set as Set, - Sized as Sized, Text as Text, TextIO as TextIO, Tuple as Tuple, Type as Type, TypedDict as TypedDict, Union as Union, - ValuesView as ValuesView, _Alias, cast as cast, no_type_check as no_type_check, diff --git a/tests/check_typeshed_structure.py b/tests/check_typeshed_structure.py index 81adb8c74..bcb02061e 100755 --- a/tests/check_typeshed_structure.py +++ b/tests/check_typeshed_structure.py @@ -9,7 +9,6 @@ from __future__ import annotations import os import re -import sys from pathlib import Path from ts_utils.metadata import read_metadata @@ -170,7 +169,6 @@ def check_requirement_pins() -> None: if __name__ == "__main__": - assert sys.version_info >= (3, 9), "Python 3.9+ is required to run this test" check_versions_file() check_metadata() check_requirement_pins() diff --git a/tests/mypy_test.py b/tests/mypy_test.py index bf3a234eb..2eeb532d1 100755 --- a/tests/mypy_test.py +++ b/tests/mypy_test.py @@ -393,8 +393,7 @@ def stdlib_module_name_from_path(path: Path) -> str: assert path.suffix == ".pyi" parts = list(path.parts[1:-1]) if path.parts[-1] != "__init__.pyi": - # TODO: Python 3.9+: Use removesuffix. - parts.append(path.parts[-1][:-4]) + parts.append(path.parts[-1].removesuffix(".pyi")) return ".".join(parts)