From be739c11614af097168925ef8842de7054a5d7cb Mon Sep 17 00:00:00 2001 From: snoopuppy582 Date: Tue, 14 Jul 2026 21:31:53 +0900 Subject: [PATCH] gh-153128: Preserve once warnings across filter changes Keep the C implementation's once registry independent of filter version changes, matching the pure Python implementation. Add a shared regression test and a Library news entry. --- Lib/test/test_warnings/__init__.py | 10 +++ ...-07-14-21-30-00.gh-issue-153128.Xm2QvA.rst | 3 + Python/_warnings.c | 66 ++++++++++++------- 3 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-14-21-30-00.gh-issue-153128.Xm2QvA.rst diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index bf1bcf8e6ed5d9..697963e11dcc44 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -242,6 +242,16 @@ def test_once(self): 42) self.assertEqual(len(w), 0) + def test_once_after_filter_change(self): + with self.module.catch_warnings(record=True) as w: + self.module.resetwarnings() + message = "FilterTests.test_once_after_filter_change" + self.module.filterwarnings("once", message, UserWarning) + self.module.warn(message, UserWarning) + self.module.filterwarnings("ignore", "unrelated") + self.module.warn(message, UserWarning) + self.assertEqual(len(w), 1) + def test_filter_module(self): MS_WINDOWS = (sys.platform == 'win32') with self.module.catch_warnings(record=True) as w: diff --git a/Misc/NEWS.d/next/Library/2026-07-14-21-30-00.gh-issue-153128.Xm2QvA.rst b/Misc/NEWS.d/next/Library/2026-07-14-21-30-00.gh-issue-153128.Xm2QvA.rst new file mode 100644 index 00000000000000..ea116555153207 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-14-21-30-00.gh-issue-153128.Xm2QvA.rst @@ -0,0 +1,3 @@ +Fix the C implementation of :mod:`warnings` so the ``"once"`` action remains +suppressed after unrelated warning filters are changed, matching the pure +Python implementation. diff --git a/Python/_warnings.c b/Python/_warnings.c index 4f6de50efa14a8..35babb2ebf5f79 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -574,11 +574,34 @@ get_filter(PyInterpreterState *interp, PyObject *category, static int -already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key, - int should_set) +already_warned_in_registry(PyObject *registry, PyObject *key, int should_set) { PyObject *already_warned; + if (key == NULL) + return -1; + + if (PyDict_GetItemRef(registry, key, &already_warned) < 0) { + return -1; + } + if (already_warned != NULL) { + int rc = PyObject_IsTrue(already_warned); + Py_DECREF(already_warned); + if (rc != 0) + return rc; + } + + /* This warning wasn't found in the registry, set it. */ + if (should_set) + return PyDict_SetItem(registry, key, Py_True); + return 0; +} + + +static int +already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key, + int should_set) +{ if (key == NULL) return -1; @@ -607,22 +630,8 @@ already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key, } Py_DECREF(version_obj); } - else { - if (PyDict_GetItemRef(registry, key, &already_warned) < 0) { - return -1; - } - if (already_warned != NULL) { - int rc = PyObject_IsTrue(already_warned); - Py_DECREF(already_warned); - if (rc != 0) - return rc; - } - } - /* This warning wasn't found in the registry, set it. */ - if (should_set) - return PyDict_SetItem(registry, key, Py_True); - return 0; + return already_warned_in_registry(registry, key, should_set); } static int @@ -642,6 +651,22 @@ update_registry(PyInterpreterState *interp, PyObject *registry, PyObject *text, return rc; } + +static int +update_once_registry(PyInterpreterState *interp, PyObject *text, + PyObject *category) +{ + PyObject *registry = get_once_registry(interp); + if (registry == NULL) { + return -1; + } + + PyObject *key = _PyTuple_FromPair(text, category); + int rc = already_warned_in_registry(registry, key, 1); + Py_XDECREF(key); + return rc; +} + static void show_warning(PyThreadState *tstate, PyObject *filename, int lineno, PyObject *text, PyObject *category, PyObject *sourceline) @@ -854,13 +879,8 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message, } if (_PyUnicode_EqualToASCIIString(action, "once")) { - if (registry == NULL || registry == Py_None) { - registry = get_once_registry(interp); - if (registry == NULL) - goto cleanup; - } /* WarningsState.once_registry[(text, category)] = 1 */ - rc = update_registry(interp, registry, text, category, 0); + rc = update_once_registry(interp, text, category); } else if (_PyUnicode_EqualToASCIIString(action, "module")) { /* registry[(text, category, 0)] = 1 */