mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Add a new warning gategory, ResourceWarning, as discussed on python-dev. It is silent by default,
except when configured --with-pydebug. Emit this warning from the GC shutdown procedure, rather than just printing to stderr.
This commit is contained in:
parent
872a702bbd
commit
08be72d0aa
12 changed files with 80 additions and 30 deletions
|
@ -410,10 +410,20 @@ module for more information.
|
||||||
|
|
||||||
Base class for warnings related to Unicode.
|
Base class for warnings related to Unicode.
|
||||||
|
|
||||||
|
|
||||||
.. exception:: BytesWarning
|
.. exception:: BytesWarning
|
||||||
|
|
||||||
Base class for warnings related to :class:`bytes` and :class:`buffer`.
|
Base class for warnings related to :class:`bytes` and :class:`buffer`.
|
||||||
|
|
||||||
|
|
||||||
|
.. exception:: ResourceWarning
|
||||||
|
|
||||||
|
Base class for warnings related to resource usage.
|
||||||
|
|
||||||
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Exception hierarchy
|
Exception hierarchy
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|
|
@ -174,17 +174,15 @@ value but should not rebind it):
|
||||||
with :meth:`__del__` methods, and *garbage* can be examined in that case to
|
with :meth:`__del__` methods, and *garbage* can be examined in that case to
|
||||||
verify that no such cycles are being created.
|
verify that no such cycles are being created.
|
||||||
|
|
||||||
If :const:`DEBUG_SAVEALL` is set, then all unreachable objects will be added to
|
If :const:`DEBUG_SAVEALL` is set, then all unreachable objects will be added
|
||||||
this list rather than freed.
|
to this list rather than freed.
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
.. versionchanged:: 3.2
|
||||||
If this list is non-empty at interpreter shutdown, a warning message
|
If this list is non-empty at interpreter shutdown, a
|
||||||
gets printed.
|
:exc:`ResourceWarning` is emitted, which is silent by default. If
|
||||||
|
:const:`DEBUG_UNCOLLECTABLE` is set, in addition all uncollectable objects
|
||||||
|
are 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`:
|
The following constants are provided for use with :func:`set_debug`:
|
||||||
|
|
||||||
|
@ -203,12 +201,12 @@ The following constants are provided for use with :func:`set_debug`:
|
||||||
.. data:: DEBUG_UNCOLLECTABLE
|
.. data:: DEBUG_UNCOLLECTABLE
|
||||||
|
|
||||||
Print information of uncollectable objects found (objects which are not
|
Print information of uncollectable objects found (objects which are not
|
||||||
reachable but cannot be freed by the collector). These objects will be added to
|
reachable but cannot be freed by the collector). These objects will be added
|
||||||
the ``garbage`` list.
|
to the ``garbage`` list.
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
.. versionchanged:: 3.2
|
||||||
Also print the contents of the :data:`garbage` list at interpreter
|
Also print the contents of the :data:`garbage` list at interpreter
|
||||||
shutdown (rather than just its length), if it isn't empty.
|
shutdown, if it isn't empty.
|
||||||
|
|
||||||
.. data:: DEBUG_SAVEALL
|
.. data:: DEBUG_SAVEALL
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,9 @@ following warnings category classes are currently defined:
|
||||||
| :exc:`BytesWarning` | Base category for warnings related to |
|
| :exc:`BytesWarning` | Base category for warnings related to |
|
||||||
| | :class:`bytes` and :class:`buffer`. |
|
| | :class:`bytes` and :class:`buffer`. |
|
||||||
+----------------------------------+-----------------------------------------------+
|
+----------------------------------+-----------------------------------------------+
|
||||||
|
| :exc:`ResourceWarning` | Base category for warnings related to |
|
||||||
|
| | resource usage. |
|
||||||
|
+----------------------------------+-----------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
While these are technically built-in exceptions, they are documented here,
|
While these are technically built-in exceptions, they are documented here,
|
||||||
|
|
|
@ -170,6 +170,7 @@ PyAPI_DATA(PyObject *) PyExc_FutureWarning;
|
||||||
PyAPI_DATA(PyObject *) PyExc_ImportWarning;
|
PyAPI_DATA(PyObject *) PyExc_ImportWarning;
|
||||||
PyAPI_DATA(PyObject *) PyExc_UnicodeWarning;
|
PyAPI_DATA(PyObject *) PyExc_UnicodeWarning;
|
||||||
PyAPI_DATA(PyObject *) PyExc_BytesWarning;
|
PyAPI_DATA(PyObject *) PyExc_BytesWarning;
|
||||||
|
PyAPI_DATA(PyObject *) PyExc_ResourceWarning;
|
||||||
|
|
||||||
|
|
||||||
/* Convenience functions */
|
/* Convenience functions */
|
||||||
|
|
|
@ -47,3 +47,4 @@ BaseException
|
||||||
+-- ImportWarning
|
+-- ImportWarning
|
||||||
+-- UnicodeWarning
|
+-- UnicodeWarning
|
||||||
+-- BytesWarning
|
+-- BytesWarning
|
||||||
|
+-- ResourceWarning
|
||||||
|
|
|
@ -485,7 +485,7 @@ class GCTests(unittest.TestCase):
|
||||||
gc.set_debug(%s)
|
gc.set_debug(%s)
|
||||||
"""
|
"""
|
||||||
def run_command(code):
|
def run_command(code):
|
||||||
p = subprocess.Popen([sys.executable, "-c", code],
|
p = subprocess.Popen([sys.executable, "-Wd", "-c", code],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
stdout, stderr = p.communicate()
|
stdout, stderr = p.communicate()
|
||||||
|
@ -494,11 +494,13 @@ class GCTests(unittest.TestCase):
|
||||||
return strip_python_stderr(stderr)
|
return strip_python_stderr(stderr)
|
||||||
|
|
||||||
stderr = run_command(code % "0")
|
stderr = run_command(code % "0")
|
||||||
self.assertIn(b"gc: 2 uncollectable objects at shutdown", stderr)
|
self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
|
||||||
|
b"shutdown; use", stderr)
|
||||||
self.assertNotIn(b"<X 'first'>", stderr)
|
self.assertNotIn(b"<X 'first'>", stderr)
|
||||||
# With DEBUG_UNCOLLECTABLE, the garbage list gets printed
|
# With DEBUG_UNCOLLECTABLE, the garbage list gets printed
|
||||||
stderr = run_command(code % "gc.DEBUG_UNCOLLECTABLE")
|
stderr = run_command(code % "gc.DEBUG_UNCOLLECTABLE")
|
||||||
self.assertIn(b"gc: 2 uncollectable objects at shutdown", stderr)
|
self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
|
||||||
|
b"shutdown", stderr)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
(b"[<X 'first'>, <X 'second'>]" in stderr) or
|
(b"[<X 'first'>, <X 'second'>]" in stderr) or
|
||||||
(b"[<X 'second'>, <X 'first'>]" in stderr), stderr)
|
(b"[<X 'second'>, <X 'first'>]" in stderr), stderr)
|
||||||
|
|
|
@ -383,4 +383,11 @@ if not _warnings_defaults:
|
||||||
else:
|
else:
|
||||||
bytes_action = "ignore"
|
bytes_action = "ignore"
|
||||||
simplefilter(bytes_action, category=BytesWarning, append=1)
|
simplefilter(bytes_action, category=BytesWarning, append=1)
|
||||||
|
# resource usage warnings are enabled by default in pydebug mode
|
||||||
|
if hasattr(sys, 'gettotalrefcount'):
|
||||||
|
resource_action = "always"
|
||||||
|
else:
|
||||||
|
resource_action = "ignore"
|
||||||
|
simplefilter(resource_action, category=ResourceWarning, append=1)
|
||||||
|
|
||||||
del _warnings_defaults
|
del _warnings_defaults
|
||||||
|
|
|
@ -691,7 +691,7 @@ Extensions
|
||||||
- Issue #8524: Add a detach() method to socket objects, so as to put the socket
|
- Issue #8524: Add a detach() method to socket objects, so as to put the socket
|
||||||
into the closed state without closing the underlying file descriptor.
|
into the closed state without closing the underlying file descriptor.
|
||||||
|
|
||||||
- Issue #477863: Print a warning at shutdown if gc.garbage is not empty.
|
- Issue #477863: Emit a ResourceWarning at shutdown if gc.garbage is not empty.
|
||||||
|
|
||||||
- Issue #6869: Fix a refcount problem in the _ctypes extension.
|
- Issue #6869: Fix a refcount problem in the _ctypes extension.
|
||||||
|
|
||||||
|
|
|
@ -1368,11 +1368,16 @@ _PyGC_Fini(void)
|
||||||
{
|
{
|
||||||
if (!(debug & DEBUG_SAVEALL)
|
if (!(debug & DEBUG_SAVEALL)
|
||||||
&& garbage != NULL && PyList_GET_SIZE(garbage) > 0) {
|
&& garbage != NULL && PyList_GET_SIZE(garbage) > 0) {
|
||||||
PySys_WriteStderr(
|
char *message;
|
||||||
"gc: "
|
if (debug & DEBUG_UNCOLLECTABLE)
|
||||||
"%" PY_FORMAT_SIZE_T "d uncollectable objects at shutdown:\n",
|
message = "gc: %" PY_FORMAT_SIZE_T "d uncollectable objects at " \
|
||||||
PyList_GET_SIZE(garbage)
|
"shutdown";
|
||||||
);
|
else
|
||||||
|
message = "gc: %" PY_FORMAT_SIZE_T "d uncollectable objects at " \
|
||||||
|
"shutdown; use gc.set_debug(gc.DEBUG_UNCOLLECTABLE) to list them";
|
||||||
|
if (PyErr_WarnFormat(PyExc_ResourceWarning, 0, message,
|
||||||
|
PyList_GET_SIZE(garbage)) < 0)
|
||||||
|
PyErr_WriteUnraisable(NULL);
|
||||||
if (debug & DEBUG_UNCOLLECTABLE) {
|
if (debug & DEBUG_UNCOLLECTABLE) {
|
||||||
PyObject *repr = NULL, *bytes = NULL;
|
PyObject *repr = NULL, *bytes = NULL;
|
||||||
repr = PyObject_Repr(garbage);
|
repr = PyObject_Repr(garbage);
|
||||||
|
@ -1387,11 +1392,6 @@ _PyGC_Fini(void)
|
||||||
Py_XDECREF(repr);
|
Py_XDECREF(repr);
|
||||||
Py_XDECREF(bytes);
|
Py_XDECREF(bytes);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
PySys_WriteStderr(
|
|
||||||
" Use gc.set_debug(gc.DEBUG_UNCOLLECTABLE) to list them.\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1852,6 +1852,7 @@ SimpleExtendsException(PyExc_Warning, UnicodeWarning,
|
||||||
"Base class for warnings about Unicode related problems, mostly\n"
|
"Base class for warnings about Unicode related problems, mostly\n"
|
||||||
"related to conversion problems.");
|
"related to conversion problems.");
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BytesWarning extends Warning
|
* BytesWarning extends Warning
|
||||||
*/
|
*/
|
||||||
|
@ -1860,6 +1861,13 @@ SimpleExtendsException(PyExc_Warning, BytesWarning,
|
||||||
"related to conversion from str or comparing to str.");
|
"related to conversion from str or comparing to str.");
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ResourceWarning extends Warning
|
||||||
|
*/
|
||||||
|
SimpleExtendsException(PyExc_Warning, ResourceWarning,
|
||||||
|
"Base class for warnings about resource usage.");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Pre-computed MemoryError instance. Best to create this as early as
|
/* Pre-computed MemoryError instance. Best to create this as early as
|
||||||
* possible and not wait until a MemoryError is actually raised!
|
* possible and not wait until a MemoryError is actually raised!
|
||||||
|
@ -1939,6 +1947,7 @@ _PyExc_Init(void)
|
||||||
PRE_INIT(ImportWarning)
|
PRE_INIT(ImportWarning)
|
||||||
PRE_INIT(UnicodeWarning)
|
PRE_INIT(UnicodeWarning)
|
||||||
PRE_INIT(BytesWarning)
|
PRE_INIT(BytesWarning)
|
||||||
|
PRE_INIT(ResourceWarning)
|
||||||
|
|
||||||
bltinmod = PyImport_ImportModule("builtins");
|
bltinmod = PyImport_ImportModule("builtins");
|
||||||
if (bltinmod == NULL)
|
if (bltinmod == NULL)
|
||||||
|
@ -2001,6 +2010,7 @@ _PyExc_Init(void)
|
||||||
POST_INIT(ImportWarning)
|
POST_INIT(ImportWarning)
|
||||||
POST_INIT(UnicodeWarning)
|
POST_INIT(UnicodeWarning)
|
||||||
POST_INIT(BytesWarning)
|
POST_INIT(BytesWarning)
|
||||||
|
POST_INIT(ResourceWarning)
|
||||||
|
|
||||||
PyExc_MemoryErrorInst = BaseException_new(&_PyExc_MemoryError, NULL, NULL);
|
PyExc_MemoryErrorInst = BaseException_new(&_PyExc_MemoryError, NULL, NULL);
|
||||||
if (!PyExc_MemoryErrorInst)
|
if (!PyExc_MemoryErrorInst)
|
||||||
|
|
|
@ -835,6 +835,7 @@ create_filter(PyObject *category, const char *action)
|
||||||
static PyObject *ignore_str = NULL;
|
static PyObject *ignore_str = NULL;
|
||||||
static PyObject *error_str = NULL;
|
static PyObject *error_str = NULL;
|
||||||
static PyObject *default_str = NULL;
|
static PyObject *default_str = NULL;
|
||||||
|
static PyObject *always_str = NULL;
|
||||||
PyObject *action_obj = NULL;
|
PyObject *action_obj = NULL;
|
||||||
PyObject *lineno, *result;
|
PyObject *lineno, *result;
|
||||||
|
|
||||||
|
@ -862,6 +863,14 @@ create_filter(PyObject *category, const char *action)
|
||||||
}
|
}
|
||||||
action_obj = default_str;
|
action_obj = default_str;
|
||||||
}
|
}
|
||||||
|
else if (!strcmp(action, "always")) {
|
||||||
|
if (always_str == NULL) {
|
||||||
|
always_str = PyUnicode_InternFromString("always");
|
||||||
|
if (always_str == NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
action_obj = always_str;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
Py_FatalError("unknown action");
|
Py_FatalError("unknown action");
|
||||||
}
|
}
|
||||||
|
@ -879,10 +888,10 @@ static PyObject *
|
||||||
init_filters(void)
|
init_filters(void)
|
||||||
{
|
{
|
||||||
/* Don't silence DeprecationWarning if -3 was used. */
|
/* Don't silence DeprecationWarning if -3 was used. */
|
||||||
PyObject *filters = PyList_New(4);
|
PyObject *filters = PyList_New(5);
|
||||||
unsigned int pos = 0; /* Post-incremented in each use. */
|
unsigned int pos = 0; /* Post-incremented in each use. */
|
||||||
unsigned int x;
|
unsigned int x;
|
||||||
const char *bytes_action;
|
const char *bytes_action, *resource_action;
|
||||||
|
|
||||||
if (filters == NULL)
|
if (filters == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -901,7 +910,14 @@ init_filters(void)
|
||||||
bytes_action = "ignore";
|
bytes_action = "ignore";
|
||||||
PyList_SET_ITEM(filters, pos++, create_filter(PyExc_BytesWarning,
|
PyList_SET_ITEM(filters, pos++, create_filter(PyExc_BytesWarning,
|
||||||
bytes_action));
|
bytes_action));
|
||||||
|
/* resource usage warnings are enabled by default in pydebug mode */
|
||||||
|
#ifdef Py_DEBUG
|
||||||
|
resource_action = "always";
|
||||||
|
#else
|
||||||
|
resource_action = "ignore";
|
||||||
|
#endif
|
||||||
|
PyList_SET_ITEM(filters, pos++, create_filter(PyExc_ResourceWarning,
|
||||||
|
resource_action));
|
||||||
for (x = 0; x < pos; x += 1) {
|
for (x = 0; x < pos; x += 1) {
|
||||||
if (PyList_GET_ITEM(filters, x) == NULL) {
|
if (PyList_GET_ITEM(filters, x) == NULL) {
|
||||||
Py_DECREF(filters);
|
Py_DECREF(filters);
|
||||||
|
|
|
@ -767,8 +767,10 @@ PyErr_WriteUnraisable(PyObject *obj)
|
||||||
}
|
}
|
||||||
Py_XDECREF(moduleName);
|
Py_XDECREF(moduleName);
|
||||||
}
|
}
|
||||||
PyFile_WriteString(" in ", f);
|
if (obj) {
|
||||||
PyFile_WriteObject(obj, f, 0);
|
PyFile_WriteString(" in ", f);
|
||||||
|
PyFile_WriteObject(obj, f, 0);
|
||||||
|
}
|
||||||
PyFile_WriteString(" ignored\n", f);
|
PyFile_WriteString(" ignored\n", f);
|
||||||
PyErr_Clear(); /* Just in case */
|
PyErr_Clear(); /* Just in case */
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue