From 65381bcd81b030bb5dcd034e111a8e04f27aac52 Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Thu, 31 Oct 2019 01:06:50 +0800 Subject: [PATCH 1/6] Add new cache_parameters() method for functools.lru_cache() to better support pickling --- Doc/library/functools.rst | 7 ++++++- Lib/functools.py | 3 ++- Lib/test/test_functools.py | 13 +++++++++++++ .../2019-10-24-08-10-30.bpo-38565.SWSUst.rst | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-10-24-08-10-30.bpo-38565.SWSUst.rst diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index d3debac8432b19..0f2193a6eb1984 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -111,7 +111,9 @@ The :mod:`functools` module defines the following functions: To help measure the effectiveness of the cache and tune the *maxsize* parameter, the wrapped function is instrumented with a :func:`cache_info` function that returns a :term:`named tuple` showing *hits*, *misses*, - *maxsize* and *currsize*. In a multi-threaded environment, the hits + *maxsize* and *currsize*. the wrapped function is also instrumented woth + a :func:`cache_parameters` function that returns a :class:`dict` which has + showing *maxsize* and *typed*. In a multi-threaded environment, the hits and misses are approximate. The decorator also provides a :func:`cache_clear` function for clearing or @@ -178,6 +180,9 @@ The :mod:`functools` module defines the following functions: .. versionchanged:: 3.8 Added the *user_function* option. + .. versionadded:: 3.9 + Added the function :func:`cache_parameters` + .. decorator:: total_ordering Given a class defining one or more rich comparison ordering methods, this diff --git a/Lib/functools.py b/Lib/functools.py index 3192bd02d93e56..f720a290792d2e 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -499,6 +499,7 @@ def lru_cache(maxsize=128, typed=False): # The user_function was passed in directly via the maxsize argument user_function, maxsize = maxsize, 128 wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo) + wrapper.cache_parameters = lambda: {'maxsize': maxsize, 'typed': typed} return update_wrapper(wrapper, user_function) elif maxsize is not None: raise TypeError( @@ -506,6 +507,7 @@ def lru_cache(maxsize=128, typed=False): def decorating_function(user_function): wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo) + wrapper.cache_parameters = lambda: {'maxsize': maxsize, 'typed': typed} return update_wrapper(wrapper, user_function) return decorating_function @@ -621,7 +623,6 @@ def cache_clear(): root[:] = [root, root, None, None] hits = misses = 0 full = False - wrapper.cache_info = cache_info wrapper.cache_clear = cache_clear return wrapper diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index c300270d49e5ec..9035bdc31929ef 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1655,6 +1655,19 @@ def orig(x, y): f_copy = copy.deepcopy(f) self.assertIs(f_copy, f) + def test_lru_cache_parameters(self): + def f(): + return 1 + f = self.module.lru_cache(2)(f) + self.assertEqual(f.cache_parameters().get("maxsize"), 2) + self.assertEqual(f.cache_parameters().get("typed"), False) + + @self.module.lru_cache(maxsize=1000, typed=True) + def f(): + return 1 + self.assertEqual(f.cache_parameters().get("maxsize"), 1000) + self.assertEqual(f.cache_parameters().get("typed"), True) + @py_functools.lru_cache() def py_cached_func(x, y): diff --git a/Misc/NEWS.d/next/Library/2019-10-24-08-10-30.bpo-38565.SWSUst.rst b/Misc/NEWS.d/next/Library/2019-10-24-08-10-30.bpo-38565.SWSUst.rst new file mode 100644 index 00000000000000..34d7afcf36df62 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-10-24-08-10-30.bpo-38565.SWSUst.rst @@ -0,0 +1 @@ +Add new cache_parameters() method for functools.lru_cache() to better support pickling. \ No newline at end of file From 7a2d731a1f7b60ed4d84b2dcedbf865146e8f9ad Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 11 Nov 2019 22:05:32 -0800 Subject: [PATCH 2/6] Improve wording. Fix spelling and spacing --- Doc/library/functools.rst | 11 +++++++---- Lib/functools.py | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 0f2193a6eb1984..ec7680f1ad798a 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -108,13 +108,16 @@ The :mod:`functools` module defines the following functions: cached separately. For example, ``f(3)`` and ``f(3.0)`` will be treated as distinct calls with distinct results. + The wrapped function is instrumented with a :func:`cache_parameters` + function that returns a new :class:`dict` showing the values for *maxsize* + and *typed*. This is for information purposes only. Mutating the values + has no effect. + To help measure the effectiveness of the cache and tune the *maxsize* parameter, the wrapped function is instrumented with a :func:`cache_info` function that returns a :term:`named tuple` showing *hits*, *misses*, - *maxsize* and *currsize*. the wrapped function is also instrumented woth - a :func:`cache_parameters` function that returns a :class:`dict` which has - showing *maxsize* and *typed*. In a multi-threaded environment, the hits - and misses are approximate. + *maxsize* and *currsize*. In a multi-threaded environment, the hits and + misses are approximate. The decorator also provides a :func:`cache_clear` function for clearing or invalidating the cache. diff --git a/Lib/functools.py b/Lib/functools.py index f720a290792d2e..0e8e429c301e62 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -499,7 +499,7 @@ def lru_cache(maxsize=128, typed=False): # The user_function was passed in directly via the maxsize argument user_function, maxsize = maxsize, 128 wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo) - wrapper.cache_parameters = lambda: {'maxsize': maxsize, 'typed': typed} + wrapper.cache_parameters = lambda: {'maxsize': maxsize, 'typed': typed} return update_wrapper(wrapper, user_function) elif maxsize is not None: raise TypeError( From acd00a972d77af51ef09da699e3864a4fd795d32 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 11 Nov 2019 22:13:29 -0800 Subject: [PATCH 3/6] Clean-up and improve tests --- Lib/functools.py | 2 +- Lib/test/test_functools.py | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py index 0e8e429c301e62..5706042efb2f04 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -499,7 +499,7 @@ def lru_cache(maxsize=128, typed=False): # The user_function was passed in directly via the maxsize argument user_function, maxsize = maxsize, 128 wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo) - wrapper.cache_parameters = lambda: {'maxsize': maxsize, 'typed': typed} + wrapper.cache_parameters = lambda : {'maxsize': maxsize, 'typed': typed} return update_wrapper(wrapper, user_function) elif maxsize is not None: raise TypeError( diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 9035bdc31929ef..a97ca398e77a33 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1656,17 +1656,15 @@ def orig(x, y): self.assertIs(f_copy, f) def test_lru_cache_parameters(self): + @self.module.lru_cache(maxsize=2) def f(): return 1 - f = self.module.lru_cache(2)(f) - self.assertEqual(f.cache_parameters().get("maxsize"), 2) - self.assertEqual(f.cache_parameters().get("typed"), False) + self.assertEqual(f.cache_parameters(), {'maxsize': 2, "typed": False}) @self.module.lru_cache(maxsize=1000, typed=True) def f(): return 1 - self.assertEqual(f.cache_parameters().get("maxsize"), 1000) - self.assertEqual(f.cache_parameters().get("typed"), True) + self.assertEqual(f.cache_parameters(), {'maxsize': 1000, "typed": True}) @py_functools.lru_cache() From d9a8207db20a7bf6cc1ed958b5a7e036b11b6f24 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 11 Nov 2019 22:17:38 -0800 Subject: [PATCH 4/6] Remove unintentional rewrap --- Doc/library/functools.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index ec7680f1ad798a..cedc3ad5ec0a61 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -116,8 +116,8 @@ The :mod:`functools` module defines the following functions: To help measure the effectiveness of the cache and tune the *maxsize* parameter, the wrapped function is instrumented with a :func:`cache_info` function that returns a :term:`named tuple` showing *hits*, *misses*, - *maxsize* and *currsize*. In a multi-threaded environment, the hits and - misses are approximate. + *maxsize* and *currsize*. In a multi-threaded environment, the hits + and misses are approximate. The decorator also provides a :func:`cache_clear` function for clearing or invalidating the cache. From a9c74501975f32567409547b8b346d8ebe012e2b Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 11 Nov 2019 22:19:36 -0800 Subject: [PATCH 5/6] Keep consistent spacing --- Lib/functools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/functools.py b/Lib/functools.py index 5706042efb2f04..d3d2ec47e6d658 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -507,7 +507,7 @@ def lru_cache(maxsize=128, typed=False): def decorating_function(user_function): wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo) - wrapper.cache_parameters = lambda: {'maxsize': maxsize, 'typed': typed} + wrapper.cache_parameters = lambda : {'maxsize': maxsize, 'typed': typed} return update_wrapper(wrapper, user_function) return decorating_function From df9a513e6469045dfc55aef7e020adf2f74f29ac Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 11 Nov 2019 22:20:39 -0800 Subject: [PATCH 6/6] Restore line spacing --- Lib/functools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/functools.py b/Lib/functools.py index d3d2ec47e6d658..2c01b2e59524bf 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -623,6 +623,7 @@ def cache_clear(): root[:] = [root, root, None, None] hits = misses = 0 full = False + wrapper.cache_info = cache_info wrapper.cache_clear = cache_clear return wrapper