]> granicus.if.org Git - python/commitdiff
Add a new warning gategory, ResourceWarning, as discussed on python-dev. It is silen...
authorGeorg Brandl <georg@python.org>
Sun, 24 Oct 2010 15:11:22 +0000 (15:11 +0000)
committerGeorg Brandl <georg@python.org>
Sun, 24 Oct 2010 15:11:22 +0000 (15:11 +0000)
except when configured --with-pydebug.

Emit this warning from the GC shutdown procedure, rather than just printing to stderr.

12 files changed:
Doc/library/exceptions.rst
Doc/library/gc.rst
Doc/library/warnings.rst
Include/pyerrors.h
Lib/test/exception_hierarchy.txt
Lib/test/test_gc.py
Lib/warnings.py
Misc/NEWS
Modules/gcmodule.c
Objects/exceptions.c
Python/_warnings.c
Python/errors.c

index 49b5b9338ac9b9b5f0b1bb81264aa6a4bab60cb7..9d776983d4ea627e798e57277686e9d7353a7dc9 100644 (file)
@@ -410,10 +410,20 @@ module for more information.
 
    Base class for warnings related to Unicode.
 
+
 .. exception:: BytesWarning
 
    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
 -------------------
 
index ae615ebe8d20fcd96aa6ed61a33b3705b4c1fe10..0281bb761f77fbb503d2e8f1e0a008d9f1df5845 100644 (file)
@@ -174,17 +174,15 @@ value but should not rebind it):
    with :meth:`__del__` methods, and *garbage* can be examined in that case to
    verify that no such cycles are being created.
 
