Skip to content

Commit ef8cc0f

Browse files
committed
Issue #16382: Improve exception message of warnings.warn() for bad category.
Initial patch by Phil Elson.
1 parent 356a68d commit ef8cc0f

File tree

4 files changed

+48
-7
lines changed

4 files changed

+48
-7
lines changed

Lib/test/test_warnings.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,41 @@ def __str__(self):
370370
with self.assertRaises(ValueError):
371371
self.module.warn(BadStrWarning())
372372

373+
def test_warning_classes(self):
374+
class MyWarningClass(Warning):
375+
pass
376+
377+
class NonWarningSubclass:
378+
pass
379+
380+
# passing a non-subclass of Warning should raise a TypeError
381+
with self.assertRaises(TypeError) as cm:
382+
self.module.warn('bad warning category', '')
383+
self.assertIn('category must be a Warning subclass, not ',
384+
str(cm.exception))
385+
386+
with self.assertRaises(TypeError) as cm:
387+
self.module.warn('bad warning category', NonWarningSubclass)
388+
self.assertIn('category must be a Warning subclass, not ',
389+
str(cm.exception))
390+
391+
# check that warning instances also raise a TypeError
392+
with self.assertRaises(TypeError) as cm:
393+
self.module.warn('bad warning category', MyWarningClass())
394+
self.assertIn('category must be a Warning subclass, not ',
395+
str(cm.exception))
396+
397+
with self.assertWarns(MyWarningClass) as cm:
398+
self.module.warn('good warning category', MyWarningClass)
399+
self.assertEqual('good warning category', str(cm.warning))
400+
401+
with self.assertWarns(UserWarning) as cm:
402+
self.module.warn('good warning category', None)
403+
self.assertEqual('good warning category', str(cm.warning))
404+
405+
with self.assertWarns(MyWarningClass) as cm:
406+
self.module.warn('good warning category', MyWarningClass)
407+
self.assertIsInstance(cm.warning, Warning)
373408

374409
class CWarnTests(WarnTests, unittest.TestCase):
375410
module = c_warnings

Lib/warnings.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,9 @@ def warn(message, category=None, stacklevel=1):
162162
# Check category argument
163163
if category is None:
164164
category = UserWarning
165-
assert issubclass(category, Warning)
165+
if not (isinstance(category, type) and issubclass(category, Warning)):
166+
raise TypeError("category must be a Warning subclass, "
167+
"not '{:s}'".format(type(category).__name__))
166168
# Get context information
167169
try:
168170
caller = sys._getframe(stacklevel)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ Core and Builtins
108108
Library
109109
-------
110110

111+
- Issue #16382: Improve exception message of warnings.warn() for bad
112+
category. Initial patch by Phil Elson.
113+
111114
- Issue #21932: os.read() now uses a :c:func:`Py_ssize_t` type instead of
112115
:c:type:`int` for the size to support reading more than 2 GB at once. On
113116
Windows, the size is truncted to INT_MAX. As any call to os.read(), the OS

Python/_warnings.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -619,16 +619,17 @@ get_category(PyObject *message, PyObject *category)
619619

620620
if (rc == 1)
621621
category = (PyObject*)message->ob_type;
622-
else if (category == NULL)
622+
else if (category == NULL || category == Py_None)
623623
category = PyExc_UserWarning;
624624

625625
/* Validate category. */
626626
rc = PyObject_IsSubclass(category, PyExc_Warning);
627-
if (rc == -1)
628-
return NULL;
629-
if (rc == 0) {
630-
PyErr_SetString(PyExc_ValueError,
631-
"category is not a subclass of Warning");
627+
/* category is not a subclass of PyExc_Warning or
628+
PyObject_IsSubclass raised an error */
629+
if (rc == -1 || rc == 0) {
630+
PyErr_Format(PyExc_TypeError,
631+
"category must be a Warning subclass, not '%s'",
632+
Py_TYPE(category)->tp_name);
632633
return NULL;
633634
}
634635

0 commit comments

Comments
 (0)