Issue #477863: Print a warning at shutdown if gc.garbage is not empty.

This commit is contained in:
Antoine Pitrou 2010-08-08 22:18:46 +00:00
parent 2e5f1178ac
commit 696e03553b
7 changed files with 99 additions and 10 deletions

View File

@ -177,6 +177,15 @@ value but should not rebind it):
If :const:`DEBUG_SAVEALL` is set, then all unreachable objects will be added to
this list rather than freed.
.. versionchanged:: 3.2
If this list is non-empty at interpreter shutdown, a warning message
gets printed:
::
gc: 2 uncollectable objects at shutdown:
Use gc.set_debug(gc.DEBUG_UNCOLLECTABLE) to list them.
The following constants are provided for use with :func:`set_debug`:
@ -197,6 +206,9 @@ The following constants are provided for use with :func:`set_debug`:
reachable but cannot be freed by the collector). These objects will be added to
the ``garbage`` list.
.. versionchanged:: 3.2
Also print the contents of the :data:`garbage` list at interpreter
shutdown (rather than just its length), if it isn't empty.
.. data:: DEBUG_SAVEALL

View File

@ -119,6 +119,11 @@ New, Improved, and Deprecated Modules
* The :class:`ftplib.FTP` class now supports the context manager protocol
(Contributed by Tarek Ziadé and Giampaolo Rodolà; :issue:`4972`.)
* A warning message will now get printed at interpreter shutdown if
the :data:`gc.garbage` list isn't empty. This is meant to make the
programmer aware that his code contains object finalization issues.
(Added by Antoine Pitrou; :issue:`477863`.)
* The :func:`shutil.copytree` function has two new options:
* *ignore_dangling_symlinks*: when ``symlinks=False`` (meaning that the

View File

@ -148,6 +148,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_Fini(void);
/* Stuff with no proper home (yet) */
PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *);

View File

@ -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,42 @@ class GCTests(unittest.TestCase):
# would be damaged, with an empty __dict__.
self.assertEqual(x, None)
def test_garbage_at_shutdown(self):
import subprocess
code = """if 1:
import gc
class X:
def __init__(self, name):
self.name = name
def __repr__(self):
return "<X %%r>" %% self.name
def __del__(self):
pass
x = X('first')
x.x = x
x.y = X('second')
del x
if %d:
gc.set_debug(gc.DEBUG_UNCOLLECTABLE)
"""
def run_command(code):
p = subprocess.Popen([sys.executable, "-c", code],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
self.assertEqual(p.returncode, 0)
self.assertEqual(stdout.strip(), b"")
return strip_python_stderr(stderr)
stderr = run_command(code % 0)
self.assertIn(b"gc: 2 uncollectable objects at shutdown", stderr)
self.assertNotIn(b"[<X 'first'>, <X 'second'>]", stderr)
# With DEBUG_UNCOLLECTABLE, the garbage list gets printed
stderr = run_command(code % 1)
self.assertIn(b"gc: 2 uncollectable objects at shutdown", stderr)
self.assertIn(b"[<X 'first'>, <X 'second'>]", stderr)
class GCTogglingTests(unittest.TestCase):
def setUp(self):
gc.enable()

View File

@ -30,6 +30,8 @@ Core and Builtins
Extensions
----------
- Issue #477863: Print a warning at shutdown if gc.garbage is not empty.
- Issue #6869: Fix a refcount problem in the _ctypes extension.
- Issue #5504: ctypes should now work with systems where mmap can't

View File

@ -1295,17 +1295,16 @@ static PyMethodDef GcMethods[] = {
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 +1363,37 @@ PyGC_Collect(void)
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)

View File

@ -404,6 +404,9 @@ 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();