From 67d0c99de7edc5249c5e1bffdfb4af6af9f39f46 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 25 Jul 2023 17:40:38 +0100 Subject: [PATCH 01/15] Use ParamSpec for wrapped signatures --- .mypy.ini | 24 ++++++++++++++++++++++ async_lru/__init__.py | 48 ++++++++++++++++++++++++------------------- setup.cfg | 6 ------ tests/conftest.py | 11 ++++++---- 4 files changed, 58 insertions(+), 31 deletions(-) create mode 100644 .mypy.ini diff --git a/.mypy.ini b/.mypy.ini new file mode 100644 index 0000000..71ff0a8 --- /dev/null +++ b/.mypy.ini @@ -0,0 +1,24 @@ +[mypy] +files = async_lru, tests +check_untyped_defs = True +follow_imports_for_stubs = True +disallow_any_decorated = True +disallow_any_generics = True +disallow_any_unimported = True +disallow_incomplete_defs = True +disallow_subclassing_any = True +disallow_untyped_calls = True +disallow_untyped_decorators = True +disallow_untyped_defs = True +enable_error_code = ignore-without-code, possibly-undefined, redundant-expr, redundant-self, truthy-bool, truthy-iterable, unused-awaitable +implicit_reexport = False +no_implicit_optional = True +pretty = True +show_column_numbers = True +show_error_codes = True +strict_equality = True +warn_incomplete_stub = True +warn_redundant_casts = True +warn_return_any = True +warn_unreachable = True +warn_unused_ignores = True diff --git a/async_lru/__init__.py b/async_lru/__init__.py index 0bdffc7..2728dd8 100644 --- a/async_lru/__init__.py +++ b/async_lru/__init__.py @@ -21,6 +21,11 @@ overload, ) +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + if sys.version_info >= (3, 11): from typing import Self @@ -35,9 +40,10 @@ _T = TypeVar("_T") _R = TypeVar("_R") +_P = ParamSpec("_P") _Coro = Coroutine[Any, Any, _R] -_CB = Callable[..., _Coro[_R]] -_CBP = Union[_CB[_R], "partial[_Coro[_R]]", "partialmethod[_Coro[_R]]"] +_CB = Callable[_P, _Coro[_R]] +_CBP = Union[_CB[_P, _R], "partial[_Coro[_R]]", "partialmethod[_Coro[_R]]"] @final @@ -61,10 +67,10 @@ def cancel(self) -> None: @final -class _LRUCacheWrapper(Generic[_R]): +class _LRUCacheWrapper(Generic[_P, _R]): def __init__( self, - fn: _CB[_R], + fn: _CB[_P, _R], maxsize: Optional[int], typed: bool, ttl: Optional[float], @@ -186,7 +192,7 @@ def _task_done_callback( fut.set_result(task.result()) - async def __call__(self, /, *fn_args: Any, **fn_kwargs: Any) -> _R: + async def __call__(self, /, *fn_args: _P.args, **fn_kwargs: _P.kwargs) -> _R: if self.__closed: raise RuntimeError(f"alru_cache is closed for {self}") @@ -204,7 +210,7 @@ async def __call__(self, /, *fn_args: Any, **fn_kwargs: Any) -> _R: exc = cache_item.fut._exception if exc is None: - self._cache_hit(key) + self._cache_hit(key) # type: ignore[unreachable] # github.com/python/typeshed/pull/10502 return cache_item.fut.result() else: # exception here @@ -213,7 +219,7 @@ async def __call__(self, /, *fn_args: Any, **fn_kwargs: Any) -> _R: fut = loop.create_future() coro = self.__wrapped__(*fn_args, **fn_kwargs) - task: asyncio.Task[_R] = loop.create_task(coro) + task = loop.create_task(coro) self.__tasks.add(task) task.add_done_callback(partial(self._task_done_callback, fut, key)) @@ -228,7 +234,7 @@ async def __call__(self, /, *fn_args: Any, **fn_kwargs: Any) -> _R: def __get__( self, instance: _T, owner: Optional[Type[_T]] - ) -> Union[Self, "_LRUCacheWrapperInstanceMethod[_R, _T]"]: + ) -> Union[Self, "_LRUCacheWrapperInstanceMethod[_P, _R, _T]"]: if owner is None: return self else: @@ -236,10 +242,10 @@ def __get__( @final -class _LRUCacheWrapperInstanceMethod(Generic[_R, _T]): +class _LRUCacheWrapperInstanceMethod(Generic[_P, _R, _T]): def __init__( self, - wrapper: _LRUCacheWrapper[_R], + wrapper: _LRUCacheWrapper[_P, _R], instance: _T, ) -> None: try: @@ -290,16 +296,16 @@ def cache_info(self) -> _CacheInfo: def cache_parameters(self) -> _CacheParameters: return self.__wrapper.cache_parameters() - async def __call__(self, /, *fn_args: Any, **fn_kwargs: Any) -> _R: - return await self.__wrapper(self.__instance, *fn_args, **fn_kwargs) + async def __call__(self, /, *fn_args: _P.args, **fn_kwargs: _P.kwargs) -> _R: + return await self.__wrapper(self.__instance, *fn_args, **fn_kwargs) # type: ignore[arg-type] def _make_wrapper( maxsize: Optional[int], typed: bool, ttl: Optional[float] = None, -) -> Callable[[_CBP[_R]], _LRUCacheWrapper[_R]]: - def wrapper(fn: _CBP[_R]) -> _LRUCacheWrapper[_R]: +) -> Callable[[_CBP[_P, _R]], _LRUCacheWrapper[_P, _R]]: + def wrapper(fn: _CBP[_P, _R]) -> _LRUCacheWrapper[_P, _R]: origin = fn while isinstance(origin, (partial, partialmethod)): @@ -312,7 +318,7 @@ def wrapper(fn: _CBP[_R]) -> _LRUCacheWrapper[_R]: if hasattr(fn, "_make_unbound_method"): fn = fn._make_unbound_method() - return _LRUCacheWrapper(cast(_CB[_R], fn), maxsize, typed, ttl) + return _LRUCacheWrapper(cast(_CB[_P, _R], fn), maxsize, typed, ttl) return wrapper @@ -323,28 +329,28 @@ def alru_cache( typed: bool = False, *, ttl: Optional[float] = None, -) -> Callable[[_CBP[_R]], _LRUCacheWrapper[_R]]: +) -> Callable[[_CBP[_P, _R]], _LRUCacheWrapper[_P, _R]]: ... @overload def alru_cache( - maxsize: _CBP[_R], + maxsize: _CBP[_P, _R], /, -) -> _LRUCacheWrapper[_R]: +) -> _LRUCacheWrapper[_P, _R]: ... def alru_cache( - maxsize: Union[Optional[int], _CBP[_R]] = 128, + maxsize: Union[Optional[int], _CBP[_P, _R]] = 128, typed: bool = False, *, ttl: Optional[float] = None, -) -> Union[Callable[[_CBP[_R]], _LRUCacheWrapper[_R]], _LRUCacheWrapper[_R]]: +) -> Union[Callable[[_CBP[_P, _R]], _LRUCacheWrapper[_P, _R]], _LRUCacheWrapper[_P, _R]]: if maxsize is None or isinstance(maxsize, int): return _make_wrapper(maxsize, typed, ttl) else: - fn = cast(_CB[_R], maxsize) + fn = maxsize if callable(fn) or hasattr(fn, "_make_unbound_method"): return _make_wrapper(128, False, None)(fn) diff --git a/setup.cfg b/setup.cfg index e7b500e..f7a0128 100644 --- a/setup.cfg +++ b/setup.cfg @@ -78,9 +78,3 @@ testpaths = tests/ junit_family=xunit2 asyncio_mode=auto timeout=15 - - -[mypy] -strict=True -pretty=True -packages=async_lru, tests diff --git a/tests/conftest.py b/tests/conftest.py index 36147d4..0ffb814 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,15 +1,18 @@ from functools import _CacheInfo -from typing import Callable +from typing import Callable, ParamSpec, TypeVar import pytest -from async_lru import _R, _LRUCacheWrapper +from async_lru import _LRUCacheWrapper + +_T = TypeVar("_T") +_P = ParamSpec("_P") @pytest.fixture -def check_lru() -> Callable[..., None]: +def check_lru() -> Callable[..., None]: # type: ignore[misc] def _check_lru( - wrapped: _LRUCacheWrapper[_R], + wrapped: _LRUCacheWrapper[_P, _T], *, hits: int, misses: int, From 79cc7e072b1f4b2e868c656265c6ff25564df82e Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 25 Jul 2023 17:47:53 +0100 Subject: [PATCH 02/15] Lint --- async_lru/__init__.py | 5 ++++- tests/conftest.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/async_lru/__init__.py b/async_lru/__init__.py index 2728dd8..0e5c67c 100644 --- a/async_lru/__init__.py +++ b/async_lru/__init__.py @@ -21,6 +21,7 @@ overload, ) + if sys.version_info >= (3, 10): from typing import ParamSpec else: @@ -346,7 +347,9 @@ def alru_cache( typed: bool = False, *, ttl: Optional[float] = None, -) -> Union[Callable[[_CBP[_P, _R]], _LRUCacheWrapper[_P, _R]], _LRUCacheWrapper[_P, _R]]: +) -> Union[ + Callable[[_CBP[_P, _R]], _LRUCacheWrapper[_P, _R]], _LRUCacheWrapper[_P, _R] +]: if maxsize is None or isinstance(maxsize, int): return _make_wrapper(maxsize, typed, ttl) else: diff --git a/tests/conftest.py b/tests/conftest.py index 0ffb814..fd55ee6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,7 @@ from async_lru import _LRUCacheWrapper + _T = TypeVar("_T") _P = ParamSpec("_P") From 30c5aa687242b49ea25012245470a8e8e22eda8c Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 25 Jul 2023 17:48:56 +0100 Subject: [PATCH 03/15] Lint --- tests/conftest.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index fd55ee6..281ca84 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,18 @@ +import sys from functools import _CacheInfo -from typing import Callable, ParamSpec, TypeVar +from typing import Callable, TypeVar import pytest from async_lru import _LRUCacheWrapper +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + + _T = TypeVar("_T") _P = ParamSpec("_P") From 58cf7f0fce4d7b761c807d7250f781e48dd88c49 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Thu, 26 Oct 2023 19:36:03 +0100 Subject: [PATCH 04/15] Update setup.cfg --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 45682fc..344abfe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -78,4 +78,4 @@ testpaths = tests/ junit_family=xunit2 asyncio_mode=auto timeout=15 -xfail_strict = true \ No newline at end of file +xfail_strict = true From 0e56d889bd45fa1792e1d781be2905ff25f5fd60 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Thu, 26 Oct 2023 19:52:43 +0100 Subject: [PATCH 05/15] Update __init__.py --- async_lru/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/async_lru/__init__.py b/async_lru/__init__.py index f7f5d8c..202bf33 100644 --- a/async_lru/__init__.py +++ b/async_lru/__init__.py @@ -211,7 +211,7 @@ async def __call__(self, /, *fn_args: _P.args, **fn_kwargs: _P.kwargs) -> _R: exc = cache_item.fut._exception if exc is None: - self._cache_hit(key) # type: ignore[unreachable] # github.com/python/typeshed/pull/10502 + self._cache_hit(key) return cache_item.fut.result() else: # exception here From f358ce3a83648d7cb31cd07254eb4899f2b6718c Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 1 Feb 2025 18:04:48 +0000 Subject: [PATCH 06/15] Run CI on any PR --- .github/workflows/ci-cd.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index c661d28..3deb49c 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -7,9 +7,6 @@ on: - '[0-9].[0-9]+' # matches to backport branches, e.g. 3.6 tags: [ 'v*' ] pull_request: - branches: - - master - - '[0-9].[0-9]+' jobs: From ae08cc08830afbbf8cb134f535013c33d81c677b Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 2 Feb 2025 21:48:18 +0000 Subject: [PATCH 07/15] Partial partial() support --- async_lru/__init__.py | 24 +++++++++++------------- tests/test_basic.py | 15 +++++++++++++++ tests/test_exception.py | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/async_lru/__init__.py b/async_lru/__init__.py index 54b76e7..17877bf 100644 --- a/async_lru/__init__.py +++ b/async_lru/__init__.py @@ -42,9 +42,6 @@ _T = TypeVar("_T") _R = TypeVar("_R") _P = ParamSpec("_P") -_Coro = Coroutine[Any, Any, _R] -_CB = Callable[_P, _Coro[_R]] -_CBP = Union[_CB[_P, _R], "partial[_Coro[_R]]", "partialmethod[_Coro[_R]]"] @final @@ -71,7 +68,7 @@ def cancel(self) -> None: class _LRUCacheWrapper(Generic[_P, _R]): def __init__( self, - fn: _CB[_P, _R], + fn: Callable[_P, Coroutine[Any, Any, _R]], maxsize: Optional[int], typed: bool, ttl: Optional[float], @@ -299,8 +296,8 @@ def _make_wrapper( maxsize: Optional[int], typed: bool, ttl: Optional[float] = None, -) -> Callable[[_CBP[_P, _R]], _LRUCacheWrapper[_P, _R]]: - def wrapper(fn: _CBP[_P, _R]) -> _LRUCacheWrapper[_P, _R]: +) -> Callable[[Callable[_P, Coroutine[Any, Any, _R]]], _LRUCacheWrapper[_P, _R]]: + def wrapper(fn: Callable[_P, Coroutine[Any, Any, _R]]) -> _LRUCacheWrapper[_P, _R]: origin = fn while isinstance(origin, (partial, partialmethod)): @@ -313,7 +310,7 @@ def wrapper(fn: _CBP[_P, _R]) -> _LRUCacheWrapper[_P, _R]: if hasattr(fn, "_make_unbound_method"): fn = fn._make_unbound_method() - return _LRUCacheWrapper(cast(_CB[_P, _R], fn), maxsize, typed, ttl) + return _LRUCacheWrapper(fn, maxsize, typed, ttl) return wrapper @@ -324,32 +321,33 @@ def alru_cache( typed: bool = False, *, ttl: Optional[float] = None, -) -> Callable[[_CBP[_P, _R]], _LRUCacheWrapper[_P, _R]]: +) -> Callable[[Callable[_P, Coroutine[Any, Any, _R]]], _LRUCacheWrapper[_P, _R]]: ... @overload -def alru_cache( - maxsize: _CBP[_P, _R], +def alru_cache( # type: ignore[misc] + maxsize: Callable[_P, Coroutine[Any, Any, _R]], /, ) -> _LRUCacheWrapper[_P, _R]: ... def alru_cache( - maxsize: Union[Optional[int], _CBP[_P, _R]] = 128, + maxsize: Union[Optional[int], Callable[_P, Coroutine[Any, Any, _R]]] = 128, typed: bool = False, *, ttl: Optional[float] = None, ) -> Union[ - Callable[[_CBP[_P, _R]], _LRUCacheWrapper[_P, _R]], _LRUCacheWrapper[_P, _R] + Callable[[Callable[_P, Coroutine[Any, Any, _R]]], _LRUCacheWrapper[_P, _R]], _LRUCacheWrapper[_P, _R] ]: if maxsize is None or isinstance(maxsize, int): return _make_wrapper(maxsize, typed, ttl) else: fn = maxsize - if callable(fn) or hasattr(fn, "_make_unbound_method"): + # partialmethod is not callable() at runtime. + if callable(fn) or hasattr(fn, "_make_unbound_method"): # type: ignore[unreachable] return _make_wrapper(128, False, None)(fn) raise NotImplementedError(f"{fn!r} decorating is not supported") diff --git a/tests/test_basic.py b/tests/test_basic.py index acc57a2..f0a4f05 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -78,6 +78,21 @@ async def coro(val: int) -> int: assert await coro_wrapped2() == 2 +async def test_alru_cache_partial_typing() -> None: + """Test that mypy produces call-arg errors correctly.""" + + async def coro(val: int) -> int: + return val + + coro_wrapped1 = alru_cache(coro) + with pytest.raises(ValueError): + await coro_wrapped1(1, 1) # type: ignore[call-arg] + + coro_wrapped2 = alru_cache(partial(coro, 2)) + with pytest.raises(ValueError): + await coro_wrapped2(4) == 2 # type: ignore[call-arg] + + async def test_alru_cache_await_same_result_async( check_lru: Callable[..., None] ) -> None: diff --git a/tests/test_exception.py b/tests/test_exception.py index 054ea3a..51b19ae 100644 --- a/tests/test_exception.py +++ b/tests/test_exception.py @@ -33,7 +33,7 @@ async def coro(val: int) -> None: reason="Memory leak is not fixed for PyPy3.9", condition=sys.implementation.name == "pypy", ) -async def test_alru_exception_reference_cleanup(check_lru: Callable[..., None]) -> None: +async def test_alru_exception_reference_cleanup(check_lru: Callable[..., None]) -> None: # type: ignore[misc] class CustomClass: ... From bf9442d12a9002b9da49ffd52ea86b2e2408b44f Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 2 Feb 2025 21:54:35 +0000 Subject: [PATCH 08/15] Update __init__.py --- async_lru/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/async_lru/__init__.py b/async_lru/__init__.py index 17877bf..cb227a1 100644 --- a/async_lru/__init__.py +++ b/async_lru/__init__.py @@ -339,7 +339,8 @@ def alru_cache( *, ttl: Optional[float] = None, ) -> Union[ - Callable[[Callable[_P, Coroutine[Any, Any, _R]]], _LRUCacheWrapper[_P, _R]], _LRUCacheWrapper[_P, _R] + Callable[[Callable[_P, Coroutine[Any, Any, _R]]], _LRUCacheWrapper[_P, _R]], + _LRUCacheWrapper[_P, _R] ]: if maxsize is None or isinstance(maxsize, int): return _make_wrapper(maxsize, typed, ttl) From 8067aa714371e77b2663c2bef28ae7dc3922071b Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 2 Feb 2025 21:55:08 +0000 Subject: [PATCH 09/15] Update test_basic.py --- tests/test_basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_basic.py b/tests/test_basic.py index f0a4f05..63b0114 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -85,11 +85,11 @@ async def coro(val: int) -> int: return val coro_wrapped1 = alru_cache(coro) - with pytest.raises(ValueError): + with pytest.raises(TypeError): await coro_wrapped1(1, 1) # type: ignore[call-arg] coro_wrapped2 = alru_cache(partial(coro, 2)) - with pytest.raises(ValueError): + with pytest.raises(TypeError): await coro_wrapped2(4) == 2 # type: ignore[call-arg] From 66d8591dea0e4bfe204aa3aca60848a06c8e6399 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 2 Feb 2025 21:56:23 +0000 Subject: [PATCH 10/15] Update __init__.py --- async_lru/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/async_lru/__init__.py b/async_lru/__init__.py index cb227a1..361f0ee 100644 --- a/async_lru/__init__.py +++ b/async_lru/__init__.py @@ -16,7 +16,6 @@ TypedDict, TypeVar, Union, - cast, final, overload, ) @@ -340,7 +339,7 @@ def alru_cache( ttl: Optional[float] = None, ) -> Union[ Callable[[Callable[_P, Coroutine[Any, Any, _R]]], _LRUCacheWrapper[_P, _R]], - _LRUCacheWrapper[_P, _R] + _LRUCacheWrapper[_P, _R], ]: if maxsize is None or isinstance(maxsize, int): return _make_wrapper(maxsize, typed, ttl) From 1d8ffe9293cec5c627cf0e27e43e61ae1d225d5c Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 2 Feb 2025 22:06:53 +0000 Subject: [PATCH 11/15] Tweak --- async_lru/__init__.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/async_lru/__init__.py b/async_lru/__init__.py index 361f0ee..e9d1653 100644 --- a/async_lru/__init__.py +++ b/async_lru/__init__.py @@ -4,7 +4,6 @@ from asyncio.coroutines import _is_coroutine # type: ignore[attr-defined] from functools import _CacheInfo, _make_key, partial, partialmethod from typing import ( - Any, Callable, Coroutine, Generic, @@ -67,7 +66,7 @@ def cancel(self) -> None: class _LRUCacheWrapper(Generic[_P, _R]): def __init__( self, - fn: Callable[_P, Coroutine[Any, Any, _R]], + fn: Callable[_P, Coroutine[object, object, _R]], maxsize: Optional[int], typed: bool, ttl: Optional[float], @@ -109,7 +108,7 @@ def __init__( self.__misses = 0 self.__tasks: Set["asyncio.Task[_R]"] = set() - def cache_invalidate(self, /, *args: Hashable, **kwargs: Any) -> bool: + def cache_invalidate(self, /, *args: _P.args, **kwargs: _P.kwargs) -> bool: key = _make_key(args, kwargs, self.__typed) cache_item = self.__cache.pop(key, None) @@ -270,7 +269,7 @@ def __init__( self.__instance = instance self.__wrapper = wrapper - def cache_invalidate(self, /, *args: Hashable, **kwargs: Any) -> bool: + def cache_invalidate(self, /, *args: _P.args, **kwargs: _P.kwargs) -> bool: return self.__wrapper.cache_invalidate(self.__instance, *args, **kwargs) def cache_clear(self) -> None: @@ -295,8 +294,8 @@ def _make_wrapper( maxsize: Optional[int], typed: bool, ttl: Optional[float] = None, -) -> Callable[[Callable[_P, Coroutine[Any, Any, _R]]], _LRUCacheWrapper[_P, _R]]: - def wrapper(fn: Callable[_P, Coroutine[Any, Any, _R]]) -> _LRUCacheWrapper[_P, _R]: +) -> Callable[[Callable[_P, Coroutine[object, object, _R]]], _LRUCacheWrapper[_P, _R]]: + def wrapper(fn: Callable[_P, Coroutine[object, object, _R]]) -> _LRUCacheWrapper[_P, _R]: origin = fn while isinstance(origin, (partial, partialmethod)): @@ -320,25 +319,25 @@ def alru_cache( typed: bool = False, *, ttl: Optional[float] = None, -) -> Callable[[Callable[_P, Coroutine[Any, Any, _R]]], _LRUCacheWrapper[_P, _R]]: +) -> Callable[[Callable[_P, Coroutine[object, object, _R]]], _LRUCacheWrapper[_P, _R]]: ... @overload -def alru_cache( # type: ignore[misc] - maxsize: Callable[_P, Coroutine[Any, Any, _R]], +def alru_cache( + maxsize: Callable[_P, Coroutine[object, object, _R]], /, ) -> _LRUCacheWrapper[_P, _R]: ... def alru_cache( - maxsize: Union[Optional[int], Callable[_P, Coroutine[Any, Any, _R]]] = 128, + maxsize: Union[Optional[int], Callable[_P, Coroutine[object, object, _R]]] = 128, typed: bool = False, *, ttl: Optional[float] = None, ) -> Union[ - Callable[[Callable[_P, Coroutine[Any, Any, _R]]], _LRUCacheWrapper[_P, _R]], + Callable[[Callable[_P, Coroutine[object, object, _R]]], _LRUCacheWrapper[_P, _R]], _LRUCacheWrapper[_P, _R], ]: if maxsize is None or isinstance(maxsize, int): From 1bfa68700d58d7a45071c7d524a0982303bce8bd Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 2 Feb 2025 22:09:29 +0000 Subject: [PATCH 12/15] Update __init__.py --- async_lru/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/async_lru/__init__.py b/async_lru/__init__.py index e9d1653..4edc0a8 100644 --- a/async_lru/__init__.py +++ b/async_lru/__init__.py @@ -295,7 +295,9 @@ def _make_wrapper( typed: bool, ttl: Optional[float] = None, ) -> Callable[[Callable[_P, Coroutine[object, object, _R]]], _LRUCacheWrapper[_P, _R]]: - def wrapper(fn: Callable[_P, Coroutine[object, object, _R]]) -> _LRUCacheWrapper[_P, _R]: + def wrapper( + fn: Callable[_P, Coroutine[object, object, _R]] + ) -> _LRUCacheWrapper[_P, _R]: origin = fn while isinstance(origin, (partial, partialmethod)): From 5ffe11590d721480807ad693d0d6f79463e39353 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 2 Feb 2025 22:23:39 +0000 Subject: [PATCH 13/15] Use overload --- async_lru/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/async_lru/__init__.py b/async_lru/__init__.py index e9d1653..646dc95 100644 --- a/async_lru/__init__.py +++ b/async_lru/__init__.py @@ -222,6 +222,18 @@ async def __call__(self, /, *fn_args: _P.args, **fn_kwargs: _P.kwargs) -> _R: self._cache_miss(key) return await asyncio.shield(fut) + @overload + def __get__( + self, instance: _T, owner: None + ) -> Self: + ... + + @overload + def __get__( + self, instance: _T, owner: Type[_T] + ) -> "_LRUCacheWrapperInstanceMethod[_P, _R, _T]": + ... + def __get__( self, instance: _T, owner: Optional[Type[_T]] ) -> Union[Self, "_LRUCacheWrapperInstanceMethod[_P, _R, _T]"]: From 095098846d5cd6eafcdefb6087f810b3a2caaf7d Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 2 Feb 2025 22:26:07 +0000 Subject: [PATCH 14/15] Formatting --- async_lru/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/async_lru/__init__.py b/async_lru/__init__.py index abed8fe..ecca5fd 100644 --- a/async_lru/__init__.py +++ b/async_lru/__init__.py @@ -223,9 +223,7 @@ async def __call__(self, /, *fn_args: _P.args, **fn_kwargs: _P.kwargs) -> _R: return await asyncio.shield(fut) @overload - def __get__( - self, instance: _T, owner: None - ) -> Self: + def __get__(self, instance: _T, owner: None) -> Self: ... @overload From 21b1eee82b30d8bc59e16b708876a995db1f35a1 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Fri, 11 Jul 2025 18:43:16 +0100 Subject: [PATCH 15/15] Update tests/conftest.py --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 281ca84..b0456ce 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,7 +18,7 @@ @pytest.fixture -def check_lru() -> Callable[..., None]: # type: ignore[misc] +def check_lru() -> Callable[..., None]: def _check_lru( wrapped: _LRUCacheWrapper[_P, _T], *,