8000 Issue #4180: The warnings registries are now reset when the filters a… · python/cpython@cb0a006 · GitHub
[go: up one dir, main page]

Skip to content

Commit cb0a006

Browse files
committed
Issue #4180: The warnings registries are now reset when the filters are modified.
1 parent 1b38bc6 commit cb0a006

File tree

4 files changed

+101
-9
lines changed

4 files changed

+101
-9
lines changed

Lib/test/test_warnings.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ def test_error(self):
9292
self.assertRaises(UserWarning, self.module.warn,
9393
"FilterTests.test_error")
9494

95+
def test_error_after_default(self):
96+
with original_warnings.catch_warnings(module=self.module) as w:
97+
self.module.resetwarnings()
98+
message = "FilterTests.test_ignore_after_default"
99+
def f():
100+
self.module.warn(message, UserWarning)
101+
f()
102+
self.module.filterwarnings("error", category=UserWarning)
103+
self.assertRaises(UserWarning, f)
104+
95105
def test_ignore(self):
96106
with original_warnings.catch_warnings(record=True,
97107
module=self.module) as w:
@@ -100,6 +110,19 @@ def test_ignore(self):
100110
self.module.warn("FilterTests.test_ignore", UserWarning)
101111
self.assertEqual(len(w), 0)
102112

113+
def test_ignore_after_default(self):
114+
with original_warnings.catch_warnings(record=True,
115+
module=self.module) as w:
116+
self.module.resetwarnings()
117+
message = "FilterTests.test_ignore_after_default"
118+
def f():
119+
self.module.warn(message, UserWarning)
120+
f()
121+
self.module.filterwarnings("ignore", category=UserWarning)
122+
f()
123+
f()
124+
self.assertEqual(len(w), 1)
125+
103126
def test_always(self):
104127
with original_warnings.catch_warnings(record=True,
105128
module=self.module) as w:
@@ -111,6 +134,26 @@ def test_always(self):
111134
self.module.warn(message, UserWarning)
112135
self.assertTrue(w[-1].message, message)
113136

137+
def test_always_after_default(self):
138+
with original_warnings.catch_warnings(record=True,
139+
module=self.module) as w:
140+
self.module.resetwarnings()
141+
message = "FilterTests.test_always_after_ignore"
142+
def f():
143+
self.module.warn(message, UserWarning)
144+
f()
145+
self.assertEqual(len(w), 1)
146+
self.assertEqual(w[-1].message.args[0], message)
147+
f()
148+
self.assertEqual(len(w), 1)
149+
self.module.filterwarnings("always", category=UserWarning)
150+
f()
151+
self.assertEqual(len(w), 2)
152+
self.assertEqual(w[-1].message.args[0], message)
153+
f()
154+
self.assertEqual(len(w), 3)
155+
self.assertEqual(w[-1].message.args[0], message)
156+
114157
def test_default(self):
115158
with original_warnings.catch_warnings(record=True,
116159
module=self.module) as w:
@@ -506,7 +549,9 @@ def test_default_action(self):
506549
registry=registry)
507550
self.assertEqual(w[-1].message, message)
508551
self.assertEqual(len(w), 1)
509-
self.assertEqual(len(registry), 1)
552+
# One actual registry key plus the "version" key
553+
self.assertEqual(len(registry), 2)
554+
self.assertIn("version", registry)
510555
del w[:]
511556
# Test removal.
512557
del self.module.defaultaction
@@ -516,7 +561,7 @@ def test_default_action(self):
516561
registry=registry)
517562
self.assertEqual(w[-1].message, message)
518563
self.assertEqual(len(w), 1)
519-
self.assertEqual(len(registry), 1)
564+
self.assertEqual(len(registry), 2)
520565
del w[:]
521566
# Test setting.
522567
self.module.defaultaction = "ignore"

Lib/warnings.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0,
5353
filters.append(item)
5454
else:
5555
filters.insert(0, item)
56+
_filters_mutated()
5657

