From c9b21ef3488a98bd29a085b6e4dd442d48f71ca7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 24 Mar 2026 13:51:47 +0100 Subject: [PATCH 1/3] gh-146358: Fix warnings.catch_warnings on Free Threading catch_warnings now also overrides warnings.showwarning() on Free Threading to support custom warnings.showwarning(). --- Lib/_py_warnings.py | 10 +++++----- Lib/test/test_warnings/__init__.py | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Lib/_py_warnings.py b/Lib/_py_warnings.py index d5a9cec86f3674..81a386c4487d95 100644 --- a/Lib/_py_warnings.py +++ b/Lib/_py_warnings.py @@ -703,8 +703,8 @@ def __enter__(self): context = None self._filters = self._module.filters self._module.filters = self._filters[:] - self._showwarning = self._module.showwarning self._showwarnmsg_impl = self._module._showwarnmsg_impl + self._showwarning = self._module.showwarning self._module._filters_mutated_lock_held() if self._record: if _use_context: @@ -712,9 +712,9 @@ def __enter__(self): else: log = [] self._module._showwarnmsg_impl = log.append - # Reset showwarning() to the default implementation to make sure - # that _showwarnmsg() calls _showwarnmsg_impl() - self._module.showwarning = self._module._showwarning_orig + # Reset showwarning() to the default implementation to make sure + # that _showwarnmsg() calls _showwarnmsg_impl() + self._module.showwarning = self._module._showwarning_orig else: log = None if self._filter is not None: @@ -729,8 +729,8 @@ def __exit__(self, *exc_info): self._module._warnings_context.set(self._saved_context) else: self._module.filters = self._filters - self._module.showwarning = self._showwarning self._module._showwarnmsg_impl = self._showwarnmsg_impl + self._module.showwarning = self._showwarning self._module._filters_mutated_lock_held() diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index a6af5057cc8968..ed3bed08ca5331 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -509,6 +509,28 @@ def test_catchwarnings_with_simplefilter_error(self): stderr = stderr.getvalue() self.assertIn(error_msg, stderr) + def test_catchwarnings_with_showwarning(self): + # gh-146358: catch_warnings must override warnings.showwarning() + # if it's not the default implementation. + + warns = [] + def custom_showwarning(message, category, filename, lineno, + file=None, line=None): + warns.append(message) + + with support.swap_attr(self.module, 'showwarning', custom_showwarning): + with self.module.catch_warnings(record=True) as recorded: + self.module.warn("recorded") + self.assertEqual(len(recorded), 1) + self.assertEqual(str(recorded[0].message), 'recorded') + self.assertIs(self.module.showwarning, custom_showwarning) + + self.module.warn("custom") + + self.assertEqual(len(warns), 1) + self.assertEqual(str(warns[0]), "custom") + + class CFilterTests(FilterTests, unittest.TestCase): module = c_warnings From 26e3e9467dfdbd123732e2aed9eef12f14130ab7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 25 Mar 2026 07:57:44 +0100 Subject: [PATCH 2/3] Fix tests when run with -Werror --- Lib/test/test_warnings/__init__.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index ed3bed08ca5331..f702ba533e1e71 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -518,14 +518,18 @@ def custom_showwarning(message, category, filename, lineno, file=None, line=None): warns.append(message) - with support.swap_attr(self.module, 'showwarning', custom_showwarning): - with self.module.catch_warnings(record=True) as recorded: - self.module.warn("recorded") - self.assertEqual(len(recorded), 1) - self.assertEqual(str(recorded[0].message), 'recorded') - self.assertIs(self.module.showwarning, custom_showwarning) - - self.module.warn("custom") + with self.module.catch_warnings(): + self.module.resetwarnings() + + with support.swap_attr(self.module, 'showwarning', + custom_showwarning): + with self.module.catch_warnings(record=True) as recorded: + self.module.warn("recorded") + self.assertEqual(len(recorded), 1) + self.assertEqual(str(recorded[0].message), 'recorded') + self.assertIs(self.module.showwarning, custom_showwarning) + + self.module.warn("custom") self.assertEqual(len(warns), 1) self.assertEqual(str(warns[0]), "custom") From fca2d39fa8805ed5fc65821d8f118967d5d0e438 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 25 Mar 2026 13:04:06 +0100 Subject: [PATCH 3/3] Test also logging.captureWarnings(True) --- Lib/test/test_warnings/__init__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index f702ba533e1e71..d86844c1a29a9a 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -1,5 +1,6 @@ from contextlib import contextmanager import linecache +import logging import os import importlib import inspect @@ -534,6 +535,21 @@ def custom_showwarning(message, category, filename, lineno, self.assertEqual(len(warns), 1) self.assertEqual(str(warns[0]), "custom") + def test_catchwarnings_logging(self): + # gh-146358: catch_warnings(record=True) must replace the + # showwarning() function set by logging.captureWarnings(True). + + with self.module.catch_warnings(): + self.module.resetwarnings() + logging.captureWarnings(True) + + with self.module.catch_warnings(record=True) as recorded: + self.module.warn("recorded") + self.assertEqual(len(recorded), 1) + self.assertEqual(str(recorded[0].message), 'recorded') + + logging.captureWarnings(False) + class CFilterTests(FilterTests, unittest.TestCase): module = c_warnings