From 0649ec91c90011de7064a41b8fc3fa6a76f8e062 Mon Sep 17 00:00:00 2001 From: "[6~" Date: Fri, 5 Jun 2020 10:36:48 -0500 Subject: [PATCH 1/6] Deprecate memoize_method_with_uncached --- pytools/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pytools/__init__.py b/pytools/__init__.py index 0567920..9989d57 100644 --- a/pytools/__init__.py +++ b/pytools/__init__.py @@ -66,7 +66,6 @@ Memoization .. autofunction:: memoize .. autofunction:: memoize_on_first_arg .. autofunction:: memoize_method -.. autofunction:: memoize_method_with_uncached .. autofunction:: memoize_in Argmin/max @@ -634,6 +633,11 @@ def memoize_method_with_uncached(uncached_args=None, uncached_kwargs=None): :arg uncached_args: a list of argument numbers (0-based, not counting 'self' argument) """ + from warnings import warn + warn("memoize_method_with_uncached is deprecated and will go away in 2022. " + "Use memoize_method_with_key instead", + DeprecationWarning, + stacklevel=2) if uncached_args is None: uncached_args = [] -- GitLab From 655e5b46a80916a1bfd20b64680dca3ffeded084 Mon Sep 17 00:00:00 2001 From: "[6~" Date: Fri, 5 Jun 2020 10:37:21 -0500 Subject: [PATCH 2/6] Remove some Py2.5 compatibility notes --- pytools/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pytools/__init__.py b/pytools/__init__.py index 9989d57..852e8d0 100644 --- a/pytools/__init__.py +++ b/pytools/__init__.py @@ -581,9 +581,6 @@ def memoize_on_first_arg(function, cache_dict_name=None): to do memoization as first argument. Supports cache deletion via ``function_name.clear_cache(self)``. - - .. note:: - *clear_cache* support requires Python 2.5 or newer. """ if cache_dict_name is None: @@ -619,9 +616,6 @@ def memoize_on_first_arg(function, cache_dict_name=None): def memoize_method(method: F) -> F: """Supports cache deletion via ``method_name.clear_cache(self)``. - - .. note:: - *clear_cache* support requires Python 2.5 or newer. """ return memoize_on_first_arg(method, intern("_memoize_dic_"+method.__name__)) -- GitLab From 5b01f96776ac1b54bb48c06a1fd03c46d1b80d45 Mon Sep 17 00:00:00 2001 From: "[6~" Date: Fri, 5 Jun 2020 10:38:06 -0500 Subject: [PATCH 3/6] Put a deadline on deprecation of memoize_method_nested --- pytools/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pytools/__init__.py b/pytools/__init__.py index 852e8d0..b066569 100644 --- a/pytools/__init__.py +++ b/pytools/__init__.py @@ -698,8 +698,9 @@ def memoize_method_nested(inner): """ from warnings import warn - warn("memoize_method_nested is deprecated. Use @memoize_in(self, 'identifier') " - "instead", DeprecationWarning, stacklevel=2) + warn("memoize_method_nested is deprecated and will go away in 2021. " + "Use @memoize_in(self, 'identifier') instead", DeprecationWarning, + stacklevel=2) from functools import wraps cache_dict_name = intern("_memoize_inner_dic_%s_%s_%d" -- GitLab From ce8f577a7228a57bf7077859531eea632c4f9147 Mon Sep 17 00:00:00 2001 From: "[6~" Date: Fri, 5 Jun 2020 10:38:45 -0500 Subject: [PATCH 4/6] Doc/comment improvements --- pytools/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytools/__init__.py b/pytools/__init__.py index b066569..b880534 100644 --- a/pytools/__init__.py +++ b/pytools/__init__.py @@ -561,6 +561,7 @@ def memoize(*args: F, **kwargs: Any) -> F: result = func(*args) func._memoize_dic[args] = result # pylint: disable=protected-access return result + if not args: return _deco if callable(args[0]) and len(args) == 1: @@ -578,7 +579,7 @@ class _HasKwargs(object): def memoize_on_first_arg(function, cache_dict_name=None): """Like :func:`memoize_method`, but for functions that take the object - to do memoization as first argument. + in which do memoization information is stored as first argument. Supports cache deletion via ``function_name.clear_cache(self)``. """ -- GitLab From 22686de27a9e4d3a1c5b23c38cb4407ee0d03e82 Mon Sep 17 00:00:00 2001 From: "[6~" Date: Fri, 5 Jun 2020 10:40:09 -0500 Subject: [PATCH 5/6] Gitignore mypy cache --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c11edd6..f9730d3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ distribute*egg distribute*tar.gz .cache +.mypy_cache -- GitLab From f7b9b63a327fd6b3d6c71749c95525d2330c1a61 Mon Sep 17 00:00:00 2001 From: "[6~" Date: Fri, 5 Jun 2020 10:40:43 -0500 Subject: [PATCH 6/6] Add keyed_memoize_{method,on_first_arg} --- pytools/__init__.py | 65 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/pytools/__init__.py b/pytools/__init__.py index b880534..24097cb 100644 --- a/pytools/__init__.py +++ b/pytools/__init__.py @@ -67,6 +67,8 @@ Memoization .. autofunction:: memoize_on_first_arg .. autofunction:: memoize_method .. autofunction:: memoize_in +.. autofunction:: keyed_memoize_on_first_arg +.. autofunction:: keyed_memoize_method Argmin/max ---------- @@ -622,6 +624,69 @@ def memoize_method(method: F) -> F: return memoize_on_first_arg(method, intern("_memoize_dic_"+method.__name__)) +class keyed_memoize_on_first_arg: # noqa: N801 + """Like :func:`memoize_method`, but for functions that take the object + in which memoization information is stored as first argument. + + Supports cache deletion via ``function_name.clear_cache(self)``. + + :arg key: A function receiving the same arguments as the decorated function + which computes and returns the cache key. + + .. versionadded :: 2020.3 + """ + + def __init__(self, key, cache_dict_name=None): + self.key = key + self.cache_dict_name = cache_dict_name + + def _default_cache_dict_name(self, function): + return intern("_memoize_dic_" + + function.__module__ + function.__name__) + + def __call__(self, function): + cache_dict_name = self.cache_dict_name + key = self.key + + if cache_dict_name is None: + cache_dict_name = self._default_cache_dict_name(function) + + def wrapper(obj, *args, **kwargs): + cache_key = key(*args, **kwargs) + + try: + return getattr(obj, cache_dict_name)[cache_key] + except AttributeError: + result = function(obj, *args, **kwargs) + setattr(obj, cache_dict_name, {cache_key: result}) + return result + except KeyError: + result = function(obj, *args, **kwargs) + getattr(obj, cache_dict_name)[cache_key] = result + return result + + def clear_cache(obj): + delattr(obj, cache_dict_name) + + from functools import update_wrapper + new_wrapper = update_wrapper(wrapper, function) + new_wrapper.clear_cache = clear_cache + + return new_wrapper + + +class keyed_memoize_method(keyed_memoize_on_first_arg): # noqa: N801 + """Supports cache deletion via ``method_name.clear_cache(self)``. + + :arg key: A function receiving the same arguments as the decorated function + which computes and returns the cache key. + + .. versionadded :: 2020.3 + """ + def _default_cache_dict_name(self, function): + return intern("_memoize_dic_" + function.__name__) + + def memoize_method_with_uncached(uncached_args=None, uncached_kwargs=None): """Supports cache deletion via ``method_name.clear_cache(self)``. -- GitLab