From 34c2521a7edd3fc3054e439cfd4de5d32facb139 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Mon, 2 May 2022 19:49:40 -0400 Subject: [PATCH 1/5] improve lru_cache by using ParamSpec --- stdlib/functools.pyi | 51 +++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/stdlib/functools.pyi b/stdlib/functools.pyi index 44feeed63f6a..8042e0d468c7 100644 --- a/stdlib/functools.pyi +++ b/stdlib/functools.pyi @@ -2,8 +2,8 @@ import sys import types from _typeshed import Self, SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized -from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, TypeAlias, final +from typing import Any, Generic, NamedTuple, Protocol, TypeVar, overload +from typing_extensions import Concatenate, Literal, ParamSpec, TypeAlias, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -57,8 +57,10 @@ else: _AnyCallable: TypeAlias = Callable[..., Any] +_P = ParamSpec("_P") _T = TypeVar("_T") _S = TypeVar("_S") +_R = TypeVar("_R") @overload def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T) -> _T: ... @@ -72,22 +74,48 @@ class _CacheInfo(NamedTuple): currsize: int @final -class _lru_cache_wrapper(Generic[_T]): - __wrapped__: Callable[..., _T] - def __call__(self, *args: Hashable, **kwargs: Hashable) -> _T: ... +class _lru_cache_wrapper_0(Generic[_P, _R]): + @property + def __wrapped__(self) -> Callable[_P, _R]: ... + def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R: ... + def cache_info(self) -> _CacheInfo: ... + def cache_clear(self) -> None: ... + def __copy__(self) -> _lru_cache_wrapper_0[_P, _R]: ... + def __deepcopy__(self, __memo: Any) -> _lru_cache_wrapper_0[_P, _R]: ... + +@final +class _lru_cache_wrapper(Generic[_S, _P, _R]): + @property + def __wrapped__(self) -> Callable[Concatenate[_S, _P], _R]: ... + def __call__(self, arg0: _S, *args: _P.args, **kwargs: _P.kwargs) -> _R: ... def cache_info(self) -> _CacheInfo: ... def cache_clear(self) -> None: ... - def __copy__(self) -> _lru_cache_wrapper[_T]: ... - def __deepcopy__(self, __memo: Any) -> _lru_cache_wrapper[_T]: ... + def __copy__(self) -> _lru_cache_wrapper[_S, _P, _R]: ... + def __deepcopy__(self, __memo: Any) -> _lru_cache_wrapper[_S, _P, _R]: ... + def __get__(self, inst: object, owner: type) -> _lru_cache_wrapper_0[_P, _R]: ... + +class _LRUCacheDecorator(Protocol): + @overload + def __call__(self, func: Callable[Concatenate[_S, _P], _R]) -> _lru_cache_wrapper[_S, _P, _R]: ... + @overload + def __call__(self, func: Callable[_P, _R]) -> _lru_cache_wrapper_0[_P, _R]: ... if sys.version_info >= (3, 8): @overload - def lru_cache(maxsize: int | None = ..., typed: bool = ...) -> Callable[[Callable[..., _T]], _lru_cache_wrapper[_T]]: ... + def lru_cache(maxsize: int | None = ..., typed: bool = ...) -> _LRUCacheDecorator: ... + @overload + def lru_cache(maxsize: Callable[Concatenate[_S, _P], _R], typed: bool = ...) -> _lru_cache_wrapper[_S, _P, _R]: ... @overload - def lru_cache(maxsize: Callable[..., _T], typed: bool = ...) -> _lru_cache_wrapper[_T]: ... + def lru_cache(maxsize: Callable[_P, _R], typed: bool = ...) -> _lru_cache_wrapper_0[_P, _R]: ... else: - def lru_cache(maxsize: int | None = ..., typed: bool = ...) -> Callable[[Callable[..., _T]], _lru_cache_wrapper[_T]]: ... + def lru_cache(maxsize: int | None = ..., typed: bool = ...) -> _LRUCacheDecorator: ... + +if sys.version_info >= (3, 9): + @overload + def cache(__user_function: Callable[Concatenate[_S, _P], _R]) -> _lru_cache_wrapper[_S, _P, _R]: ... + @overload + def cache(__user_function: Callable[_P, _R]) -> _lru_cache_wrapper_0[_P, _R]: ... WRAPPER_ASSIGNMENTS: tuple[ Literal["__module__"], Literal["__name__"], Literal["__qualname__"], Literal["__doc__"], Literal["__annotations__"], @@ -178,9 +206,6 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... -if sys.version_info >= (3, 9): - def cache(__user_function: Callable[..., _T]) -> _lru_cache_wrapper[_T]: ... - def _make_key( args: tuple[Hashable, ...], kwds: SupportsItems[Any, Any], From 172540d9c58f64b9e9add488802faf432cd9ee2d Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 19 Jul 2022 16:30:46 +0100 Subject: [PATCH 2/5] type: ignore --- stdlib/functools.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/functools.pyi b/stdlib/functools.pyi index e2eab6f540cd..0fa79f46e6a4 100644 --- a/stdlib/functools.pyi +++ b/stdlib/functools.pyi @@ -69,7 +69,7 @@ class _lru_cache_wrapper(Generic[_S, _P, _R]): class _LRUCacheDecorator(Protocol): @overload - def __call__(self, func: Callable[Concatenate[_S, _P], _R]) -> _lru_cache_wrapper[_S, _P, _R]: ... + def __call__(self, func: Callable[Concatenate[_S, _P], _R]) -> _lru_cache_wrapper[_S, _P, _R]: ... # type: ignore[misc] @overload def __call__(self, func: Callable[_P, _R]) -> _lru_cache_wrapper_0[_P, _R]: ... @@ -77,7 +77,7 @@ if sys.version_info >= (3, 8): @overload def lru_cache(maxsize: int | None = ..., typed: bool = ...) -> _LRUCacheDecorator: ... @overload - def lru_cache(maxsize: Callable[Concatenate[_S, _P], _R], typed: bool = ...) -> _lru_cache_wrapper[_S, _P, _R]: ... + def lru_cache(maxsize: Callable[Concatenate[_S, _P], _R], typed: bool = ...) -> _lru_cache_wrapper[_S, _P, _R]: ... # type: ignore[misc] @overload def lru_cache(maxsize: Callable[_P, _R], typed: bool = ...) -> _lru_cache_wrapper_0[_P, _R]: ... From 0a1e546a6ce8f1b462431512b56e47c779a426a5 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 19 Jul 2022 16:34:18 +0100 Subject: [PATCH 3/5] More ignore --- stdlib/functools.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/functools.pyi b/stdlib/functools.pyi index 0fa79f46e6a4..8d52b49717d3 100644 --- a/stdlib/functools.pyi +++ b/stdlib/functools.pyi @@ -86,7 +86,7 @@ else: if sys.version_info >= (3, 9): @overload - def cache(__user_function: Callable[Concatenate[_S, _P], _R]) -> _lru_cache_wrapper[_S, _P, _R]: ... + def cache(__user_function: Callable[Concatenate[_S, _P], _R]) -> _lru_cache_wrapper[_S, _P, _R]: ... # type: ignore[misc] @overload def cache(__user_function: Callable[_P, _R]) -> _lru_cache_wrapper_0[_P, _R]: ... From c224f26848026191cb5a1887223cc271715340bf Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Wed, 20 Jul 2022 18:05:09 +0100 Subject: [PATCH 4/5] Fix stubtest issues --- stdlib/functools.pyi | 2 +- tests/stubtest_allowlists/py3_common.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/functools.pyi b/stdlib/functools.pyi index 8d52b49717d3..54238ddbe340 100644 --- a/stdlib/functools.pyi +++ b/stdlib/functools.pyi @@ -65,7 +65,7 @@ class _lru_cache_wrapper(Generic[_S, _P, _R]): def cache_clear(self) -> None: ... def __copy__(self) -> _lru_cache_wrapper[_S, _P, _R]: ... def __deepcopy__(self, __memo: Any) -> _lru_cache_wrapper[_S, _P, _R]: ... - def __get__(self, inst: object, owner: type) -> _lru_cache_wrapper_0[_P, _R]: ... + def __get__(self, inst: object | None, owner: type | None = ...) -> _lru_cache_wrapper_0[_P, _R]: ... class _LRUCacheDecorator(Protocol): @overload diff --git a/tests/stubtest_allowlists/py3_common.txt b/tests/stubtest_allowlists/py3_common.txt index e0f4e4496880..a41c1ba3549e 100644 --- a/tests/stubtest_allowlists/py3_common.txt +++ b/tests/stubtest_allowlists/py3_common.txt @@ -92,6 +92,7 @@ distutils.command.bdist_packager # It exists in docs as package name but not in distutils.version.Version._cmp # class should have declared this distutils.version.Version.parse # class should have declared this enum.Enum._generate_next_value_ +functools._lru_cache_wrapper.__wrapped__ # Exists at runtime but stubtest can't see it hashlib.sha3_\d+ # Can be a class or a built-in function, can't be subclassed at runtime hashlib.shake_\d+ # Can be a class or a built-in function, can't be subclassed at runtime http.HTTPStatus.description # set in __new__ From dd99e4b081bd88f130a21cd1b9000067db8341db Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Wed, 20 Jul 2022 18:07:32 +0100 Subject: [PATCH 5/5] More stubtest fixes --- tests/stubtest_allowlists/py310.txt | 1 + tests/stubtest_allowlists/py37.txt | 1 + tests/stubtest_allowlists/py38.txt | 1 + tests/stubtest_allowlists/py39.txt | 1 + 4 files changed, 4 insertions(+) diff --git a/tests/stubtest_allowlists/py310.txt b/tests/stubtest_allowlists/py310.txt index b370842a52c5..af73d9df830d 100644 --- a/tests/stubtest_allowlists/py310.txt +++ b/tests/stubtest_allowlists/py310.txt @@ -175,3 +175,4 @@ builtins.property.__get__ builtins.staticmethod.__get__ types.FunctionType.__get__ types.LambdaType.__get__ +functools._lru_cache_wrapper.__get__ diff --git a/tests/stubtest_allowlists/py37.txt b/tests/stubtest_allowlists/py37.txt index 8f3eb69e8f3e..ac4029a682f1 100644 --- a/tests/stubtest_allowlists/py37.txt +++ b/tests/stubtest_allowlists/py37.txt @@ -160,3 +160,4 @@ builtins.property.__get__ builtins.staticmethod.__get__ types.FunctionType.__get__ types.LambdaType.__get__ +functools._lru_cache_wrapper.__get__ diff --git a/tests/stubtest_allowlists/py38.txt b/tests/stubtest_allowlists/py38.txt index e50a633a39be..44c771f54846 100644 --- a/tests/stubtest_allowlists/py38.txt +++ b/tests/stubtest_allowlists/py38.txt @@ -177,3 +177,4 @@ builtins.property.__get__ builtins.staticmethod.__get__ types.FunctionType.__get__ types.LambdaType.__get__ +functools._lru_cache_wrapper.__get__ diff --git a/tests/stubtest_allowlists/py39.txt b/tests/stubtest_allowlists/py39.txt index 5783e943da7a..d4650687b058 100644 --- a/tests/stubtest_allowlists/py39.txt +++ b/tests/stubtest_allowlists/py39.txt @@ -176,3 +176,4 @@ builtins.property.__get__ builtins.staticmethod.__get__ types.FunctionType.__get__ types.LambdaType.__get__ +functools._lru_cache_wrapper.__get__