From 4af58041ce436917a187e96d24e187ad6ac7f822 Mon Sep 17 00:00:00 2001 From: ANSHUL SINGH <72524975+ekanshul@users.noreply.github.com> Date: Wed, 19 Aug 2026 21:33:03 +0530 Subject: [PATCH] [cachetools] Fix TTLCache/TLRUCache constructor type inference The first overload of TTLCache.__init__ and TLRUCache.__init__ annotated self with fresh type variables (_KT2, _VT2) instead of the class-scoped _KT and _VT. Those method-scoped type variables are never solved from the arguments, so pyright infers TTLCache[_KT2, _VT2, float] and mypy infers TTLCache[Never, Never, float] even when the target type is declared. Use the class type variables in the self annotation (with the usual #11780 pyright ignore, as in builtins.dict) and add test cases. Co-Authored-By: Claude Fable 5 --- .../@tests/test_cases/check_cachetools.py | 32 +++++++++++++++++-- stubs/cachetools/cachetools/__init__.pyi | 14 ++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/stubs/cachetools/@tests/test_cases/check_cachetools.py b/stubs/cachetools/@tests/test_cases/check_cachetools.py index cd890c76a209..8e269eb9eab4 100644 --- a/stubs/cachetools/@tests/test_cases/check_cachetools.py +++ b/stubs/cachetools/@tests/test_cases/check_cachetools.py @@ -1,12 +1,15 @@ from __future__ import annotations from collections.abc import Hashable -from typing import Any +from typing import Any, TypeVar from typing_extensions import assert_type -from cachetools import LRUCache, cached, keys as cachekeys +from cachetools import LRUCache, TLRUCache, TTLCache, cached, keys as cachekeys from cachetools.func import fifo_cache, lfu_cache, lru_cache, rr_cache, ttl_cache +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") + # Tests for cachetools.cached # Explicitly parameterize the cache to avoid Unknown types @@ -102,3 +105,28 @@ def method(self, a: int) -> int: k4 = cachekeys.typedmethodkey(inst, 2) assert_type(k4, tuple[Hashable, ...]) + + +# Tests for TTLCache / TLRUCache constructors +# See https://github.com/python/typeshed/issues/15798 + +ttl_inst: TTLCache[str, int] = TTLCache(maxsize=128, ttl=600) +assert_type(ttl_inst, TTLCache[str, int, float]) +assert_type(TTLCache[str, int](maxsize=128, ttl=600), TTLCache[str, int, float]) +ttl_sized: TTLCache[str, bytes] = TTLCache(maxsize=128, ttl=600, getsizeof=len) +ttl_timer: TTLCache[str, int, int] = TTLCache(maxsize=128, ttl=600, timer=lambda: 1) +assert_type(ttl_timer, TTLCache[str, int, int]) + +tlru_inst: TLRUCache[str, int] = TLRUCache(10, lambda k, v, now: now + 10) +assert_type(tlru_inst, TLRUCache[str, int, float]) +assert_type(TLRUCache[str, int](10, lambda k, v, now: now + 10), TLRUCache[str, int, float]) + + +class _TrackedTTLCache(TTLCache[_KT, _VT]): + def popitem(self) -> tuple[_KT, _VT]: + key, value = super().popitem() + return key, value + + +tracked: _TrackedTTLCache[str, int] = _TrackedTTLCache(maxsize=128, ttl=600) +assert_type(tracked, _TrackedTTLCache[str, int]) diff --git a/stubs/cachetools/cachetools/__init__.pyi b/stubs/cachetools/cachetools/__init__.pyi index add55e47a10d..f3955694b28a 100644 --- a/stubs/cachetools/cachetools/__init__.pyi +++ b/stubs/cachetools/cachetools/__init__.pyi @@ -11,8 +11,6 @@ _VT = TypeVar("_VT") _TT = TypeVar("_TT", default=float) _T = TypeVar("_T") _R = TypeVar("_R") -_KT2 = TypeVar("_KT2") -_VT2 = TypeVar("_VT2") class Cache(MutableMapping[_KT, _VT]): def __init__(self, maxsize: float, getsizeof: Callable[[_VT], float] | None = None) -> None: ... @@ -66,7 +64,11 @@ class _TimedCache(Cache[_KT, _VT], Generic[_KT, _VT, _TT]): class TTLCache(_TimedCache[_KT, _VT, _TT]): @overload def __init__( - self: TTLCache[_KT2, _VT2, float], maxsize: float, ttl: float, *, getsizeof: Callable[[_VT2], float] | None = None + self: TTLCache[_KT, _VT, float], # pyright: ignore[reportInvalidTypeVarUse] #11780 + maxsize: float, + ttl: float, + *, + getsizeof: Callable[[_VT], float] | None = None, ) -> None: ... @overload def __init__( @@ -84,11 +86,11 @@ class TTLCache(_TimedCache[_KT, _VT, _TT]): class TLRUCache(_TimedCache[_KT, _VT, _TT]): @overload def __init__( - self: TLRUCache[_KT2, _VT2, float], + self: TLRUCache[_KT, _VT, float], # pyright: ignore[reportInvalidTypeVarUse] #11780 maxsize: float, - ttu: Callable[[_KT2, _VT2, float], float], + ttu: Callable[[_KT, _VT, float], float], *, - getsizeof: Callable[[_VT2], float] | None = None, + getsizeof: Callable[[_VT], float] | None = None, ) -> None: ... @overload def __init__(