Issue #477863: Print a warning at shutdown if gc.garbage is not empty.
This commit is contained in:
parent
2e5f1178ac
commit
696e03553b
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue