From 9437c6169e5ca4ed350b516d27e7c72e9f531efa Mon Sep 17 00:00:00 2001 From: msmol Date: Tue, 31 May 2016 17:08:37 -0400 Subject: [PATCH 1/4] Added fix for functions with only *args & **kwargs --- flask_cache/__init__.py | 83 +++++++++++++++++++++-------------------- test_cache.py | 19 ++++++++++ 2 files changed, 62 insertions(+), 40 deletions(-) diff --git a/flask_cache/__init__.py b/flask_cache/__init__.py index 85d90e3..63ccc06 100644 --- a/flask_cache/__init__.py +++ b/flask_cache/__init__.py @@ -418,46 +418,49 @@ def _memoize_kwargs_to_args(self, f, *args, **kwargs): argspec = inspect.getargspec(f) args_len = len(argspec.args) - for i in range(args_len): - if i == 0 and argspec.args[i] in ('self', 'cls'): - #: use the repr of the class instance - #: this supports instance methods for - #: the memoized functions, giving more - #: flexibility to developers - arg = repr(args[0]) - arg_num += 1 - elif argspec.args[i] in kwargs: - arg = kwargs[argspec.args[i]] - elif arg_num < len(args): - arg = args[arg_num] - arg_num += 1 - elif abs(i-args_len) <= len(argspec.defaults): - arg = argspec.defaults[i-args_len] - arg_num += 1 - else: - arg = None - arg_num += 1 - - #: Attempt to convert all arguments to a - #: hash/id or a representation? - #: Not sure if this is necessary, since - #: using objects as keys gets tricky quickly. - # if hasattr(arg, '__class__'): - # try: - # arg = hash(arg) - # except: - # arg = repr(arg) - - #: Or what about a special __cacherepr__ function - #: on an object, this allows objects to act normal - #: upon inspection, yet they can define a representation - #: that can be used to make the object unique in the - #: cache key. Given that a case comes across that - #: an object "must" be used as a cache key - # if hasattr(arg, '__cacherepr__'): - # arg = arg.__cacherepr__ - - new_args.append(arg) + if args_len: + for i in range(args_len): + if i == 0 and argspec.args[i] in ('self', 'cls'): + #: use the repr of the class instance + #: this supports instance methods for + #: the memoized functions, giving more + #: flexibility to developers + arg = repr(args[0]) + arg_num += 1 + elif argspec.args[i] in kwargs: + arg = kwargs[argspec.args[i]] + elif arg_num < len(args): + arg = args[arg_num] + arg_num += 1 + elif abs(i-args_len) <= len(argspec.defaults): + arg = argspec.defaults[i-args_len] + arg_num += 1 + else: + arg = None + arg_num += 1 + + #: Attempt to convert all arguments to a + #: hash/id or a representation? + #: Not sure if this is necessary, since + #: using objects as keys gets tricky quickly. + # if hasattr(arg, '__class__'): + # try: + # arg = hash(arg) + # except: + # arg = repr(arg) + + #: Or what about a special __cacherepr__ function + #: on an object, this allows objects to act normal + #: upon inspection, yet they can define a representation + #: that can be used to make the object unique in the + #: cache key. Given that a case comes across that + #: an object "must" be used as a cache key + # if hasattr(arg, '__cacherepr__'): + # arg = arg.__cacherepr__ + + new_args.append(arg) + else: + new_args = args return tuple(new_args), {} diff --git a/test_cache.py b/test_cache.py index 9811572..acd7fab 100644 --- a/test_cache.py +++ b/test_cache.py @@ -164,6 +164,25 @@ def big_foo(a, b): assert big_foo(5, 2) == result + def test_06b_memoize_func_only_special_args(self): + self.app.config['CACHE_DEFAULT_TIMEOUT'] = 1 + self.cache = Cache(self.app) + + with self.app.test_request_context(): + @self.cache.memoize(10) + def big_foo(*args, **kwargs): + return sum(args) + + res_1 = big_foo(1, 2, 3) + + time.sleep(1) + + res_2 = big_foo(4, 5, 6) + + assert res_1 != res_2 + assert res_1 == 6 + assert res_2 == 15 + def test_07_delete_memoize(self): with self.app.test_request_context(): From 19f43056864dec99b96c7c15e288f42df0201a70 Mon Sep 17 00:00:00 2001 From: Mitch Smolash Date: Wed, 1 Jun 2016 22:57:14 -0400 Subject: [PATCH 2/4] Update setup.py bump version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7f75223..17928fb 100755 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name='Flask-Cache', - version='0.13', + version='0.14', url='http://github.com/thadeusb/flask-cache', license='BSD', author='Thadeus Burgess', From aacbefc4322ae1f9554e4163da91c44a7cf4b63d Mon Sep 17 00:00:00 2001 From: msmol Date: Thu, 2 Jun 2016 14:33:25 -0400 Subject: [PATCH 3/4] changed inspect.argspec to inspect.signature and told it to follow wraps --- flask_cache/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask_cache/__init__.py b/flask_cache/__init__.py index 63ccc06..2f909cc 100644 --- a/flask_cache/__init__.py +++ b/flask_cache/__init__.py @@ -42,7 +42,7 @@ def function_namespace(f, args=None): """ Attempts to returns unique namespace for function """ - m_args = inspect.getargspec(f)[0] + m_args = inspect.signature(f, follow_wrapped=True)[0] instance_token = None instance_self = getattr(f, '__self__', None) @@ -415,7 +415,7 @@ def _memoize_kwargs_to_args(self, f, *args, **kwargs): #: 1, b=2 is equivilant to a=1, b=2, etc. new_args = [] arg_num = 0 - argspec = inspect.getargspec(f) + argspec = inspect.signature(f, follow_wrapped=True) args_len = len(argspec.args) if args_len: From 9ab5edb2e31c37399f0ee7dbf8fe2b1353b367bd Mon Sep 17 00:00:00 2001 From: msmol Date: Thu, 2 Jun 2016 16:35:40 -0400 Subject: [PATCH 4/4] Python3 only, tests passing --- README | 4 ++- flask_cache/__init__.py | 63 ++++++++++------------------------------- test_cache.py | 2 +- tox.ini | 2 +- 4 files changed, 20 insertions(+), 51 deletions(-) diff --git a/README b/README index cc45c70..789847b 100644 --- a/README +++ b/README @@ -1,3 +1,5 @@ Flask-Cache -Adds easy cache support to Flask. +Adds easy cache support to Flask. Fork to support nesting decorators. + +Python3.x only diff --git a/flask_cache/__init__.py b/flask_cache/__init__.py index 2f909cc..2927abb 100644 --- a/flask_cache/__init__.py +++ b/flask_cache/__init__.py @@ -42,7 +42,7 @@ def function_namespace(f, args=None): """ Attempts to returns unique namespace for function """ - m_args = inspect.signature(f, follow_wrapped=True)[0] + m_args = list(inspect.signature(f).parameters.keys()) instance_token = None instance_self = getattr(f, '__self__', None) @@ -414,53 +414,20 @@ def _memoize_kwargs_to_args(self, f, *args, **kwargs): #: whether the function was called with #: 1, b=2 is equivilant to a=1, b=2, etc. new_args = [] - arg_num = 0 - argspec = inspect.signature(f, follow_wrapped=True) - - args_len = len(argspec.args) - if args_len: - for i in range(args_len): - if i == 0 and argspec.args[i] in ('self', 'cls'): - #: use the repr of the class instance - #: this supports instance methods for - #: the memoized functions, giving more - #: flexibility to developers - arg = repr(args[0]) - arg_num += 1 - elif argspec.args[i] in kwargs: - arg = kwargs[argspec.args[i]] - elif arg_num < len(args): - arg = args[arg_num] - arg_num += 1 - elif abs(i-args_len) <= len(argspec.defaults): - arg = argspec.defaults[i-args_len] - arg_num += 1 - else: - arg = None - arg_num += 1 - - #: Attempt to convert all arguments to a - #: hash/id or a representation? - #: Not sure if this is necessary, since - #: using objects as keys gets tricky quickly. - # if hasattr(arg, '__class__'): - # try: - # arg = hash(arg) - # except: - # arg = repr(arg) - - #: Or what about a special __cacherepr__ function - #: on an object, this allows objects to act normal - #: upon inspection, yet they can define a representation - #: that can be used to make the object unique in the - #: cache key. Given that a case comes across that - #: an object "must" be used as a cache key - # if hasattr(arg, '__cacherepr__'): - # arg = arg.__cacherepr__ - - new_args.append(arg) - else: - new_args = args + + signature = inspect.signature(f) + processed_args = 0 + for i, key in enumerate(signature.parameters): + if key in ['self', 'cls'] and i == 0: + new_args.append(repr(args[i])) + processed_args += 1 + elif key in kwargs: + new_args.append(kwargs[key]) + elif processed_args < len(args): + new_args.append(args[processed_args]) + processed_args += 1 + else: + new_args.append(signature.parameters[key].default) return tuple(new_args), {} diff --git a/test_cache.py b/test_cache.py index acd7fab..4ec83ff 100644 --- a/test_cache.py +++ b/test_cache.py @@ -7,7 +7,7 @@ import string from flask import Flask, render_template, render_template_string -from flask.ext.cache import Cache, function_namespace, make_template_fragment_key +from flask_cache import Cache, function_namespace, make_template_fragment_key if sys.version_info < (2,7): import unittest2 as unittest diff --git a/tox.ini b/tox.ini index f659f43..2569f7e 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py33 +envlist = py33,py34,py35 [testenv] deps = -r{toxinidir}/requirements.txt