Skip to content
51 changes: 38 additions & 13 deletions stdlib/functools.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import sys
import types
from _typeshed import IdentityFunction, 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
Expand All @@ -30,8 +30,10 @@ if sys.version_info >= (3, 9):

_AnyCallable: TypeAlias = Callable[..., object]

_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: ...
Expand All @@ -45,22 +47,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 | None, owner: type | None = ...) -> _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]: ... # type: ignore[misc]
@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]: ... # type: ignore[misc]
@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]: ... # type: ignore[misc]
@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__"],
Expand Down Expand Up @@ -151,9 +179,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],
Expand Down
3 changes: 2 additions & 1 deletion tests/stubtest_allowlists/py37.txt
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ builtins.classmethod.__get__
builtins.property.__get__
builtins.staticmethod.__get__
types.FunctionType.__get__
types.LambdaType.__get__
types.LambdaType.__get__
functools._lru_cache_wrapper.__get__

# Missing from distutils (deprecated, to be removed in 3.12)
distutils.core.Command.dump_options
Expand Down
1 change: 1 addition & 0 deletions tests/stubtest_allowlists/py38.txt
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ builtins.property.__get__
builtins.staticmethod.__get__
types.FunctionType.__get__
types.LambdaType.__get__
functools._lru_cache_wrapper.__get__

# Missing from distutils (deprecated, to be removed in 3.12)
distutils.core.Command.dump_options
Expand Down
1 change: 1 addition & 0 deletions tests/stubtest_allowlists/py39.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ builtins.property.__get__
builtins.staticmethod.__get__
types.FunctionType.__get__
types.LambdaType.__get__
functools._lru_cache_wrapper.__get__

# Missing from distutils (deprecated, to be removed in 3.12)
distutils.core.Command.dump_options
Expand Down
1 change: 1 addition & 0 deletions tests/stubtest_allowlists/py3_common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,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__
Expand Down