Issue #25447: The lru_cache() wrapper objects now can be copied and pickled
authorSerhiy Storchaka <storchaka@gmail.com>
Sat, 24 Oct 2015 06:49:56 +0000 (09:49 +0300)
committerSerhiy Storchaka <storchaka@gmail.com>
Sat, 24 Oct 2015 06:49:56 +0000 (09:49 +0300)
(by returning the original object unchanged).

Lib/test/test_functools.py
Misc/NEWS
Modules/_functoolsmodule.c

index 7ecf877b11de22b9acb3eef29f13961ce8d86ed3..d822b2de4578926d6c5ab48aa7c217e96dab6e46 100644 (file)
@@ -1,5 +1,6 @@
 import abc
 import collections
+import copy
 from itertools import permutations
 import pickle
 from random import choice
@@ -1251,11 +1252,64 @@ class TestLRU:
         self.assertEqual(b.f.cache_info(), X.f.cache_info())
         self.assertEqual(c.f.cache_info(), X.f.cache_info())
 
-class TestLRUC(TestLRU, unittest.TestCase):
-    module = c_functools
+    def test_pickle(self):
+        cls = self.__class__
+        for f in cls.cached_func[0], cls.cached_meth, cls.cached_staticmeth:
+            for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+                with self.subTest(proto=proto, func=f):
+                    f_copy = pickle.loads(pickle.dumps(f, proto))
+                    self.assertIs(f_copy, f)
+
+    def test_copy(self):
+        cls = self.__class__
+        for f in cls.cached_func[0], cls.cached_meth, cls.cached_staticmeth:
+            with self.subTest(func=f):
+                f_copy = copy.copy(f)
+                self.assertIs(f_copy, f)
+
+    def test_deepcopy(self):
+        cls = self.__class__
+        for f in cls.cached_func[0], cls.cached_meth, cls.cached_staticmeth:
+            with self.subTest(func=f):
+                f_copy = copy.deepcopy(f)
+                self.assertIs(f_copy, f)
+
+
+@py_functools.lru_cache()
+def py_cached_func(x, y):
+    return 3 * x + y
+
+@c_functools.lru_cache()
+def c_cached_func(x, y):
+    return 3 * x + y
+
 
 class TestLRUPy(TestLRU, unittest.TestCase):
     module = py_functools
+    cached_func = py_cached_func,
+
+    @module.lru_cache()
+    def cached_meth(self, x, y):
+        return 3 * x + y
+
+    @staticmethod
+    @module.lru_cache()
+    def cached_staticmeth(x, y):
+        return 3 * x + y
+
+
+class TestLRUC(TestLRU, unittest.TestCase):
+    module = c_functools
+    cached_func = c_cached_func,
+
+    @module.lru_cache()
+    def cached_meth(self, x, y):
+        return 3 * x + y
+
+    @staticmethod
+    @module.lru_cache()
+    def cached_staticmeth(x, y):
+        return 3 * x + y
 
 
 class TestSingleDispatch(unittest.TestCase):
index 30d5f5e7b9b5663614a4f71fe985902e4600329f..6a24291935547a3ae1d35fb6a15b92712093f0b2 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -45,6 +45,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #25447: The lru_cache() wrapper objects now can be copied and pickled
+  (by returning the original object unchanged).
+
 - Issue #25390: typing: Don't crash on Union[str, Pattern].
 
 - Issue #25441: asyncio: Raise error from drain() when socket is closed.
index 1f9806728f53c351bd17642b1e9d5bd2de39b3c6..fadc0a9c2057681aff2c799ba4a7efa2c276db15 100644 (file)
@@ -1047,6 +1047,12 @@ lru_cache_cache_clear(lru_cache_object *self, PyObject *unused)
     Py_RETURN_NONE;
 }
 
+static PyObject *
+lru_cache_reduce(PyObject *self, PyObject *unused)
+{
+    return PyObject_GetAttrString(self, "__qualname__");
+}
+
 static int
 lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg)
 {
@@ -1097,6 +1103,7 @@ cache_info_type:    namedtuple class with the fields:\n\
 static PyMethodDef lru_cache_methods[] = {
     {"cache_info", (PyCFunction)lru_cache_cache_info, METH_NOARGS},
     {"cache_clear", (PyCFunction)lru_cache_cache_clear, METH_NOARGS},
+    {"__reduce__", (PyCFunction)lru_cache_reduce, METH_NOARGS},
     {NULL}
 };