[Python-checkins] cpython (merge 3.4 -> default): Issue #4180: The warnings registries are now reset when the filters are

antoine.pitrou python-checkins at python.org
Thu Sep 18 02:44:40 CEST 2014


http://hg.python.org/cpython/rev/4bc60eb68d3e
changeset:   92459:4bc60eb68d3e
parent:      92457:04147b0172d7
parent:      92458:8adb2c6e0803
user:        Antoine Pitrou <solipsis at pitrou.net>
date:        Thu Sep 18 02:42:05 2014 +0200
summary:
  Issue #4180: The warnings registries are now reset when the filters are modified.

files:
  Lib/test/test_warnings.py |  49 +++++++++++++++++++++++++-
  Lib/warnings.py           |  17 ++++++++-
  Misc/NEWS                 |   3 +
  Python/_warnings.c        |  41 +++++++++++++++++++---
  4 files changed, 101 insertions(+), 9 deletions(-)


diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py
--- a/Lib/test/test_warnings.py
+++ b/Lib/test/test_warnings.py
@@ -92,6 +92,16 @@
             self.assertRaises(UserWarning, self.module.warn,
                                 "FilterTests.test_error")
 
+    def test_error_after_default(self):
+        with original_warnings.catch_warnings(module=self.module) as w:
+            self.module.resetwarnings()
+            message = "FilterTests.test_ignore_after_default"
+            def f():
+                self.module.warn(message, UserWarning)
+            f()
+            self.module.filterwarnings("error", category=UserWarning)
+            self.assertRaises(UserWarning, f)
+
     def test_ignore(self):
         with original_warnings.catch_warnings(record=True,
                 module=self.module) as w:
@@ -100,6 +110,19 @@
             self.module.warn("FilterTests.test_ignore", UserWarning)
             self.assertEqual(len(w), 0)
 
+    def test_ignore_after_default(self):
+        with original_warnings.catch_warnings(record=True,
+                module=self.module) as w:
+            self.module.resetwarnings()
+            message = "FilterTests.test_ignore_after_default"
+            def f():
+                self.module.warn(message, UserWarning)
+            f()
+            self.module.filterwarnings("ignore", category=UserWarning)
+            f()
+            f()
+            self.assertEqual(len(w), 1)
+
     def test_always(self):
         with original_warnings.catch_warnings(record=True,
                 module=self.module) as w:
@@ -111,6 +134,26 @@
             self.module.warn(message, UserWarning)
             self.assertTrue(w[-1].message, message)
 
+    def test_always_after_default(self):
+        with original_warnings.catch_warnings(record=True,
+                module=self.module) as w:
+            self.module.resetwarnings()
+            message = "FilterTests.test_always_after_ignore"
+            def f():
+                self.module.warn(message, UserWarning)
+            f()
+            self.assertEqual(len(w), 1)
+            self.assertEqual(w[-1].message.args[0], message)
+            f()
+            self.assertEqual(len(w), 1)
+            self.module.filterwarnings("always", category=UserWarning)
+            f()
+            self.assertEqual(len(w), 2)
+            self.assertEqual(w[-1].message.args[0], message)
+            f()
+            self.assertEqual(len(w), 3)
+            self.assertEqual(w[-1].message.args[0], message)
+
     def test_default(self):
         with original_warnings.catch_warnings(record=True,
                 module=self.module) as w:
@@ -541,7 +584,9 @@
                                             registry=registry)
                 self.assertEqual(w[-1].message, message)
                 self.assertEqual(len(w), 1)
-                self.assertEqual(len(registry), 1)
+                # One actual registry key plus the "version" key
+                self.assertEqual(len(registry), 2)
+                self.assertIn("version", registry)
                 del w[:]
                 # Test removal.
                 del self.module.defaultaction
@@ -551,7 +596,7 @@
                                             registry=registry)
                 self.assertEqual(w[-1].message, message)
                 self.assertEqual(len(w), 1)
-                self.assertEqual(len(registry), 1)
+                self.assertEqual(len(registry), 2)
                 del w[:]
                 # Test setting.
                 self.module.defaultaction = "ignore"
diff --git a/Lib/warnings.py b/Lib/warnings.py
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -53,6 +53,7 @@
         filters.append(item)
     else:
         filters.insert(0, item)
