summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/pythonrun.h1
-rw-r--r--Lib/test/test_gc.py36
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/gcmodule.c8
-rw-r--r--Python/import.c8
-rw-r--r--Python/pythonrun.c5
6 files changed, 56 insertions, 5 deletions
diff --git a/Include/pythonrun.h b/Include/pythonrun.h
index e8a582d..66766dd 100644
--- a/Include/pythonrun.h
+++ b/Include/pythonrun.h
@@ -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);
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index 85dbc97..6b52e5a 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -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)
diff --git a/Misc/NEWS b/Misc/NEWS
index 76a7e57..3a9dd7a 100644
--- 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
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 9ac594f..c9c1252 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -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);
}
diff --git a/Python/import.c b/Python/import.c
index b77eda1..cd4fb78 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -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)) {
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 96b0988..02a4329 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -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();