]> granicus.if.org Git - python/commitdiff
Merged revisions 85392 via svnmerge from
authorBenjamin Peterson <benjamin@python.org>
Tue, 12 Oct 2010 23:06:22 +0000 (23:06 +0000)
committerBenjamin Peterson <benjamin@python.org>
Tue, 12 Oct 2010 23:06:22 +0000 (23:06 +0000)
svn+ssh://pythondev@svn.python.org/python/branches/py3k

........
  r85392 | benjamin.peterson | 2010-10-12 17:57:59 -0500 (Tue, 12 Oct 2010) | 1 line

  prefer clearing global objects to obscure module.__dict__ bugs #10068
........

Doc/reference/datamodel.rst
Lib/test/test_module.py
Misc/NEWS
Objects/moduleobject.c

index 3d1d26d3fcc524867968e09313caa7105d138979..9643f2b79e385ce0b8f329d23ae87d37c16c5faf 100644 (file)
@@ -654,6 +654,13 @@ Modules
    Special read-only attribute: :attr:`__dict__` is the module's namespace as a
    dictionary object.
 
+   .. impl-detail::
+
+      Because of the way CPython clears module dictionaries, the module
+      dictionary will be cleared when the module falls out of scope even if the
+      dictionary still has live references.  To avoid this, copy the dictionary
+      or keep the module around while using its dictionary directly.
+
    .. index::
       single: __name__ (module attribute)
       single: __doc__ (module attribute)
index 21ddc9af6fe0a5ca24e7eb078969a88538c8c350..7734fb04a260e694b7241704b36a3ae872296458 100644 (file)
@@ -1,6 +1,6 @@
 # Test the module type
 import unittest
-from test.support import run_unittest
+from test.support import run_unittest, gc_collect
 
 import sys
 ModuleType = type(sys)
@@ -55,14 +55,29 @@ class ModuleTests(unittest.TestCase):
               {"__name__": "foo", "__doc__": "foodoc", "bar": 42})
         self.assertTrue(foo.__dict__ is d)
 
+    @unittest.expectedFailure
     def test_dont_clear_dict(self):
         # See issue 7140.
         def f():
             foo = ModuleType("foo")
             foo.bar = 4
             return foo
+        gc_collect()
         self.assertEqual(f().__dict__["bar"], 4)
 
+    def test_clear_dict_in_ref_cycle(self):
+        destroyed = []
+        m = ModuleType("foo")
+        m.destroyed = destroyed
+        s = """class A:
+    def __del__(self):
+        destroyed.append(1)
+a = A()"""
+        exec(s, m.__dict__)
+        del m
+        gc_collect()
+        self.assertEqual(destroyed, [1])
+
 def test_main():
     run_unittest(ModuleTests)
 
index 4f45059847deb2a78586effafa69dfec85646090..f4ba9db8050031dc4591e835119c6dac00178a84 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -46,6 +46,9 @@ Core and Builtins
 
 - Issue #83755:  Implicit set-to-frozenset conversion was not thread-safe.
 
+- Issue #10068: Global objects which have reference cycles with their module's
+  dict are now cleared again. This causes issue #7140 to appear again.
+
 - Issue #9416: Fix some issues with complex formatting where the
   output with no type specifier failed to match the str output:
 
index def37810b8f421d22e953758c18af8689b981eef..5dd9c017e05a201ace075a0510fb521223776268 100644 (file)
@@ -312,10 +312,7 @@ module_dealloc(PyModuleObject *m)
     if (m->md_def && m->md_def->m_free)
         m->md_def->m_free(m);
     if (m->md_dict != NULL) {
-        /* If we are the only ones holding a reference, we can clear
-           the dictionary. */
-        if (Py_REFCNT(m->md_dict) == 1)
-            _PyModule_Clear((PyObject *)m);
+        _PyModule_Clear((PyObject *)m);
         Py_DECREF(m->md_dict);
     }
     if (m->md_state != NULL)