# HG changeset patch # Parent fcf3ae3e7db49dc19279fd5ae39d0aa5fc737f19 Issue #28688: Immortal flag to avoid duplicating warning filters The “_warnings” C module keeps the original filter list alive even when the modules are reloaded, so the new flag allows the “warnings” Python module to remember if it has finished initializing the filter list at the Python level. This also relaxes the code so that it may again add duplicate (redundant) filters when explicitly asked, but the behaviour should still be consistent. diff -r fcf3ae3e7db4 Lib/test/test_warnings/__init__.py --- a/Lib/test/test_warnings/__init__.py Mon Nov 14 05:04:36 2016 +0000 +++ b/Lib/test/test_warnings/__init__.py Fri Nov 18 23:17:35 2016 +0000 @@ -288,48 +288,53 @@ self.assertEqual(str(w[-1].message), "b") def test_filterwarnings_duplicate_filters(self): - with original_warnings.catch_warnings(module=self.module): + with original_warnings.catch_warnings(module=self.module, + record=True) as w: self.module.resetwarnings() self.module.filterwarnings("error", category=UserWarning) self.assertEqual(len(self.module.filters), 1) self.module.filterwarnings("ignore", category=UserWarning) + self.module.warn("dummy", UserWarning) + self.assertEqual(len(w), 0) + self.module.filterwarnings("error", category=UserWarning) self.assertEqual( - len(self.module.filters), 2, - "filterwarnings inserted duplicate filter" - ) - self.assertEqual( self.module.filters[0][0], "error", "filterwarnings did not promote filter to " "the beginning of list" ) + with self.assertRaises(UserWarning): + self.module.warn("dummy", UserWarning) def test_simplefilter_duplicate_filters(self): - with original_warnings.catch_warnings(module=self.module): + with original_warnings.catch_warnings(module=self.module, + record=True) as w: self.module.resetwarnings() self.module.simplefilter("error", category=UserWarning) self.assertEqual(len(self.module.filters), 1) self.module.simplefilter("ignore", category=UserWarning) + self.module.warn("dummy", UserWarning) + self.assertEqual(len(w), 0) + self.module.simplefilter("error", category=UserWarning) self.assertEqual( - len(self.module.filters), 2, - "simplefilter inserted duplicate filter" - ) - self.assertEqual( self.module.filters[0][0], "error", "simplefilter did not promote filter to the beginning of list" ) + with self.assertRaises(UserWarning): + self.module.warn("dummy", UserWarning) + def test_append_duplicate(self): with original_warnings.catch_warnings(module=self.module, record=True) as w: self.module.resetwarnings() self.module.simplefilter("ignore") self.module.simplefilter("error", append=True) + self.module.warn("test_append_duplicate", category=UserWarning) + self.assertEqual(len(w), 0, "warning should be ignored") + self.module.simplefilter("ignore", append=True) self.module.warn("test_append_duplicate", category=UserWarning) - self.assertEqual(len(self.module.filters), 2, - "simplefilter inserted duplicate filter" - ) self.assertEqual(len(w), 0, "appended duplicate changed order of filters" ) diff -r fcf3ae3e7db4 Lib/warnings.py --- a/Lib/warnings.py Mon Nov 14 05:04:36 2016 +0000 +++ b/Lib/warnings.py Fri Nov 18 23:17:35 2016 +0000 @@ -149,17 +149,10 @@ _add_filter(action, None, category, None, lineno, append=append) def _add_filter(*item, append): - # Remove possible duplicate filters, so new one will be placed - # in correct place. If append=True and duplicate exists, do nothing. - if not append: - try: - filters.remove(item) - except ValueError: - pass + if append: + filters.append(item) + else: filters.insert(0, item) - else: - if item not in filters: - filters.append(item) _filters_mutated() def resetwarnings(): @@ -473,14 +466,14 @@ # - a compiled regex that must match the module that is being warned # - a line number for the line being warning, or 0 to mean any line # If either if the compiled regexs are None, match anything. -_warnings_defaults = False try: + import _warnings from _warnings import (filters, _defaultaction, _onceregistry, warn, warn_explicit, _filters_mutated) defaultaction = _defaultaction onceregistry = _onceregistry - _warnings_defaults = True except ImportError: + _warnings = None filters = [] defaultaction = "default" onceregistry = {} @@ -493,8 +486,12 @@ # Module initialization -_processoptions(sys.warnoptions) -if not _warnings_defaults: +if not getattr(_warnings, "filters_initialized", False): + _processoptions(sys.warnoptions) +if _warnings: + # _warnings.filters_initialized is a list, so as to be a mutable object + _warnings.filters_initialized.append(True) +else: silence = [ImportWarning, PendingDeprecationWarning] silence.append(DeprecationWarning) for cls in silence: @@ -514,4 +511,4 @@ resource_action = "ignore" simplefilter(resource_action, category=ResourceWarning, append=1) -del _warnings_defaults +del _warnings diff -r fcf3ae3e7db4 Python/_warnings.c --- a/Python/_warnings.c Mon Nov 14 05:04:36 2016 +0000 +++ b/Python/_warnings.c Fri Nov 18 23:17:35 2016 +0000 @@ -10,6 +10,7 @@ /* Both 'filters' and 'onceregistry' can be set in warnings.py; get_warnings_attr() will reset these variables accordingly. */ static PyObject *_filters; /* List */ +static PyObject *_filters_initialized; /* List, either [] or [True] */ static PyObject *_once_registry; /* Dict */ static PyObject *_default_action; /* String */ static long _filters_version; @@ -1246,6 +1247,18 @@ if (PyModule_AddObject(m, "filters", _filters) < 0) return NULL; + if (_filters_initialized == NULL) { + _filters_initialized = PyList_New(0); + if (_filters_initialized == NULL) { + return NULL; + } + } + Py_INCREF(_filters_initialized); + if (PyModule_AddObject(m, "filters_initialized", + _filters_initialized) < 0) { + return NULL; + } + if (_once_registry == NULL) { _once_registry = PyDict_New(); if (_once_registry == NULL)