]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Fix WeakSequence circular reference 5051/head
authorCarson Ip <carsonip715@gmail.com>
Tue, 24 Dec 2019 04:55:14 +0000 (12:55 +0800)
committerCarson Ip <carsonip715@gmail.com>
Tue, 24 Dec 2019 08:45:00 +0000 (16:45 +0800)
WeakSequence's  _remove instance method had __self__ which caused a
circular reference. Use a weak selfref to break the cycle.

Fixes: #5050
lib/sqlalchemy/util/_collections.py
test/base/test_utils.py

index 75e1727df6d8dfd9b6bbd88c20ceb353d6aaac3a..04409cfd91c78e7e5ca7573fcfdfdc593b2d8215 100644 (file)
@@ -676,16 +676,18 @@ class IdentitySet(object):
 
 class WeakSequence(object):
     def __init__(self, __elements=()):
+        def _remove(item, selfref=weakref.ref(self)):
+            self = selfref()
+            if self is not None:
+                self._storage.remove(item)
+        self._remove = _remove
         self._storage = [
-            weakref.ref(element, self._remove) for element in __elements
+            weakref.ref(element, _remove) for element in __elements
         ]
 
     def append(self, item):
         self._storage.append(weakref.ref(item, self._remove))
 
-    def _remove(self, ref):
-        self._storage.remove(ref)
-
     def __len__(self):
         return len(self._storage)
 
index e4d5a4d5fb36ff05a7746556d024dd748da1547a..7cdda0c23998d01bdb88a44d32da3765f6cdf455 100644 (file)
@@ -188,6 +188,19 @@ class WeakSequenceTest(fixtures.TestBase):
         eq_(len(w), 2)
         eq_(len(w._storage), 2)
 
+    @testing.requires.predictable_gc
+    def test_cleanout_container(self):
+        import weakref
+
+        class Foo(object):
+            pass
+
+        f = Foo()
+        w = WeakSequence([f])
+        w_wref = weakref.ref(w)
+        del w
+        eq_(w_wref(), None)
+
 
 class OrderedDictTest(fixtures.TestBase):
     def test_odict(self):