Skip to content

Commit 18e5930

Browse files
committed
Fix #218: Fix and properly document @cachedmethod.cache_key handling.
1 parent 98ec79f commit 18e5930

3 files changed

Lines changed: 37 additions & 18 deletions

File tree

docs/index.rst

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -370,13 +370,10 @@ often called with the same arguments:
370370
with urllib.request.urlopen(url) as s:
371371
return s.read()
372372

373-
# make sure access to cache is synchronized
373+
# remove (pop) an individual cached PEP from the cache
374+
key = get_pep.cache_key(42)
374375
with get_pep.cache_lock:
375-
get_pep.cache.clear()
376-
377-
# always use the key function for accessing cache items
378-
with get_pep.cache_lock:
379-
get_pep.cache.pop(get_pep.cache_key(42), None)
376+
get_pep.cache.pop(key, None)
380377

381378
For the common use case of clearing or invalidating the cache, the
382379
decorator also provides a :func:`cache_clear()` function which
@@ -582,6 +579,15 @@ often called with the same arguments:
582579
>>> peps.get.cache_info()
583580
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)
584581

582+
>>> # remove an individual cached PEP from the cache
583+
>>> key = peps.get.cache_key(320)
584+
>>> with peps.get.cache_lock:
585+
... del peps.get.cache[key]
586+
587+
>>> peps.get.cache_info()
588+
CacheInfo(hits=3, misses=8, maxsize=32, currsize=7)
589+
590+
>>> # remove all cached PEPs and clear cache info
585591
>>> peps.get.cache_clear()
586592

587593
>>> peps.get.cache_info()
@@ -590,8 +596,9 @@ often called with the same arguments:
590596
The `key` function will be called as `key(self, *args, **kwargs)`
591597
to retrieve a suitable cache key. Note that the default `key`
592598
function, :func:`cachetools.keys.methodkey`, ignores its first
593-
argument, i.e. :const:`self`. This has mostly historical reasons,
594-
but also ensures that :const:`self` does not have to be hashable.
599+
implicit argument, i.e. :const:`self`. This has mostly historical
600+
reasons, but also ensures that :const:`self` does not have to be
601+
hashable.
595602

596603
You may provide a different `key` function,
597604
e.g. :func:`cachetools.keys.hashkey`, if you need :const:`self` to

src/cachetools/_cachedmethod.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def cache(self):
4848

4949
@property
5050
def cache_key(self):
51-
return self.__key
51+
return functools.partial(self.__key, self._obj)
5252

5353
@property
5454
def cache_lock(self):
@@ -153,7 +153,7 @@ def __call__(self, *args, **kwargs):
153153
cache = self.cache
154154
lock = self.cache_lock
155155
cond = self.cache_condition
156-
key = self.cache_key(self._obj, *args, **kwargs)
156+
key = self.cache_key(*args, **kwargs)
157157

158158
with lock:
159159
cond.wait_for(lambda: key not in self.__pending)
@@ -199,7 +199,7 @@ def __init__(self, obj):
199199
def __call__(self, *args, **kwargs):
200200
cache = self.cache
201201
lock = self.cache_lock
202-
key = self.cache_key(self._obj, *args, **kwargs)
202+
key = self.cache_key(*args, **kwargs)
203203
with lock:
204204
try:
205205
result = cache[key]
@@ -238,7 +238,7 @@ def __init__(self, obj):
238238

239239
def __call__(self, *args, **kwargs):
240240
cache = self.cache
241-
key = self.cache_key(self._obj, *args, **kwargs)
241+
key = self.cache_key(*args, **kwargs)
242242
try:
243243
result = cache[key]
244244
self.__hits += 1

tests/test_cachedmethod.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -370,24 +370,26 @@ def test_decorator_attributes(self):
370370
cached = Cached(cache)
371371

372372
self.assertIs(cached.get.cache, cache)
373-
self.assertIs(cached.get.cache_key, keys.methodkey)
374373
self.assertIs(cached.get.cache_lock, None)
375374
self.assertIs(cached.get.cache_condition, None)
375+
self.assertEqual(cached.get.cache_key(42), keys.methodkey(cached, 42))
376376

377377
self.assertIs(cached.get_lock.cache, cache)
378-
self.assertIs(cached.get_lock.cache_key, keys.methodkey)
379378
self.assertIs(cached.get_lock.cache_lock, cached.lock)
380379
self.assertIs(cached.get_lock.cache_condition, None)
380+
self.assertEqual(cached.get_lock.cache_key(42), keys.methodkey(cached, 42))
381381

382382
self.assertIs(cached.get_cond.cache, cache)
383-
self.assertIs(cached.get_cond.cache_key, keys.methodkey)
384383
self.assertIs(cached.get_cond.cache_lock, cached.cond)
385384
self.assertIs(cached.get_cond.cache_condition, cached.cond)
385+
self.assertEqual(cached.get_cond.cache_key(42), keys.methodkey(cached, 42))
386386

387387
self.assertIs(cached.get_lock_cond_info.cache, cache)
388-
self.assertIs(cached.get_lock_cond_info.cache_key, keys.methodkey)
389388
self.assertIs(cached.get_lock_cond_info.cache_lock, cached.lock)
390389
self.assertIs(cached.get_lock_cond_info.cache_condition, cached.cond)
390+
self.assertEqual(
391+
cached.get_lock_cond_info.cache_key(42), keys.methodkey(cached, 42)
392+
)
391393

392394
def test_decorator_clear(self):
393395
cache = self.cache(2)
@@ -603,15 +605,25 @@ def test_shared_cache(self):
603605
self.assertEqual(cached1.get_info.cache_info(), (0, 0, 2, 0))
604606
self.assertEqual(cached2.get_info.cache_info(), (0, 0, 2, 0))
605607

608+
# hits/misses are counted by instance
606609
self.assertEqual(cached1.get_info(0), 0)
607-
608610
self.assertEqual(cached1.get_info.cache_info(), (0, 1, 2, 1))
609611
self.assertEqual(cached2.get_info.cache_info(), (0, 0, 2, 1))
610612

613+
# default methodkey discards "self", so results will be shared
614+
# across instances
611615
self.assertEqual(cached2.get_info(0), 0)
612-
613616
self.assertEqual(cached1.get_info.cache_info(), (0, 1, 2, 1))
614617
self.assertEqual(cached2.get_info.cache_info(), (1, 0, 2, 1))
618+
self.assertEqual(cached1.get_info(0), 0)
619+
self.assertEqual(cached1.get_info.cache_info(), (1, 1, 2, 1))
620+
self.assertEqual(cached2.get_info.cache_info(), (1, 0, 2, 1))
621+
self.assertEqual(cached1.get_info(1), 1)
622+
self.assertEqual(cached1.get_info.cache_info(), (1, 2, 2, 2))
623+
self.assertEqual(cached2.get_info.cache_info(), (1, 0, 2, 2))
624+
self.assertEqual(cached2.get_info(1), 1)
625+
self.assertEqual(cached1.get_info.cache_info(), (1, 2, 2, 2))
626+
self.assertEqual(cached2.get_info.cache_info(), (2, 0, 2, 2))
615627

616628
def test_value_too_large(self):
617629
cache = self.cache(1, getsizeof=lambda x: x)

0 commit comments

Comments
 (0)