]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-146358: Fix warnings.catch_warnings on Free Threading (GH-146374) (#146418)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 25 Mar 2026 16:23:21 +0000 (17:23 +0100)
committerGitHub <noreply@github.com>
Wed, 25 Mar 2026 16:23:21 +0000 (16:23 +0000)
gh-146358: Fix warnings.catch_warnings on Free Threading (GH-146374)

catch_warnings now also overrides warnings.showwarning() on Free
Threading to support custom warnings.showwarning().
(cherry picked from commit 0055140b2cf3e3a86ef9ab7a39e2083212b27c58)

Co-authored-by: Victor Stinner <vstinner@python.org>
Lib/_py_warnings.py
Lib/test/test_warnings/__init__.py

index 55f8c06959180bb548a9d21b7db1ad35b1d7b802..36513ba2e05ef4dd140be6b3cd24f10226fb8ff3 100644 (file)
@@ -647,8 +647,8 @@ class catch_warnings(object):
                 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:
@@ -656,9 +656,9 @@ class catch_warnings(object):
                 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:
@@ -673,8 +673,8 @@ class catch_warnings(object):
                 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()
 
 
index 4c3c715a8fbf0ad8be0ae1a2d81e8c4eb30354e9..799ea4939ddab593f86fa0f6a4ecd3b1b1ef223b 100644 (file)
@@ -1,5 +1,6 @@
 from contextlib import contextmanager
 import linecache
+import logging
 import os
 import importlib
 import inspect
@@ -498,6 +499,47 @@ class FilterTests(BaseTest):
                     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 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")
+
+    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