]> granicus.if.org Git - python/commitdiff
Issue #1545463: Global variables caught in reference cycles are now garbage-collected...
authorAntoine Pitrou <solipsis@pitrou.net>
Mon, 6 May 2013 19:15:57 +0000 (21:15 +0200)
committerAntoine Pitrou <solipsis@pitrou.net>
Mon, 6 May 2013 19:15:57 +0000 (21:15 +0200)
Include/pythonrun.h
Lib/test/test_gc.py
Misc/NEWS
Modules/gcmodule.c
Python/import.c
Python/pythonrun.c

index e8a582d50a5c4ce1f82ade3b6089044df0dcaacf..66766dd0bb81897ba2c68e68daf4467345008fb2 100644 (file)
@@ -217,6 +217,7 @@ PyAPI_FUNC(void) PyBytes_Fini(void);
 PyAPI_FUNC(void) PyByteArray_Fini(void);
 PyAPI_FUNC(void) PyFloat_Fini(void);
 PyAPI_FUNC(void) PyOS_FiniInterrupts(void);
+PyAPI_FUNC(void) _PyGC_DumpShutdownStats(void);
 PyAPI_FUNC(void) _PyGC_Fini(void);
 PyAPI_FUNC(void) PySlice_Fini(void);
 PyAPI_FUNC(void) _PyType_Fini(void);
index 85dbc97bb2a9940f932195b043e54ab82deda31f..6b52e5a66e0ee512fd12359e2a90311df773c02d 100644 (file)
@@ -1,6 +1,8 @@
 import unittest
 from test.support import (verbose, refcount_test, run_unittest,
                             strip_python_stderr)
+from test.script_helper import assert_python_ok, make_script, temp_dir
+
 import sys
 import time
 import gc
@@ -610,6 +612,40 @@ class GCTests(unittest.TestCase):
         stderr = run_command(code % "gc.DEBUG_SAVEALL")
         self.assertNotIn(b"uncollectable objects at shutdown", stderr)
 
+    def test_gc_main_module_at_shutdown(self):
+        # Create a reference cycle through the __main__ module and check
+        # it gets collected at interpreter shutdown.
+        code = """if 1:
+            import weakref
+            class C:
+                def __del__(self):
+                    print('__del__ called')
+            l = [C()]
+            l.append(l)
+            """
+        rc, out, err = assert_python_ok('-c', code)
+        self.assertEqual(out.strip(), b'__del__ called')
+
+    def test_gc_ordinary_module_at_shutdown(self):
+        # Same as above, but with a non-__main__ module.
+        with temp_dir() as script_dir:
+            module = """if 1:
+                import weakref
+                class C:
+                    def __del__(self):
+                        print('__del__ called')
+                l = [C()]
+                l.append(l)
+                """
+            code = """if 1:
+                import sys
+                sys.path.insert(0, %r)
+                import gctest
+                """ % (script_dir,)
+            make_script(script_dir, 'gctest', module)
+            rc, out, err = assert_python_ok('-c', code)
+            self.assertEqual(out.strip(), b'__del__ called')
+
     def test_get_stats(self):
         stats = gc.get_stats()
         self.assertEqual(len(stats), 3)
index 76a7e57dc5c1f77d98aa7027ece17c2f4181a877..3a9dd7a93f529fec8860766a2084673630d99264 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.4.0 Alpha 1?
 Core and Builtins
 -----------------
 
+- Issue #1545463: Global variables caught in reference cycles are now
+  garbage-collected at shutdown.
+
 - Issue #17094: Clear stale thread states after fork().  Note that this
   is a potentially disruptive change since it may release some system
   resources which would otherwise remain perpetually alive (e.g. database
index 9ac594fc7c8fa83a26d9c6f2f3ffbaee2d27bdb7..c9c1252d345e69064d81c23798fa3b8a715a1dfc 100644 (file)
@@ -1544,8 +1544,9 @@ PyGC_Collect(void)
     return n;
 }
 
+
 void
-_PyGC_Fini(void)
+_PyGC_DumpShutdownStats(void)
 {
     if (!(debug & DEBUG_SAVEALL)
         && garbage != NULL && PyList_GET_SIZE(garbage) > 0) {
@@ -1574,6 +1575,11 @@ _PyGC_Fini(void)
             Py_XDECREF(bytes);
         }
     }
+}
+
+void
+_PyGC_Fini(void)
+{
     Py_CLEAR(callbacks);
 }
 
index b77eda106d82c8524cc27b66cb4472d1646fb651..cd4fb788a10ad98ef6504f4ad846c6e017265c87 100644 (file)
@@ -403,6 +403,14 @@ PyImport_Cleanup(void)
         }
     }
 
+    /* Collect garbage remaining after deleting the modules. Mostly
+       reference cycles created by classes. */
+    PyGC_Collect();
+
+    /* Dump GC stats before it's too late, since it uses the warnings
+       machinery. */
+    _PyGC_DumpShutdownStats();
+
     /* Next, delete sys and builtins (in that order) */
     value = PyDict_GetItemString(modules, "sys");
     if (value != NULL && PyModule_Check(value)) {
index 96b0988ec7044c7f2c67521b21fd6642e6ecbc32..02a43299504c4ffaead1b038092b98dae0c9925e 100644 (file)
@@ -544,10 +544,6 @@ Py_Finalize(void)
     while (PyGC_Collect() > 0)
         /* nothing */;
 #endif
-    /* We run this while most interpreter state is still alive, so that
-       debug information can be printed out */
-    _PyGC_Fini();
-
     /* Destroy all modules */
     PyImport_Cleanup();
 
@@ -628,6 +624,7 @@ Py_Finalize(void)
     PyFloat_Fini();
     PyDict_Fini();
     PySlice_Fini();
+    _PyGC_Fini();
 
     /* Cleanup Unicode implementation */
     _PyUnicode_Fini();