]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
allow cycles throught the __dict__ slot to be cleared (closes #1469629)
authorBenjamin Peterson <benjamin@python.org>
Thu, 8 Mar 2012 00:41:11 +0000 (18:41 -0600)
committerBenjamin Peterson <benjamin@python.org>
Thu, 8 Mar 2012 00:41:11 +0000 (18:41 -0600)
Patch from Armin, test from me.

Lib/test/test_descr.py
Misc/NEWS
Objects/typeobject.c

index 92304b4e7ef51088723e5b91f6b2350d3de28d0b..4aeb77fd0f202a2bb80c42e8907c710adca39725 100644 (file)
@@ -1,8 +1,10 @@
 import builtins
+import gc
 import sys
 import types
 import math
 import unittest
+import weakref
 
 from copy import deepcopy
 from test import support
@@ -1186,7 +1188,6 @@ order (MRO) for bases """
         self.assertEqual(Counted.counter, 0)
 
         # Test lookup leaks [SF bug 572567]
-        import gc
         if hasattr(gc, 'get_objects'):
             class G(object):
                 def __eq__(self, other):
@@ -4380,7 +4381,6 @@ order (MRO) for bases """
         self.assertRaises(AttributeError, getattr, C(), "attr")
         self.assertEqual(descr.counter, 4)
 
-        import gc
         class EvilGetattribute(object):
             # This used to segfault
             def __getattr__(self, name):
@@ -4429,6 +4429,21 @@ order (MRO) for bases """
         foo = Foo()
         str(foo)
 
+    def test_cycle_through_dict(self):
+        # See bug #1469629
+        class X(dict):
+            def __init__(self):
+                dict.__init__(self)
+                self.__dict__ = self
+        x = X()
+        x.attr = 42
+        wr = weakref.ref(x)
+        del x
+        support.gc_collect()
+        self.assertIsNone(wr())
+        for o in gc.get_objects():
+            self.assertIsNot(type(o), X)
+
 class DictProxyTests(unittest.TestCase):
     def setUp(self):
         class C(object):
index 44683f5d5f3acf57ab1efbdfbc04cef9064a95d1..119dfd71bfb7bcd955f5a6a39aceb86991cb8605 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.2.3 release candidate 1?
 Core and Builtins
 -----------------
 
+- Issue #1469629: Allow cycles through an object's __dict__ slot to be
+  collected. (For example if ``x.__dict__ is x``).
+
 - Issue #14172: Fix reference leak when marshalling a buffer-like object
   (other than a bytes object).
 
index c3822abb0e065aa522da2c087c6ae7a653c5e770..e00669480590a5f89cd1f2379deb2a70b80d7561 100644 (file)
@@ -830,8 +830,13 @@ subtype_clear(PyObject *self)
         assert(base);
     }
 
-    /* There's no need to clear the instance dict (if any);
-       the collector will call its tp_clear handler. */
+    /* Clear the instance dict (if any), to break cycles involving only
+       __dict__ slots (as in the case 'self.__dict__ is self'). */
+    if (type->tp_dictoffset != base->tp_dictoffset) {
+        PyObject **dictptr = _PyObject_GetDictPtr(self);
+        if (dictptr && *dictptr)
+            Py_CLEAR(*dictptr);
+    }
 
     if (baseclear)
         return baseclear(self);