Skip to content

Commit 1a9b936

Browse files
Fix cache_clear() when pending tasks (#506)
1 parent 8310323 commit 1a9b936

4 files changed

Lines changed: 45 additions & 5 deletions

File tree

async_lru/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ def cache_invalidate(self, /, *args: Hashable, **kwargs: Any) -> bool:
119119
def cache_clear(self) -> None:
120120
self.__hits = 0
121121
self.__misses = 0
122+
123+
for c in self.__cache.values():
124+
if c.later_call:
125+
c.later_call.cancel()
122126
self.__cache.clear()
123127
self.__tasks.clear()
124128

@@ -162,7 +166,7 @@ def _cache_miss(self, key: Hashable) -> None:
162166
def _task_done_callback(
163167
self, fut: "asyncio.Future[_R]", key: Hashable, task: "asyncio.Task[_R]"
164168
) -> None:
165-
self.__tasks.remove(task)
169+
self.__tasks.discard(task)
166170

167171
cache_item = self.__cache.get(key)
168172
if self.__ttl is not None and cache_item is not None:

setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ install_requires =
5353
[flake8]
5454
exclude = .git,.env,__pycache__,.eggs
5555
max-line-length = 88
56-
ignore = N801,N802,N803,E252,W503,E133,E203
56+
extend-select = B950
57+
ignore = N801,N802,N803,E252,W503,E133,E203,E501
5758

5859
[coverage:run]
5960
branch = True

tests/test_basic.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ async def coro(val: int) -> int:
143143
coro1._LRUCacheWrapper__cache[1].fut.result() # type: ignore[attr-defined]
144144
== coro2._LRUCacheWrapper__cache[1].fut.result() # type: ignore[attr-defined]
145145
)
146-
assert coro1._LRUCacheWrapper__cache != coro2._LRUCacheWrapper__cache # type: ignore[attr-defined] # noqa: E501
147-
assert coro1._LRUCacheWrapper__cache.keys() == coro2._LRUCacheWrapper__cache.keys() # type: ignore[attr-defined] # noqa: E501
148-
assert coro1._LRUCacheWrapper__cache is not coro2._LRUCacheWrapper__cache # type: ignore[attr-defined] # noqa: E501
146+
assert coro1._LRUCacheWrapper__cache != coro2._LRUCacheWrapper__cache # type: ignore[attr-defined]
147+
assert coro1._LRUCacheWrapper__cache.keys() == coro2._LRUCacheWrapper__cache.keys() # type: ignore[attr-defined]
148+
assert coro1._LRUCacheWrapper__cache is not coro2._LRUCacheWrapper__cache # type: ignore[attr-defined]
149149

150150

151151
async def test_alru_cache_parameters() -> None:

tests/test_cache_clear.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,38 @@ async def coro(val: int) -> int:
1818
coro.cache_clear()
1919

2020
check_lru(coro, hits=0, misses=0, cache=0, tasks=0)
21+
22+
23+
async def test_cache_clear_pending_task() -> None:
24+
@alru_cache()
25+
async def coro() -> str:
26+
await asyncio.sleep(0.5)
27+
return "foo"
28+
29+
t = asyncio.create_task(coro())
30+
await asyncio.sleep(0)
31+
assert len(coro._LRUCacheWrapper__tasks) == 1 # type: ignore[attr-defined]
32+
inner_task = next(iter(coro._LRUCacheWrapper__tasks)) # type: ignore[attr-defined]
33+
assert not inner_task.done()
34+
35+
coro.cache_clear()
36+
await inner_task
37+
38+
assert await t == "foo"
39+
assert inner_task.done()
40+
41+
42+
async def test_cache_clear_ttl_callback(check_lru: Callable[..., None]) -> None:
43+
@alru_cache(ttl=0.5)
44+
async def coro() -> str:
45+
return "foo"
46+
47+
await coro()
48+
assert len(coro._LRUCacheWrapper__cache) == 1 # type: ignore[attr-defined]
49+
cache_item = next(iter(coro._LRUCacheWrapper__cache.values())) # type: ignore[attr-defined]
50+
assert not cache_item.later_call.cancelled()
51+
52+
coro.cache_clear()
53+
54+
assert cache_item.later_call.cancelled()
55+
await asyncio.sleep(0.5)

0 commit comments

Comments
 (0)