From db672f45f4f139722edd2dcc6b0c19892725c9de Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Tue, 24 Dec 2019 12:55:14 +0800 Subject: [PATCH] Fix WeakSequence circular reference 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 | 10 ++++++---- test/base/test_utils.py | 13 +++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/sqlalchemy/util/_collections.py b/lib/sqlalchemy/util/_collections.py index 75e1727df6..04409cfd91 100644 --- a/lib/sqlalchemy/util/_collections.py +++ b/lib/sqlalchemy/util/_collections.py @@ -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) diff --git a/test/base/test_utils.py b/test/base/test_utils.py index e4d5a4d5fb..7cdda0c239 100644 --- a/test/base/test_utils.py +++ b/test/base/test_utils.py @@ -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): -- 2.47.2