5758
def simplefilter(action, category=Warning, lineno=0, append=False):
5859
"""Insert a simple entry into the list of warnings filters (at the front).
@@ -73,10 +74,12 @@ def simplefilter(action, category=Warning, lineno=0, append=False):
7374
filters.append(item)
7475
else:
7576
filters.insert(0, item)
77+
_filters_mutated()
7678

7779
def resetwarnings():
7880
"""Clear the list of warning filters, so that no filters are active."""
7981
filters[:] = []
82+
_filters_mutated()
8083

8184
class _OptionError(Exception):
8285
"""Exception used by option processing helpers."""
@@ -204,6 +207,9 @@ def warn_explicit(message, category, filename, lineno,
204207
module = module[:-3] # XXX What about leading pathname?
205208
if registry is None:
206209
registry = {}
210+
if registry.get('version', 0) != _filters_version:
211+
registry.clear()
212+
registry['version'] = _filters_version
207213
if isinstance(message, Warning):
208214
text = str(message)
209215
category = message.__class__
@@ -329,6 +335,7 @@ def __enter__(self):
329335
self._entered = True
330336
self._filters = self._module.filters
331337
self._module.filters = self._filters[:]
338+
self._module._filters_mutated()
332339
self._showwarning = self._module.showwarning
333340
if self._record:
334341
log = []
@@ -343,6 +350,7 @@ def __exit__(self, *exc_info):
343350
if not self._entered:
344351
raise RuntimeError("Cannot exit %r without entering first" % self)
345352
self._module.filters = self._filters
353+
self._module._filters_mutated()
346354
self._module.showwarning = self._showwarning
347355

348356

@@ -357,15 +365,22 @@ def __exit__(self, *exc_info):
357365
_warnings_defaults = False
358366
try:
359367
from _warnings import (filters, _defaultaction, _onceregistry,
360-
warn, warn_explicit)
368+
warn, warn_explicit, _filters_mutated)
361369
defaultaction = _defaultaction
362370
onceregistry = _onceregistry
363371
_warnings_defaults = True
372+
364373
except ImportError:
365374
filters = []
366375
defaultaction = "default"
367376
onceregistry = {}
368377

378+
_filters_version = 1
379+
380+
def _filters_mutated():
381+
global _filters_version
382+
_filters_version += 1
383+
369384

370385
# Module initialization
371386
_processoptions(sys.warnoptions)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ Core and Builtins
3232
Library
3333
-------
3434

35+
- Issue #4180: The warnings registries are now reset when the filters
36+
are modified.
37+
3538
- Issue #22419: Limit the length of incoming HTTP request in wsgiref server to
3639
65536 bytes and send a 414 error code for higher lengths. Patch contributed
3740
by Devin Cook.

Python/_warnings.c

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ MODULE_NAME " provides basic warning filtering support.\n"
1212
static PyObject *_filters; /* List */
1313
static PyObject *_once_registry; /* Dict */
1414
static PyObject *_default_action; /* String */
15+
static long _filters_version;
1516

1617
_Py_IDENTIFIER(argv);
1718
_Py_IDENTIFIER(stderr);
@@ -178,16 +179,33 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
178179
static int
179180
already_warned(PyObject *registry, PyObject *key, int should_set)
180181
{
181-
PyObject *already_warned;
182+
PyObject *version_obj, *already_warned;
183+
_Py_IDENTIFIER(version);
182184

183185
if (key == NULL)
184186
return -1;
185187

186-
already_warned = PyDict_GetItem(registry, key);
187-
if (already_warned != NULL) {
188-
int rc = PyObject_IsTrue(already_warned);
189-
if (rc != 0)
190-
return rc;
188+
version_obj = _PyDict_GetItemId(registry, &PyId_version);
189+
if (version_obj == NULL
190+
|| !PyLong_CheckExact(version_obj)
191+
|| PyLong_AsLong(version_obj) != _filters_version) {
192+
PyDict_Clear(registry);
193+
version_obj = PyLong_FromLong(_filters_version);
194+
if (version_obj == NULL)
195+
return -1;
196+
if (_PyDict_SetItemId(registry, &PyId_version, version_obj) < 0) {
197+
Py_DECREF(version_obj);
198+
return -1;
199+
}
200+
Py_DECREF(version_obj);
201+
}
202+
else {
203+
already_warned = PyDict_GetItem(registry, key);
204+
if (already_warned != NULL) {
205+
int rc = PyObject_IsTrue(already_warned);
206+
if (rc != 0)
207+
return rc;
208+
}
191209
}
192210

193211
/* This warning wasn't found in the registry, set it. */
@@ -750,6 +768,13 @@ warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds)
750768
registry, NULL);
751769
}
752770

771+
static PyObject *
772+
warnings_filters_mutated(PyObject *self, PyObject *args)
773+
{
774+
_filters_version++;
775+
Py_RETURN_NONE;
776+
}
777+
753778

754779
/* Function to issue a warning message; may raise an exception. */
755780

@@ -917,6 +942,8 @@ static PyMethodDef warnings_functions[] = {
917942
warn_doc},
918943
{"warn_explicit", (PyCFunction)warnings_warn_explicit,
919944
METH_VARARGS | METH_KEYWORDS, warn_explicit_doc},
945+
{"_filters_mutated", (PyCFunction)warnings_filters_mutated, METH_NOARGS,
946+
NULL},
920947
/* XXX(brett.cannon): add showwarning? */
921948
/* XXX(brett.cannon): Reasonable to add formatwarning? */
922949
{NULL, NULL} /* sentinel */
@@ -1069,5 +1096,7 @@ _PyWarnings_Init(void)
10691096
Py_INCREF(_default_action);
10701097
if (PyModule_AddObject(m, "_defaultaction", _default_action) < 0)
10711098
return NULL;
1099+
1100+
_filters_version = 0;
10721101
return m;
10731102
}

0 commit comments

Comments
 (0)
0