https://github.com/python/cpython/commit/c47ffbf1a3615efac58a6ec0238929ac3c65bfea
commit: c47ffbf1a3615efac58a6ec0238929ac3c65bfea
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-08-14T14:59:04+03:00
summary:

gh-125854: Improve error messages for invalid category in the warnings module 
(GH-137750)

Include the type name if the category is a type, but not a Warning
subclass, instead of just 'type'.

files:
A Misc/NEWS.d/next/Library/2025-08-14-10-27-07.gh-issue-125854.vDzFcZ.rst
M Lib/_py_warnings.py
M Lib/test/test_warnings/__init__.py
M Python/_warnings.c

diff --git a/Lib/_py_warnings.py b/Lib/_py_warnings.py
index cbaa94458629ac..5070caea6bb054 100644
--- a/Lib/_py_warnings.py
+++ b/Lib/_py_warnings.py
@@ -449,9 +449,12 @@ def warn(message, category=None, stacklevel=1, source=None,
     # Check category argument
     if category is None:
         category = UserWarning
-    if not (isinstance(category, type) and issubclass(category, Warning)):
-        raise TypeError("category must be a Warning subclass, "
-                        "not '{:s}'".format(type(category).__name__))
+    elif not isinstance(category, type):
+        raise TypeError(f"category must be a Warning subclass, not "
+                        f"'{type(category).__name__}'")
+    elif not issubclass(category, Warning):
+        raise TypeError(f"category must be a Warning subclass, not "
+                        f"class '{category.__name__}'")
     if not isinstance(skip_file_prefixes, tuple):
         # The C version demands a tuple for implementation performance.
         raise TypeError('skip_file_prefixes must be a tuple of strs.')
diff --git a/Lib/test/test_warnings/__init__.py 
b/Lib/test/test_warnings/__init__.py
index f89e94449b3031..694cfc97064c30 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -596,25 +596,19 @@ def test_warning_classes(self):
         class MyWarningClass(Warning):
             pass
 
-        class NonWarningSubclass:
-            pass
-
         # passing a non-subclass of Warning should raise a TypeError
-        with self.assertRaises(TypeError) as cm:
+        expected = "category must be a Warning subclass, not 'str'"
+        with self.assertRaisesRegex(TypeError, expected):
             self.module.warn('bad warning category', '')
-        self.assertIn('category must be a Warning subclass, not ',
-                      str(cm.exception))
 
-        with self.assertRaises(TypeError) as cm:
-            self.module.warn('bad warning category', NonWarningSubclass)
-        self.assertIn('category must be a Warning subclass, not ',
-                      str(cm.exception))
+        expected = "category must be a Warning subclass, not class 'int'"
+        with self.assertRaisesRegex(TypeError, expected):
+            self.module.warn('bad warning category', int)
 
         # check that warning instances also raise a TypeError
-        with self.assertRaises(TypeError) as cm:
+        expected = "category must be a Warning subclass, not 
'.*MyWarningClass'"
+        with self.assertRaisesRegex(TypeError, expected):
             self.module.warn('bad warning category', MyWarningClass())
-        self.assertIn('category must be a Warning subclass, not ',
-                      str(cm.exception))
 
         with self.module.catch_warnings():
             self.module.resetwarnings()
diff --git 
a/Misc/NEWS.d/next/Library/2025-08-14-10-27-07.gh-issue-125854.vDzFcZ.rst 
b/Misc/NEWS.d/next/Library/2025-08-14-10-27-07.gh-issue-125854.vDzFcZ.rst
new file mode 100644
index 00000000000000..40925a4ab19fbc
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-08-14-10-27-07.gh-issue-125854.vDzFcZ.rst
@@ -0,0 +1 @@
+Improve error messages for invalid category in :func:`warnings.warn`.
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 12e6172b0cf828..243a5e88e9dbbc 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -823,11 +823,7 @@ warn_explicit(PyThreadState *tstate, PyObject *category, 
PyObject *message,
 
     /* Normalize message. */
     Py_INCREF(message);  /* DECREF'ed in cleanup. */
-    rc = PyObject_IsInstance(message, PyExc_Warning);
-    if (rc == -1) {
-        goto cleanup;
-    }
-    if (rc == 1) {
+    if (PyObject_TypeCheck(message, (PyTypeObject *)PyExc_Warning)) {
         text = PyObject_Str(message);
         if (text == NULL)
             goto cleanup;
@@ -1124,26 +1120,25 @@ setup_context(Py_ssize_t stack_level,
 static PyObject *
 get_category(PyObject *message, PyObject *category)
 {
-    int rc;
-
-    /* Get category. */
-    rc = PyObject_IsInstance(message, PyExc_Warning);
-    if (rc == -1)
-        return NULL;
-
-    if (rc == 1)
-        category = (PyObject*)Py_TYPE(message);
-    else if (category == NULL || category == Py_None)
-        category = PyExc_UserWarning;
+    if (PyObject_TypeCheck(message, (PyTypeObject *)PyExc_Warning)) {
+        /* Ignore the category argument. */
+        return (PyObject*)Py_TYPE(message);
+    }
+    if (category == NULL || category == Py_None) {
+        return PyExc_UserWarning;
+    }
 
     /* Validate category. */
-    rc = PyObject_IsSubclass(category, PyExc_Warning);
-    /* category is not a subclass of PyExc_Warning or
-       PyObject_IsSubclass raised an error */
-    if (rc == -1 || rc == 0) {
+    if (!PyType_Check(category)) {
+        PyErr_Format(PyExc_TypeError,
+                     "category must be a Warning subclass, not '%T'",
+                     category);
+        return NULL;
+    }
+    if (!PyType_IsSubtype((PyTypeObject *)category, (PyTypeObject 
*)PyExc_Warning)) {
         PyErr_Format(PyExc_TypeError,
-                     "category must be a Warning subclass, not '%s'",
-                     Py_TYPE(category)->tp_name);
+                     "category must be a Warning subclass, not class '%N'",
+                     (PyTypeObject *)category);
         return NULL;
     }
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to