-   If :const:`DEBUG_SAVEALL` is set, then all unreachable objects will be added to
-   this list rather than freed.
+   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.
+      If this list is non-empty at interpreter shutdown, a
+      :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`:
 
@@ -203,12 +201,12 @@ The following constants are provided for use with :func:`set_debug`:
 .. data:: DEBUG_UNCOLLECTABLE
 
    Print information of uncollectable objects found (objects which are not
-   reachable but cannot be freed by the collector).  These objects will be added to
-   the ``garbage`` list.
+   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.
+      shutdown, if it isn't empty.
 
 .. data:: DEBUG_SAVEALL
 
index 309fec5032e0b9ef816c54ad554d0225914308a4..64689adfb9f76aba12dab3baf1897b35fa96c502 100644 (file)
@@ -82,6 +82,9 @@ following warnings category classes are currently defined:
 | :exc:`BytesWarning`              | Base category for warnings related to         |
 |                                  | :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,
index 58a3df731ee0831c495b6cc657cdc4149008bedb..25843c359d50f4587083efe6cff42f5a4766aec2 100644 (file)
@@ -170,6 +170,7 @@ PyAPI_DATA(PyObject *) PyExc_FutureWarning;
 PyAPI_DATA(PyObject *) PyExc_ImportWarning;
 PyAPI_DATA(PyObject *) PyExc_UnicodeWarning;
 PyAPI_DATA(PyObject *) PyExc_BytesWarning;
+PyAPI_DATA(PyObject *) PyExc_ResourceWarning;
 
 
 /* Convenience functions */
index 73ccb6674b787f253a1c1f1ae5a62a2b1b6c02ed..5037b335d978d8ebe87c2219378ac11412fe85c9 100644 (file)
@@ -47,3 +47,4 @@ BaseException
            +-- ImportWarning
            +-- UnicodeWarning
            +-- BytesWarning
+           +-- ResourceWarning
index 6c8907d4fafdbbc18080c21c95cca708b7b40747..9de7c29190f97500019e14f60d991c399af6f368 100644 (file)
@@ -485,7 +485,7 @@ class GCTests(unittest.TestCase):
             gc.set_debug(%s)
         """
         def run_command(code):
-            p = subprocess.Popen([sys.executable, "-c", code],
+            p = subprocess.Popen([sys.executable, "-Wd", "-c", code],
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE)
             stdout, stderr = p.communicate()
@@ -494,11 +494,13 @@ class GCTests(unittest.TestCase):
             return strip_python_stderr(stderr)
 
         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)
         # With DEBUG_UNCOLLECTABLE, the garbage list gets printed
         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(
             (b"[<X 'first'>, <X 'second'>]" in stderr) or
             (b"[<X 'second'>, <X 'first'>]" in stderr), stderr)
index a81aab3fe5677e8d88d8c5f16b5697d9dd782ecd..5b5821b1c3d818277ef8fb293fa3b4d83318d139 100644 (file)
@@ -383,4 +383,11 @@ if not _warnings_defaults:
     else:
         bytes_action = "ignore"
     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
index e1b818d36da9bde1a8497709a6e710390aa1259f..015ce1f1cc78f73ea0c6c29d9e1a748eb76da0f0 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -691,7 +691,7 @@ Extensions
 - 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.
 
-- 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.
 
index a95bec773a6fb08636ad6a92c2514a85e86f549b..3f96c42257488c501ea6fec1d2a2a2e1c26e5bee 100644 (file)
@@ -1368,11 +1368,16 @@ _PyGC_Fini(void)
 {
     if (!(debug & DEBUG_SAVEALL)
         && garbage != NULL && PyList_GET_SIZE(garbage) > 0) {
-        PySys_WriteStderr(
-            "gc: "
-            "%" PY_FORMAT_SIZE_T "d uncollectable objects at shutdown:\n",
-            PyList_GET_SIZE(garbage)
-            );
+        char *message;
+        if (debug & DEBUG_UNCOLLECTABLE)
+            message = "gc: %" PY_FORMAT_SIZE_T "d uncollectable objects at " \
+                "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) {
             PyObject *repr = NULL, *bytes = NULL;
             repr = PyObject_Repr(garbage);
@@ -1387,11 +1392,6 @@ _PyGC_Fini(void)
             Py_XDECREF(repr);
             Py_XDECREF(bytes);
         }
-        else {
-            PySys_WriteStderr(
-                "    Use gc.set_debug(gc.DEBUG_UNCOLLECTABLE) to list them.\n"
-                );
-        }
     }
 }
 
index b82b6ba87f7ce42734056a1c9d510a23b56c6b42..5715b26fa7da3bd0192bc49ad8119b2cf6fbc67c 100644 (file)
@@ -1852,6 +1852,7 @@ SimpleExtendsException(PyExc_Warning, UnicodeWarning,
     "Base class for warnings about Unicode related problems, mostly\n"
     "related to conversion problems.");
 
+
 /*
  *    BytesWarning extends Warning
  */
@@ -1860,6 +1861,13 @@ SimpleExtendsException(PyExc_Warning, BytesWarning,
     "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
  * possible and not wait until a MemoryError is actually raised!
@@ -1939,6 +1947,7 @@ _PyExc_Init(void)
     PRE_INIT(ImportWarning)
     PRE_INIT(UnicodeWarning)
     PRE_INIT(BytesWarning)
+    PRE_INIT(ResourceWarning)
 
     bltinmod = PyImport_ImportModule("builtins");
     if (bltinmod == NULL)
@@ -2001,6 +2010,7 @@ _PyExc_Init(void)
     POST_INIT(ImportWarning)
     POST_INIT(UnicodeWarning)
     POST_INIT(BytesWarning)
+    POST_INIT(ResourceWarning)
 
     PyExc_MemoryErrorInst = BaseException_new(&_PyExc_MemoryError, NULL, NULL);
     if (!PyExc_MemoryErrorInst)
index a4e9d48e8ec20a731efe4e7a6ca5a95087424bd5..87755e1edbc5d7c352db54a14c8d12c2620bf9b6 100644 (file)
@@ -835,6 +835,7 @@ create_filter(PyObject *category, const char *action)
     static PyObject *ignore_str = NULL;
     static PyObject *error_str = NULL;
     static PyObject *default_str = NULL;
+    static PyObject *always_str = NULL;
     PyObject *action_obj = NULL;
     PyObject *lineno, *result;
 
@@ -862,6 +863,14 @@ create_filter(PyObject *category, const char *action)
         }
         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 {
         Py_FatalError("unknown action");
     }
@@ -879,10 +888,10 @@ static PyObject *
 init_filters(void)
 {
     /* 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 x;
-    const char *bytes_action;
+    const char *bytes_action, *resource_action;
 
     if (filters == NULL)
         return NULL;
@@ -901,7 +910,14 @@ init_filters(void)
         bytes_action = "ignore";
     PyList_SET_ITEM(filters, pos++, create_filter(PyExc_BytesWarning,
                     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) {
         if (PyList_GET_ITEM(filters, x) == NULL) {
             Py_DECREF(filters);
index e3486b95c510161016421e415c7535717e658a5c..04906141d52d34cad799d147d545a8efb7588649 100644 (file)
@@ -767,8 +767,10 @@ PyErr_WriteUnraisable(PyObject *obj)
             }
             Py_XDECREF(moduleName);
         }
-        PyFile_WriteString(" in ", f);
-        PyFile_WriteObject(obj, f, 0);
+        if (obj) {
+            PyFile_WriteString(" in ", f);
+            PyFile_WriteObject(obj, f, 0);
+        }
         PyFile_WriteString(" ignored\n", f);
         PyErr_Clear(); /* Just in case */
     }