bpo-27535: Fix memory leak with warnings ignore#4489
bpo-27535: Fix memory leak with warnings ignore#4489vstinner merged 1 commit intopython:masterfrom vstinner:warn_ignore
Conversation
serhiy-storchaka
left a comment
There was a problem hiding this comment.
I want to play with the code more.
Python/_warnings.c
Outdated
| else if (_PyUnicode_EqualToASCIIString(action, "ignore")) | ||
| } | ||
|
|
||
| if (_PyUnicode_EqualToASCIIString(action, "ignore")) |
There was a problem hiding this comment.
Isn't this already tested above?
There was a problem hiding this comment.
I removed it. I kept it for tests, but then I forgot to remove it.
Python/_warnings.c
Outdated
| if (!_PyUnicode_EqualToASCIIString(action, "always")) { | ||
| if (registry != NULL && registry != Py_None && | ||
| PyDict_SetItem(registry, key, Py_True) < 0) | ||
| PyDict_SetItem(registry, key, Py_True) < 0) { |
There was a problem hiding this comment.
If you want to add braces, place the opening brace after a multiline condition on a separate line.
There was a problem hiding this comment.
The PEP 7 only shows "if (test) {", it says nothing about multiline condition. Until a newline is required by the PEP 7, I prefer to put "{" on the same line.
There was a problem hiding this comment.
* When you break a long expression at a binary operator, the
operator goes at the end of the previous line, and braces should be
formatted as shown. E.g.::
if (type->tp_dictoffset != 0 && base->tp_dictoffset == 0 &&
type->tp_dictoffset == b_size &&
(size_t)t_size == b_size + sizeof(PyObject *))
{
return 0; /* "Forgive" adding a __dict__ only */
}
There was a problem hiding this comment.
Oh nice, it's specified in the PEP 7. In that case, ok to reformat the code as the PEP 7 requires ;-)
Lib/warnings.py
Outdated
| re.compile(module), lineno, append=append) | ||
|
|
||
| if message: | ||
| regex = re.compile(message, re.I) |
There was a problem hiding this comment.
This could be written shorter.
regex = re.compile(message, re.I) if message else None
module = re.compile(module) if module else None
_add_filter(action, regex, category, module, lineno, append=append)
Lib/test/test_warnings/__init__.py
Outdated
| self.module.filterwarnings("ignore", category=UserWarning) | ||
| self.module.warn("FilterTests.test_ignore", UserWarning) | ||
| self.assertEqual(len(w), 0) | ||
| self.assertEqual(list(__warningregistry__.keys()), |
There was a problem hiding this comment.
This line doesn't look too long for splitting it. Especially if remove redundant .keys().
|
Ooops, previously, I pushed optimization commits in this PR by mistake. It's now fixed. Again, this PR is only the change to leave the registry unchanged for the "ignore" action. |
The warnings module doesn't leak memory anymore in the hidden warnings registry for the "ignore" action of warnings filters. The warn_explicit() function doesn't add the warning key to the registry anymore for the "ignore" action.
|
I reformatted the if with multiline condition and rebased my PR. |
|
Latest reliable (using isolated CPUs) benchmark: So this PR adds a slowdown of +133 ns on warning.warn() when the warning is ignored. This slowdown can be removed using a "C cache" of warnings.filters: the optimization implemented in PR #4502. The problem is that I'm not sure that it's ok to cache warnings.filters and/or make warnings.filters "immutable" (convert it to a tuple). |
|
The overhead depends on the number and kind of filters. For warnings emitted in a C code the relative slowdown will be larger. |
|
@serhiy-storchaka: Are you ok to merge this PR even with the "1.2x slower" (+19%)? Or do you prefer to wait until a decision can be made on a "C cache" for warnings.filters? |
|
Could you please make a benchmark for warnings emitted in a tight loop in the C code. It would be nice to know what is the worst case. If it is less than 2x slower I will prefer this simpler PR. |
It's not 2x slower, it's between 1.03x slower (+21 ns) and 1.09x slower (+79 ns): |
|
Thanks @vstinner for the PR 🌮🎉.. I'm working now to backport this PR to: 3.6. |
The warnings module doesn't leak memory anymore in the hidden warnings registry for the "ignore" action of warnings filters. The warn_explicit() function doesn't add the warning key to the registry anymore for the "ignore" action. (cherry picked from commit c975878)
|
GH-4587 is a backport of this pull request to the 3.6 branch. |
|
I backported manually the change to Python 2.7: PR #4588. |
The warnings module doesn't leak memory anymore in the hidden
warnings registry for the "ignore" action of warnings filters.
The warn_explicit() function doesn't add the warning key to the
registry anymore for the "ignore" action.
https://bugs.python.org/issue27535