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]