]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-125854: Improve error messages for invalid category in the warnings module (GH...
authorSerhiy Storchaka <storchaka@gmail.com>
Thu, 14 Aug 2025 11:59:04 +0000 (14:59 +0300)
committerGitHub <noreply@github.com>
Thu, 14 Aug 2025 11:59:04 +0000 (14:59 +0300)
Include the type name if the category is a type, but not a Warning
subclass, instead of just 'type'.

Lib/_py_warnings.py
Lib/test/test_warnings/__init__.py
Misc/NEWS.d/next/Library/2025-08-14-10-27-07.gh-issue-125854.vDzFcZ.rst [new file with mode: 0644]
Python/_warnings.c

index cbaa94458629ac64280fe32bd4246cb5a42d66ad..5070caea6bb05488507afcd15277b4fbe4b65770 100644 (file)
@@ -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.')
index f89e94449b30310633720f79778dabcef87953ae..694cfc97064c30a1e83efac9985c0db0e0e87400 100644 (file)
@@ -596,25 +596,19 @@ class WarnTests(BaseTest):
         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 (file)
index 0000000..40925a4
--- /dev/null
@@ -0,0 +1 @@
+Improve error messages for invalid category in :func:`warnings.warn`.
index 12e6172b0cf82813556ee803afb2aa4a3083cfd7..243a5e88e9dbbc141d25887394fce3125eab0757 100644 (file)
@@ -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;
     }