Invalidating cached values in "cachemethod"
ShivKJ opened this issue · 10 comments
Hi,
Is there a way to update/remove cache value returned by cachemethod
?
For example, in the below code,
import operator as op
from dataclasses import dataclass, field
from cachetools import TTLCache, cachedmethod
@dataclass
class A:
cache: TTLCache = field(init=False)
def __post_init__(self):
self.cache = TTLCache(maxsize=32, ttl=2 * 3600)
@cachedmethod(cache=op.attrgetter('cache'))
def compute(self, i):
print('value of i', i)
return i * 2
Suppose, I want to remove cached value for a given i
from cache, then it is not possible to do directly.
Meanwhile, I learned about hashkey
function in keys
module. But it is not exposed in cachetools __init__
, so can not be imported (of course there are hacks around it). Using this, we can updated cached value.
So, I think there could be two solutions to it,
- As
cachemethod
wraps a method to enable caching feature in it, so apart from the caching feature, it can add functionality to invalidate. For example,
a = A()
print(a.compute(10))
a.compute.remove(10) # remove can have signature *args, **kwargs. Instead of word `remove`, a more better function name can be thought
- Exposing
keys
module in cachetools__init__.py
.
from cachetools.keys import hashkey
a = A()
i = 10
print(a.compute(i))
del a.cache[hashkey(i)]
I think approach 1 would be more user friendly.
There is an example for removing individual cache items using hashkey
in the @cached
decorator docs: https://cachetools.readthedocs.io/en/stable/#cachetools.cached
Maybe I should state explicitly that this work similar for @cachedmethod
.
However, I agree that it is quite cumbersome to use as it is. OTOH, my usual advice for anybody trying to do fancy stuff with the @cached
or @cachedmethod
decorators is: Don't. Use the decorators for the most simple use cases, for which they were designed. If you think you need to do something that requires more control of the underlying cache object, just access/query the cache object directly.
I am in similar situation. Is there any way to delete a specific cache key from TTLCache. The key in TTLCache is stored as tuple
.
TTLCache({(<class 'cachetools.keys._HashedTuple'>, 'token', 'd4e498d636e76dd3ea19fb727dc8f828'): TokenClass object at 0x000001F939670250>}
How do I reconstruct this cache key to pop its value?
token_function.cache.pop(token_function.cache_key(?), None)
When I tried to reconstruct the tuple to pass it as a key, it raises KeyError
.
token_function.cache.pop(token_function.cache_key('token', 'd4e498d636e76dd3ea19fb727dc8f828'), None)
I'm not able to reconstruct the first element of this tuple <class 'cachetools.keys._HashedTuple'
.
@infohash: Are you talking about the @cached
or the @cachedmethod
decorator?
@cachedmethod
properties are still considered somewhat "experimental", and are therefore not properly documented yet.
For @cached
, the arguments to the cache_key
function are simply the same as for the wrapped function, i.e. not a tuple:
from cachetools import TTLCache, cached
cache = TTLCache(maxsize=1024, ttl=600)
@cached(cache=cache)
def token_function(token, id):
return (token, id)
print(token_function('token', 'd4e498d636e76dd3ea19fb727dc8f828'))
print(cache)
token_function.cache.pop(token_function.cache_key('token', 'd4e498d636e76dd3ea19fb727dc8f828'), None)
print(cache)
I am using @cachedmethod
because the method that I am applying cache on is an instance method of a class and I initialize TTLCache inside the init method.
from cachetools import TTLCache, cachedmethod
class TokenClass:
def __init__(self):
self.cache = TTLCache(maxsize=128, ttl=300)
@cachedmethod(cache=lambda self: self.cache)
def token_function(self, token):
...
It works fine for storing the cache, the problem happens when I try to remove a specific key from it. I have changed my design so I don't need to pop a key from TTLCache anymore. But it's something you can check because it almost works.
So, the token
argument is actually a tuple?
Bear in mind that cache
in @cachedmethod
is actually a method itself (i.e. needs passing self
), and the cache_key
gets passed self
as its first argument:
from cachetools import TTLCache, cachedmethod
class TokenClass:
def __init__(self):
self.cache = TTLCache(maxsize=128, ttl=300)
@cachedmethod(cache=lambda self: self.cache)
def token_function(self, token):
return token
tc = TokenClass()
print(tc.token_function(('token', 'd4e498d636e76dd3ea19fb727dc8f828')))
print(tc.token_function.cache(tc))
tc.token_function.cache(tc).pop(tc.token_function.cache_key(tc, ('token', 'd4e498d636e76dd3ea19fb727dc8f828')), None)
print(tc.token_function.cache(tc))
It's cumbersome, and I'm not too happy with it myself, so it will probably remain an experimental, undocumented feature until someone comes up with something better (or somebody provides sensible documentation for this)...