+    _filters_mutated()
 
 def simplefilter(action, category=Warning, lineno=0, append=False):
     """Insert a simple entry into the list of warnings filters (at the front).
@@ -73,10 +74,12 @@
         filters.append(item)
     else:
         filters.insert(0, item)
+    _filters_mutated()
 
 def resetwarnings():
     """Clear the list of warning filters, so that no filters are active."""
     filters[:] = []
+    _filters_mutated()
 
 class _OptionError(Exception):
     """Exception used by option processing helpers."""
@@ -206,6 +209,9 @@
             module = module[:-3] # XXX What about leading pathname?
     if registry is None:
         registry = {}
+    if registry.get('version', 0) != _filters_version:
+        registry.clear()
+        registry['version'] = _filters_version
     if isinstance(message, Warning):
         text = str(message)
         category = message.__class__
@@ -331,6 +337,7 @@
         self._entered = True
         self._filters = self._module.filters
         self._module.filters = self._filters[:]
+        self._module._filters_mutated()
         self._showwarning = self._module.showwarning
         if self._record:
             log = []
@@ -345,6 +352,7 @@
         if not self._entered:
             raise RuntimeError("Cannot exit %r without entering first" % self)
         self._module.filters = self._filters
+        self._module._filters_mutated()
         self._module.showwarning = self._showwarning
 
 
@@ -359,15 +367,22 @@
 _warnings_defaults = False
 try:
     from _warnings import (filters, _defaultaction, _onceregistry,
-                            warn, warn_explicit)
+                           warn, warn_explicit, _filters_mutated)
     defaultaction = _defaultaction
     onceregistry = _onceregistry
     _warnings_defaults = True
+
 except ImportError:
     filters = []
     defaultaction = "default"
     onceregistry = {}
 
+    _filters_version = 1
+
+    def _filters_mutated():
+        global _filters_version
+        _filters_version += 1
+
 
 # Module initialization
 _processoptions(sys.warnoptions)
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -132,6 +132,9 @@
 Library
 -------
 
+- Issue #4180: The warnings registries are now reset when the filters
+  are modified.
+
 - Issue #22419: Limit the length of incoming HTTP request in wsgiref server to
   65536 bytes and send a 414 error code for higher lengths. Patch contributed
   by Devin Cook.
diff --git a/Python/_warnings.c b/Python/_warnings.c
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -12,6 +12,7 @@
 static PyObject *_filters;  /* List */
 static PyObject *_once_registry;  /* Dict */
 static PyObject *_default_action; /* String */
+static long _filters_version;
 
 _Py_IDENTIFIER(argv);
 _Py_IDENTIFIER(stderr);
@@ -178,16 +179,33 @@
 static int
 already_warned(PyObject *registry, PyObject *key, int should_set)
 {
-    PyObject *already_warned;
+    PyObject *version_obj, *already_warned;
+    _Py_IDENTIFIER(version);
 
     if (key == NULL)
         return -1;
 
-    already_warned = PyDict_GetItem(registry, key);
-    if (already_warned != NULL) {
-        int rc = PyObject_IsTrue(already_warned);
-        if (rc != 0)
-            return rc;
+    version_obj = _PyDict_GetItemId(registry, &PyId_version);
+    if (version_obj == NULL
+        || !PyLong_CheckExact(version_obj)
+        || PyLong_AsLong(version_obj) != _filters_version) {
+        PyDict_Clear(registry);
+        version_obj = PyLong_FromLong(_filters_version);
+        if (version_obj == NULL)
+            return -1;
+        if (_PyDict_SetItemId(registry, &PyId_version, version_obj) < 0) {
+            Py_DECREF(version_obj);
+            return -1;
+        }
+        Py_DECREF(version_obj);
+    }
+    else {
+        already_warned = PyDict_GetItem(registry, key);
+        if (already_warned != NULL) {
+            int rc = PyObject_IsTrue(already_warned);
+            if (rc != 0)
+                return rc;
+        }
     }
 
     /* This warning wasn't found in the registry, set it. */
@@ -751,6 +769,13 @@
                          registry, NULL);
 }
 
+static PyObject *
+warnings_filters_mutated(PyObject *self, PyObject *args)
+{
+    _filters_version++;
+    Py_RETURN_NONE;
+}
+
 
 /* Function to issue a warning message; may raise an exception. */
 
@@ -918,6 +943,8 @@
         warn_doc},
     {"warn_explicit", (PyCFunction)warnings_warn_explicit,
         METH_VARARGS | METH_KEYWORDS, warn_explicit_doc},
+    {"_filters_mutated", (PyCFunction)warnings_filters_mutated, METH_NOARGS,
+        NULL},
     /* XXX(brett.cannon): add showwarning? */
     /* XXX(brett.cannon): Reasonable to add formatwarning? */
     {NULL, NULL}                /* sentinel */
@@ -1070,5 +1097,7 @@
     Py_INCREF(_default_action);
     if (PyModule_AddObject(m, "_defaultaction", _default_action) < 0)
         return NULL;
+
+    _filters_version = 0;
     return m;
 }

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list