flask_cache.Cache.memoize
not working with flask_restful.Resource
Here is sample code:
from flask import Flask, request, jsonify
from flask_restful import Resource, Api
from flask_cache import Cache
app = Flask(__name__)
api = Api(app)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
class MyResource(Resource):
JSONIFY = True
PATH = None
ENDPOINT = None
def dispatch_request(self, *args, **kwargs):
kw = dict(**kwargs)
kw.update(request.args.items())
r = super().dispatch_request(*args, **kw)
if self.JSONIFY:
return jsonify(r)
else:
return r
class DebugResource(MyResource):
PATH = '/debug'
ENDPOINT = 'debug'
@cache.memoize(timeout=30)
def get(self, **kwargs):
print('cache is not used!')
return kwargs
for r in [DebugResource]:
api.add_resource(r, r.PATH, endpoint=r.ENDPOINT)
print('running!')
app.run()
Notice that in get()
I added print so I can see when the code is actually called and when cached value is used.
I start server then in browser i go to http://localhost:5000/debug?a=1
and press f5
repeatetely. I expect that my function get
is called once and then cached value is used. But in server console I see my print each time I press f5
. So memoize
is not working. What am I doing wrong?
edit:
I moved my cached function outside from Resource
class
@cache.memoize(timeout=30)
def my_foo(a):
print('cache is not used!')
return dict(kw=a, id=id(a))
class DebugResource(MyResource):
PATH = '/debug'
ENDPOINT = 'debug'
def get(self, a):
return my_foo(a)
and that worked. As far as I can see, the issue was self
argument that was actually unique in each call.
The question is still, how to make it work without extracting additional function for each method i want to cache? Current solution looks like a workaround.
The cache doesn't work because you use memoize method. In this case it will cache the result of a function. Decorator doesn't know anything about route(view, path).
To fix it you should use cached method. @cached
decorator has argument key_prefix
with default value = view/request.path
.
So, just change @cache.memoize(timeout=30)
to @cache.cached(timeout=30)
Thank @Rugnar, this decision came in handy.
The only point, I had to change it a bit, so that I would not exclude the first element (self), but use it, in order to store more unique keys in a situation where the cached method is defined in the base class, and in the children they are customized.
Method _extract_self_arg
updated.
class ResourceCache(Cache):
""" When the class method is being memoized,
cache key uses the class name from self or cls."""
def _memoize_make_cache_key(self, make_name=None, timeout=None):
def make_cache_key(f, *args, **kwargs):
fname, _ = function_namespace(f)
if callable(make_name):
altfname = make_name(fname)
else:
altfname = fname
updated = altfname + json.dumps(dict(
args=self._extract_self_arg(f, args),
kwargs=kwargs), sort_keys=True)
return b64encode(
md5(updated.encode('utf-8')).digest()
)[:16].decode('utf-8')
return make_cache_key
@staticmethod
def _extract_self_arg(f, args):
argspec_args = inspect.getargspec(f).args
if argspec_args and argspec_args[0] in ('self', 'cls'):
if hasattr(args[0], '__name__'):
return (args[0].__name__,) + args[1:]
return (args[0].__class__.__name__,) + args[1:]
return args
Maybe it will also be useful to someone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With