From 5c4bfc4af070c5c62f76cf3c2d23a19e7c794c4e Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 12 Oct 2010 22:57:59 +0000 Subject: prefer clearing global objects to obscure module.__dict__ bugs #10068 --- Doc/reference/datamodel.rst | 7 +++++++ Lib/test/test_module.py | 17 ++++++++++++++++- Misc/NEWS | 3 +++ Objects/moduleobject.c | 5 +---- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 9d084cf..d3b2c15 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -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) diff --git a/Lib/test/test_module.py b/Lib/test/test_module.py index 21ddc9a..7734fb0 100644 --- a/Lib/test/test_module.py +++ b/Lib/test/test_module.py @@ -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) diff --git a/Misc/NEWS b/Misc/NEWS index 8ff3f3b..0b7d77c 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -37,6 +37,9 @@ What's New in Python 3.2 Alpha 3? Core and Builtins ----------------- +- 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 #9738: Document PyErr_SetString() and PyErr_SetFromErrnoWithFilename() encodings. diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 3a95261..1e3349d 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -335,10 +335,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) -- cgit v0.12