diff --git a/Lib/_py_warnings.py b/Lib/_py_warnings.py index ab09913de6812dd..fd8f3961d6a9e9f 100644 --- a/Lib/_py_warnings.py +++ b/Lib/_py_warnings.py @@ -250,6 +250,19 @@ def _formatwarnmsg(msg): msg.filename, msg.lineno, msg.line) return _wm._formatwarnmsg_impl(msg) +def _valid_warning_category(category): + """ + Return True if category is a Warning subclass or tuple of such. + Always perform class checks; only perform tuple iteration in debug mode. + """ + if not __debug__: + return True + if isinstance(category, type) and issubclass(category, Warning): + return True + if isinstance(category, tuple): + return all(isinstance(c, type) and issubclass(c, Warning) + for c in category) + return False def filterwarnings(action, message="", category=Warning, module="", lineno=0, append=False): @@ -267,8 +280,9 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0, raise ValueError(f"invalid action: {action!r}") if not isinstance(message, str): raise TypeError("message must be a string") - if not isinstance(category, type) or not issubclass(category, Warning): - raise TypeError("category must be a Warning subclass") + if not _valid_warning_category(category): + raise TypeError("category must be a Warning subclass, " + "not '{:s}'".format(type(category).__name__)) if not isinstance(module, str): raise TypeError("module must be a string") if not isinstance(lineno, int): @@ -303,6 +317,9 @@ def simplefilter(action, category=Warning, lineno=0, append=False): """ if action not in {"error", "ignore", "always", "all", "default", "module", "once"}: raise ValueError(f"invalid action: {action!r}") + if not _valid_warning_category(category): + raise TypeError("category must be a Warning subclass, " + "not '{:s}'".format(type(category).__name__)) if not isinstance(lineno, int): raise TypeError("lineno must be an int") if lineno < 0: diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index bf1bcf8e6ed5d9a..74c8bda413be831 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -489,6 +489,35 @@ def test_argument_validation(self): with self.assertRaises(ValueError): self.module.simplefilter('ignore', lineno=-1) + # these tests fail if python is run with -O, so check __debug__ + @unittest.skipUnless(__debug__, "Won't work if __debug__ is False") + def test_invalid_category_types(self): + with self.assertRaises(TypeError): + self.module.filterwarnings("ignore", category="notawarning") + with self.assertRaises(TypeError): + self.module.filterwarnings("ignore", category=123) + with self.assertRaises(TypeError): + self.module.filterwarnings("ignore", category=17.02) + with self.assertRaises(TypeError): + self.module.filterwarnings("ignore", category=True) + with self.assertRaises(TypeError): + self.module.filterwarnings( + "ignore", category=(UserWarning, 17) + ) + + with self.assertRaises(TypeError): + self.module.simplefilter("ignore", category="notawarning") + with self.assertRaises(TypeError): + self.module.simplefilter("ignore", category=123) + with self.assertRaises(TypeError): + self.module.filterwarnings("ignore", category=17.02) + with self.assertRaises(TypeError): + self.module.filterwarnings("ignore", category=True) + with self.assertRaises(TypeError): + self.module.simplefilter( + "ignore", category=(UserWarning, 'abc') + ) + def test_catchwarnings_with_simplefilter_ignore(self): with self.module.catch_warnings(module=self.module): self.module.resetwarnings() diff --git a/Misc/ACKS b/Misc/ACKS index fec00c1b272f4ea..90c027eaa8dbd6b 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -75,6 +75,7 @@ Emmanuel Arias Alicia Arlen Jeffrey Armstrong Justin Turner Arthur +Hasrat Ali Arzoo Jason Asbahr David Ascher Ammar Askar diff --git a/Misc/NEWS.d/next/Windows/2025-07-10-19-28-11.gh-issue-125893.lD8Nyd.rst b/Misc/NEWS.d/next/Windows/2025-07-10-19-28-11.gh-issue-125893.lD8Nyd.rst new file mode 100644 index 000000000000000..6c0a212a6c8483b --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2025-07-10-19-28-11.gh-issue-125893.lD8Nyd.rst @@ -0,0 +1,2 @@ +Added validation for the ``category`` argument in +:func:`warnings.filterwarnings` and :func:`warnings.simplefilter`.