From: Carson Ip Date: Fri, 27 Dec 2019 17:05:30 +0000 (-0500) Subject: Fix WeakSequence circular reference X-Git-Tag: rel_1_4_0b1~581^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f7a7af70c4f6f07011fa2d521fb1560917896427;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Fix WeakSequence circular reference Fixed a reference cycle which could impact the GC behavior of the :class:`.WeakSequence` object, currently used within one place in certain mapper configurations. The issue only affects configuration-time structures. Pull request courtesy Carson Ip. Fixes: #5050 Closes: #5051 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5051 Pull-request-sha: db672f45f4f139722edd2dcc6b0c19892725c9de Change-Id: I72673a33e655c44b68283ec1a2d7358b904e90ae --- diff --git a/doc/build/changelog/unreleased_13/5050.rst b/doc/build/changelog/unreleased_13/5050.rst new file mode 100644 index 0000000000..9831406b83 --- /dev/null +++ b/doc/build/changelog/unreleased_13/5050.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: bug, orm + :tickets: 5050 + + Fixed a reference cycle which could impact the GC behavior of the + :class:`.WeakSequence` object, currently used within one place in certain + mapper configurations. The issue only affects configuration-time + structures. Pull request courtesy Carson Ip. + 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):