Index: Python/pythonrun.c =================================================================== --- Python/pythonrun.c (révision 83711) +++ Python/pythonrun.c (copie de travail) @@ -402,6 +402,9 @@ 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(); Index: Include/pythonrun.h =================================================================== --- Include/pythonrun.h (révision 83711) +++ Include/pythonrun.h (copie de travail) @@ -148,6 +148,7 @@ PyAPI_FUNC(void) PyByteArray_Fini(void); PyAPI_FUNC(void) PyFloat_Fini(void); PyAPI_FUNC(void) PyOS_FiniInterrupts(void); +PyAPI_FUNC(void) _PyGC_Fini(void); /* Stuff with no proper home (yet) */ PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *); Index: Lib/test/test_gc.py =================================================================== --- Lib/test/test_gc.py (révision 83711) +++ Lib/test/test_gc.py (copie de travail) @@ -1,5 +1,5 @@ import unittest -from test.support import verbose, run_unittest +from test.support import verbose, run_unittest, strip_python_stderr import sys import gc import weakref @@ -466,6 +466,29 @@ # would be damaged, with an empty __dict__. self.assertEqual(x, None) + def test_garbage_at_shutdown(self): + import subprocess + p = subprocess.Popen([sys.executable, "-c", """if 1: + class X: + def __init__(self, name): + self.name = name + def __repr__(self): + return "" % self.name + def __del__(self): + pass + + x = X('first') + x.x = x + x.y = X('second') + del x + """], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + self.assertEqual(stdout.strip(), b"") + stderr = strip_python_stderr(stderr) + self.assertIn(b"gc: 2 uncollectable objects at shutdown", stderr) + class GCTogglingTests(unittest.TestCase): def setUp(self): gc.enable() Index: Modules/gcmodule.c =================================================================== --- Modules/gcmodule.c (révision 83711) +++ Modules/gcmodule.c (copie de travail) @@ -1255,7 +1255,6 @@ return result; } - PyDoc_STRVAR(gc__doc__, "This module provides access to the garbage collector for reference cycles.\n" "\n" @@ -1295,17 +1294,16 @@ static struct PyModuleDef gcmodule = { PyModuleDef_HEAD_INIT, - "gc", - gc__doc__, - -1, - GcMethods, - NULL, - NULL, - NULL, - NULL + "gc", /* m_name */ + gc__doc__, /* m_doc */ + -1, /* m_size */ + GcMethods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL /* m_free */ }; - PyMODINIT_FUNC PyInit_gc(void) { @@ -1364,6 +1362,37 @@ return n; } +void +_PyGC_Fini(void) +{ + if (garbage != NULL && PyList_GET_SIZE(garbage) > 0) { + PySys_WriteStderr( + "gc: " + "%" PY_FORMAT_SIZE_T "d uncollectable objects at shutdown:\n", + PyList_GET_SIZE(garbage) + ); + if (debug & DEBUG_UNCOLLECTABLE) { + PyObject *repr = NULL, *bytes = NULL; + repr = PyObject_Repr(garbage); + if (!repr || !(bytes = PyUnicode_EncodeFSDefault(repr))) + PyErr_WriteUnraisable(garbage); + else { + PySys_WriteStderr( + " %s\n", + PyBytes_AS_STRING(bytes) + ); + } + Py_XDECREF(repr); + Py_XDECREF(bytes); + } + else { + PySys_WriteStderr( + " Use gc.set_debug(gc.DEBUG_UNCOLLECTABLE) to list them.\n" + ); + } + } +} + /* for debugging */ void _PyGC_Dump(PyGC_Head *g)