Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
warnings.filters becomes an immutable tuple
Make it more explicit that warnings.filters must not be replaced
directly, but modified by:

* filterwarning()
* simplefilter()
* resetwarnings()
  • Loading branch information
vstinner committed Nov 22, 2017
commit caf78aabd7608154370bf2fd12b5431ef28d99af
11 changes: 5 additions & 6 deletions Lib/test/test_warnings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,15 +850,14 @@ def test_issue31416(self):
# bad warnings.filters or warnings.defaultaction.
wmod = self.module
with original_warnings.catch_warnings(module=wmod):
wmod.filters = [(None, None, Warning, None, 0)]
with self.assertRaises(TypeError):
wmod._filters_mutated()
wmod._add_filter(None, None, Warning, None, 0)
wmod.warn_explicit('foo', Warning, 'bar', 1)

wmod.filters = []
with original_warnings.catch_warnings(module=wmod):
with support.swap_attr(wmod, 'defaultaction', None), \
self.assertRaises(TypeError):
wmod._filters_mutated()
wmod.resetwarnings()
wmod.warn_explicit('foo', Warning, 'bar', 1)

@support.cpython_only
Expand Down Expand Up @@ -1016,14 +1015,14 @@ def test_catch_warnings_defaults(self):
with wmod.catch_warnings(module=wmod) as w:
self.assertIsNone(w)
self.assertIs(wmod.showwarning, orig_showwarning)
self.assertIsNot(wmod.filters, orig_filters)
self.assertIs(wmod.filters, orig_filters)
self.assertIs(wmod.filters, orig_filters)
if wmod is sys.modules['warnings']:
# Ensure the default module is this one
with wmod.catch_warnings() as w:
self.assertIsNone(w)
self.assertIs(wmod.showwarning, orig_showwarning)
self.assertIsNot(wmod.filters, orig_filters)
self.assertIs(wmod.filters, orig_filters)
self.assertIs(wmod.filters, orig_filters)

def test_record_override_showwarning_before(self):
Expand Down
19 changes: 12 additions & 7 deletions Lib/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,26 @@ def simplefilter(action, category=Warning, lineno=0, append=False):
_add_filter(action, None, category, None, lineno, append=append)

def _add_filter(*item, append):
global filters
# Remove possible duplicate filters, so new one will be placed
# in correct place. If append=True and duplicate exists, do nothing.
filters_list = list(filters)
if not append:
try:
filters.remove(item)
filters_list.remove(item)
except ValueError:
pass
filters.insert(0, item)
filters_list.insert(0, item)
else:
if item not in filters:
filters.append(item)
if item not in filters_list:
filters_list.append(item)
filters = tuple(filters_list)
_filters_mutated()

def resetwarnings():
"""Clear the list of warning filters, so that no filters are active."""
filters[:] = []
global filters
filters = ()
_filters_mutated()

class _OptionError(Exception):
Expand Down Expand Up @@ -462,7 +466,7 @@ def __enter__(self):
raise RuntimeError("Cannot enter %r twice" % self)
self._entered = True
self._filters = self._module.filters
self._module.filters = self._filters[:]
self._module.filters = tuple(self._filters)
self._module._filters_mutated()
self._showwarning = self._module.showwarning
self._showwarnmsg_impl = self._module._showwarnmsg_impl
Expand Down Expand Up @@ -499,8 +503,9 @@ def __exit__(self, *exc_info):
defaultaction = _defaultaction
onceregistry = _onceregistry
_warnings_defaults = True
filters = tuple(filters)
except ImportError:
filters = []
filters = ()
defaultaction = "default"
onceregistry = {}

Expand Down
16 changes: 10 additions & 6 deletions Python/_warnings.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,10 @@ get_filters(void)
Py_SETREF(_PyRuntime.warnings.filters, warnings_filters);
}

PyObject *filters_list = _PyRuntime.warnings.filters;
if (filters_list == NULL || !PyList_Check(filters_list)) {
PyObject *filters_tuple = _PyRuntime.warnings.filters;
if (filters_tuple == NULL || !PyTuple_Check(filters_tuple)) {
PyErr_SetString(PyExc_ValueError,
MODULE_NAME ".filters must be a list");
MODULE_NAME ".filters must be a tuple");
return NULL;
}

Expand All @@ -283,8 +283,9 @@ get_filters(void)
new_filters.version = _PyRuntime.warnings.filters_version;

/* _PyRuntime.warnings.filters could change while we are iterating over it. */
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(filters_list); i++) {
PyObject *item = PyList_GET_ITEM(filters_list, i);
Py_ssize_t nfilter = PyTuple_GET_SIZE(filters_tuple);
for (Py_ssize_t i = 0; i < nfilter; i++) {
PyObject *item = PyTuple_GET_ITEM(filters_tuple, i);
if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 5) {
PyErr_Format(PyExc_ValueError,
MODULE_NAME ".filters item %zd isn't a 5-tuple", i);
Expand Down Expand Up @@ -1408,7 +1409,10 @@ init_filters(void)
}
}

return filters;
PyObject *filters_tuple = PyList_AsTuple(filters);
Py_DECREF(filters);

return filters_tuple;
}

static struct PyModuleDef warningsmodule = {
Expand Down