]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-40504: Allow weakrefs to lru_cache objects (GH-19938)
authorDennis Sweeney <36520290+sweeneyde@users.noreply.github.com>
Tue, 5 May 2020 21:14:32 +0000 (17:14 -0400)
committerGitHub <noreply@github.com>
Tue, 5 May 2020 21:14:32 +0000 (14:14 -0700)
Lib/test/test_functools.py
Misc/NEWS.d/next/Library/2020-05-05-17-12-47.bpo-40504.EX6wPn.rst [new file with mode: 0644]
Modules/_functoolsmodule.c

index 9503f4086b1cb9f670ac6fb7e22963749bd8ba0c..b3893a15566fa67513c02c8c832ce7de6a7c74fe 100644 (file)
@@ -14,6 +14,8 @@ import typing
 import unittest
 import unittest.mock
 import os
+import weakref
+import gc
 from weakref import proxy
 import contextlib
 
@@ -1938,6 +1940,35 @@ class TestLRU:
             return 1
         self.assertEqual(f.cache_parameters(), {'maxsize': 1000, "typed": True})
 
+    def test_lru_cache_weakrefable(self):
+        @self.module.lru_cache
+        def test_function(x):
+            return x
+
+        class A:
+            @self.module.lru_cache
+            def test_method(self, x):
+                return (self, x)
+
+            @staticmethod
+            @self.module.lru_cache
+            def test_staticmethod(x):
+                return (self, x)
+
+        refs = [weakref.ref(test_function),
+                weakref.ref(A.test_method),
+                weakref.ref(A.test_staticmethod)]
+
+        for ref in refs:
+            self.assertIsNotNone(ref())
+
+        del A
+        del test_function
+        gc.collect()
+
+        for ref in refs:
+            self.assertIsNone(ref())
+
 
 @py_functools.lru_cache()
 def py_cached_func(x, y):
diff --git a/Misc/NEWS.d/next/Library/2020-05-05-17-12-47.bpo-40504.EX6wPn.rst b/Misc/NEWS.d/next/Library/2020-05-05-17-12-47.bpo-40504.EX6wPn.rst
new file mode 100644 (file)
index 0000000..261a49e
--- /dev/null
@@ -0,0 +1 @@
+:func:`functools.lru_cache` objects can now be the targets of weakrefs.
\ No newline at end of file
index fd4b4c268cc979a2201c9b93b9a83ae683183514..d158d3bae157b2429629e4c09be2f9ec5c1135f3 100644 (file)
@@ -783,6 +783,7 @@ typedef struct lru_cache_object {
     Py_ssize_t misses;
     PyObject *cache_info_type;
     PyObject *dict;
+    PyObject *weakreflist;
 } lru_cache_object;
 
 static PyTypeObject lru_cache_type;
@@ -1196,6 +1197,7 @@ lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw)
     Py_INCREF(cache_info_type);
     obj->cache_info_type = cache_info_type;
     obj->dict = NULL;
+    obj->weakreflist = NULL;
     return (PyObject *)obj;
 }
 
@@ -1227,6 +1229,8 @@ lru_cache_dealloc(lru_cache_object *obj)
     lru_list_elem *list;
     /* bpo-31095: UnTrack is needed before calling any callbacks */
     PyObject_GC_UnTrack(obj);
+    if (obj->weakreflist != NULL)
+        PyObject_ClearWeakRefs((PyObject*)obj);
 
     list = lru_cache_unlink_list(obj);
     Py_XDECREF(obj->cache);
@@ -1384,7 +1388,8 @@ static PyTypeObject lru_cache_type = {
     (traverseproc)lru_cache_tp_traverse,/* tp_traverse */
     (inquiry)lru_cache_tp_clear,        /* tp_clear */
     0,                                  /* tp_richcompare */
-    0,                                  /* tp_weaklistoffset */
+    offsetof(lru_cache_object, weakreflist),
+                                        /* tp_weaklistoffset */
     0,                                  /* tp_iter */
     0,                                  /* tp_iternext */
     lru_cache_methods,                  /* tp_methods */