diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 4755046fe1904d..763077b0586bdd 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -1008,7 +1008,7 @@ async def foo(): return (await Awaitable()) with self.assertRaisesRegex( - TypeError, "__await__.*returned non-iterator of type"): + TypeError, "__await__.*must return an iterator, not"): run_async(foo()) @@ -1106,7 +1106,7 @@ async def foo(): return await Awaitable() with self.assertRaisesRegex( - TypeError, r"__await__\(\) returned a coroutine"): + TypeError, r"__await__\(\) must return an iterator, not coroutine"): run_async(foo()) c.close() @@ -1120,7 +1120,7 @@ async def foo(): return await Awaitable() with self.assertRaisesRegex( - TypeError, "__await__.*returned non-iterator of type"): + TypeError, "__await__.*must return an iterator, not"): run_async(foo()) @@ -2490,7 +2490,7 @@ async def foo(): return (await future) with self.assertRaisesRegex( - TypeError, "__await__.*returned non-iterator of type 'int'"): + TypeError, "__await__.*must return an iterator, not int"): self.assertEqual(foo().send(None), 1) diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py index c66cb058552643..1415bbca22707c 100644 --- a/Lib/test/test_type_annotations.py +++ b/Lib/test/test_type_annotations.py @@ -309,7 +309,7 @@ def check_annotations(self, f): print(f.__annotations__) f.__annotate__ = lambda x: 42 - with self.assertRaisesRegex(TypeError, r"__annotate__ returned non-dict of type 'int'"): + with self.assertRaisesRegex(TypeError, r"__annotate__\(\) must return a dict, not int"): print(f.__annotations__) f.__annotate__ = lambda x: {"x": x} diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-28-18-59-11.gh-issue-130821.B11LU1.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-28-18-59-11.gh-issue-130821.B11LU1.rst new file mode 100644 index 00000000000000..09ffaf80a6d535 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-28-18-59-11.gh-issue-130821.B11LU1.rst @@ -0,0 +1,2 @@ +Enhance wrong type error messages and make them more consistent. Patch by +Semyon Moroz. diff --git a/Objects/abstract.c b/Objects/abstract.c index df96b935eccb44..50abb16e78cbb6 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -132,8 +132,9 @@ PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue) return defaultvalue; } if (!PyLong_Check(result)) { - PyErr_Format(PyExc_TypeError, "__length_hint__ must be an integer, not %.100s", - Py_TYPE(result)->tp_name); + PyErr_Format(PyExc_TypeError, + "%T.__length_hint__() must return an int, not %T", + o, result); Py_DECREF(result); return -1; } @@ -143,7 +144,8 @@ PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue) return -1; } if (res < 0) { - PyErr_Format(PyExc_ValueError, "__length_hint__() should return >= 0"); + PyErr_Format(PyExc_ValueError, + "%T.__length_hint__() must return a positive int", o); return -1; } return res; @@ -887,8 +889,8 @@ PyObject_Format(PyObject *obj, PyObject *format_spec) if (result && !PyUnicode_Check(result)) { PyErr_Format(PyExc_TypeError, - "__format__ must return a str, not %.200s", - Py_TYPE(result)->tp_name); + "%T.__format__() must return a str, not %T", + obj, result); Py_SETREF(result, NULL); goto done; } @@ -1421,17 +1423,17 @@ _PyNumber_Index(PyObject *item) if (!PyLong_Check(result)) { PyErr_Format(PyExc_TypeError, - "__index__ returned non-int (type %.200s)", - Py_TYPE(result)->tp_name); + "%T.__index__() must return an int, not %T", + item, result); Py_DECREF(result); return NULL; } /* Issue #17576: warn if 'result' not of exact type int. */ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "__index__ returned non-int (type %.200s). " + "%T.__index__() must return an int, not %T. " "The ability to return an instance of a strict subclass of int " "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(result)->tp_name)) { + item, result)) { Py_DECREF(result); return NULL; } @@ -1531,17 +1533,17 @@ PyNumber_Long(PyObject *o) if (!PyLong_Check(result)) { PyErr_Format(PyExc_TypeError, - "__int__ returned non-int (type %.200s)", - Py_TYPE(result)->tp_name); + "%T.__int__() must return an int, not %T", + o, result); Py_DECREF(result); return NULL; } /* Issue #17576: warn if 'result' not of exact type int. */ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "__int__ returned non-int (type %.200s). " + "%T.__int__() must return an int, not %T. " "The ability to return an instance of a strict subclass of int " "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(result)->tp_name)) { + o, result)) { Py_DECREF(result); return NULL; } @@ -1609,17 +1611,16 @@ PyNumber_Float(PyObject *o) if (!PyFloat_Check(res)) { PyErr_Format(PyExc_TypeError, - "%.50s.__float__ returned non-float (type %.50s)", - Py_TYPE(o)->tp_name, Py_TYPE(res)->tp_name); + "%T.__float__() must return a float, not %T", o, res); Py_DECREF(res); return NULL; } /* Issue #26983: warn if 'res' not of exact type float. */ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "%.50s.__float__ returned non-float (type %.50s). " + "%T.__float__() must return a float, not %T. " "The ability to return an instance of a strict subclass of float " "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(o)->tp_name, Py_TYPE(res)->tp_name)) { + o, res)) { Py_DECREF(res); return NULL; } @@ -2435,10 +2436,8 @@ method_output_as_list(PyObject *o, PyObject *meth) PyThreadState *tstate = _PyThreadState_GET(); if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError)) { _PyErr_Format(tstate, PyExc_TypeError, - "%.200s.%U() returned a non-iterable (type %.200s)", - Py_TYPE(o)->tp_name, - meth, - Py_TYPE(meth_output)->tp_name); + "%T.%U() must return an iterable, not %T", + o, meth, meth_output); } Py_DECREF(meth_output); return NULL; @@ -2818,9 +2817,8 @@ PyObject_GetIter(PyObject *o) PyObject *res = (*f)(o); if (res != NULL && !PyIter_Check(res)) { PyErr_Format(PyExc_TypeError, - "iter() returned non-iterator " - "of type '%.100s'", - Py_TYPE(res)->tp_name); + "%T.iter() must return an iterator, not %T", + o, res); Py_SETREF(res, NULL); } return res; @@ -2839,8 +2837,8 @@ PyObject_GetAIter(PyObject *o) { PyObject *it = (*f)(o); if (it != NULL && !PyAIter_Check(it)) { PyErr_Format(PyExc_TypeError, - "aiter() returned not an async iterator of type '%.100s'", - Py_TYPE(it)->tp_name); + "%T.aiter() must return an async iterator, not %T", + o, it); Py_SETREF(it, NULL); } return it; diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 87ea1162e03513..933a371f6bbcc0 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -566,8 +566,8 @@ format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen) return NULL; if (!PyBytes_Check(result)) { PyErr_Format(PyExc_TypeError, - "__bytes__ returned non-bytes (type %.200s)", - Py_TYPE(result)->tp_name); + "%T.__bytes__() must return a bytes, not %T", + v, result); Py_DECREF(result); return NULL; } @@ -2793,8 +2793,8 @@ bytes_new_impl(PyTypeObject *type, PyObject *x, const char *encoding, return NULL; if (!PyBytes_Check(bytes)) { PyErr_Format(PyExc_TypeError, - "__bytes__ returned non-bytes (type %.200s)", - Py_TYPE(bytes)->tp_name); + "%T.__bytes__() must return a bytes, not %T", + x, bytes); Py_DECREF(bytes); return NULL; } diff --git a/Objects/complexobject.c b/Objects/complexobject.c index b66ebe131ae605..9a5e11289a30a7 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -515,17 +515,17 @@ try_complex_special_method(PyObject *op) } if (!PyComplex_Check(res)) { PyErr_Format(PyExc_TypeError, - "__complex__ returned non-complex (type %.200s)", - Py_TYPE(res)->tp_name); + "%T.__complex__() must return a complex, not %T", + op, res); Py_DECREF(res); return NULL; } /* Issue #29894: warn if 'res' not of exact type complex. */ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "__complex__ returned non-complex (type %.200s). " + "%T.__complex__() must return a complex, not %T. " "The ability to return an instance of a strict subclass of complex " "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(res)->tp_name)) { + op, res)) { Py_DECREF(res); return NULL; } diff --git a/Objects/fileobject.c b/Objects/fileobject.c index e624405bd5f62f..05c3e75b4642ee 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -68,9 +68,9 @@ PyFile_GetLine(PyObject *f, int n) } if (result != NULL && !PyBytes_Check(result) && !PyUnicode_Check(result)) { + PyErr_Format(PyExc_TypeError, + "%T.readline() must return a str, not %T", f, result); Py_SETREF(result, NULL); - PyErr_SetString(PyExc_TypeError, - "object.readline() returned non-string"); } if (n < 0 && result != NULL && PyBytes_Check(result)) { @@ -193,8 +193,8 @@ PyObject_AsFileDescriptor(PyObject *o) Py_DECREF(fno); } else { - PyErr_SetString(PyExc_TypeError, - "fileno() returned a non-integer"); + PyErr_Format(PyExc_TypeError, + "%T.fileno() must return an int, not %T", o, fno); Py_DECREF(fno); return -1; } diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 93e1973d6b32fc..4e54e0829d3def 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -288,16 +288,16 @@ PyFloat_AsDouble(PyObject *op) if (!PyFloat_CheckExact(res)) { if (!PyFloat_Check(res)) { PyErr_Format(PyExc_TypeError, - "%.50s.__float__ returned non-float (type %.50s)", - Py_TYPE(op)->tp_name, Py_TYPE(res)->tp_name); + "%T.__float__() must return a float, not %T", + op, res); Py_DECREF(res); return -1; } if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "%.50s.__float__ returned non-float (type %.50s). " + "%T.__float__() must return a float, not %T. " "The ability to return an instance of a strict subclass of float " "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(op)->tp_name, Py_TYPE(res)->tp_name)) { + op, res)) { Py_DECREF(res); return -1; } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 9532c21fc7082e..d8a10075578087 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -560,8 +560,9 @@ func_get_annotation_dict(PyFunctionObject *op) return NULL; } if (!PyDict_Check(ann_dict)) { - PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'", - Py_TYPE(ann_dict)->tp_name); + PyErr_Format(PyExc_TypeError, + "__annotate__() must return a dict, not %T", + ann_dict); Py_DECREF(ann_dict); return NULL; } diff --git a/Objects/genobject.c b/Objects/genobject.c index 3e7d6257006cfd..bcde9e1a7be07e 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1092,14 +1092,14 @@ _PyCoro_GetAwaitableIter(PyObject *o) if (PyCoro_CheckExact(res) || gen_is_coroutine(res)) { /* __await__ must return an *iterator*, not a coroutine or another awaitable (see PEP 492) */ - PyErr_SetString(PyExc_TypeError, - "__await__() returned a coroutine"); + PyErr_Format(PyExc_TypeError, + "%T.__await__() must return an iterator, " + "not coroutine", o); Py_CLEAR(res); } else if (!PyIter_Check(res)) { PyErr_Format(PyExc_TypeError, - "__await__() returned non-iterator " - "of type '%.100s'", - Py_TYPE(res)->tp_name); + "%T.__await__() must return an iterator, " + "not %T", o, res); Py_CLEAR(res); } } diff --git a/Objects/iterobject.c b/Objects/iterobject.c index 5712e02ae828ab..e323987601d5d4 100644 --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -357,8 +357,9 @@ anextawaitable_getiter(anextawaitableobject *obj) } Py_SETREF(awaitable, new_awaitable); if (!PyIter_Check(awaitable)) { - PyErr_SetString(PyExc_TypeError, - "__await__ returned a non-iterable"); + PyErr_Format(PyExc_TypeError, + "%T.__await__() must return an iterable, not %T", + obj, awaitable); Py_DECREF(awaitable); return NULL; } diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 862395e7881870..47681e4251849c 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -1329,8 +1329,9 @@ module_get_annotations(PyObject *self, void *Py_UNUSED(ignored)) return NULL; } if (!PyDict_Check(annotations)) { - PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'", - Py_TYPE(annotations)->tp_name); + PyErr_Format(PyExc_TypeError, + "__annotate__() must return a dict, not %T", + annotations); Py_DECREF(annotate); Py_DECREF(annotations); Py_DECREF(dict); diff --git a/Objects/object.c b/Objects/object.c index 3ed7d55593dffa..4914e41819fd51 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -784,8 +784,7 @@ PyObject_Repr(PyObject *v) } if (!PyUnicode_Check(res)) { _PyErr_Format(tstate, PyExc_TypeError, - "__repr__ returned non-string (type %.200s)", - Py_TYPE(res)->tp_name); + "%T.__repr__() must return a str, not %T", v, res); Py_DECREF(res); return NULL; } @@ -827,8 +826,7 @@ PyObject_Str(PyObject *v) } if (!PyUnicode_Check(res)) { _PyErr_Format(tstate, PyExc_TypeError, - "__str__ returned non-string (type %.200s)", - Py_TYPE(res)->tp_name); + "%T.__str__() must return a str, not %T", v, res); Py_DECREF(res); return NULL; } @@ -883,8 +881,8 @@ PyObject_Bytes(PyObject *v) return NULL; if (!PyBytes_Check(result)) { PyErr_Format(PyExc_TypeError, - "__bytes__ returned non-bytes (type %.200s)", - Py_TYPE(result)->tp_name); + "%T.__bytes__() must return a bytes, not %T", + v, result); Py_DECREF(result); return NULL; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e84278d13c3e9c..c878cbb5b551a4 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2164,8 +2164,9 @@ type_get_annotations(PyObject *tp, void *Py_UNUSED(closure)) return NULL; } if (!PyDict_Check(annotations)) { - PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'", - Py_TYPE(annotations)->tp_name); + PyErr_Format(PyExc_TypeError, + "__annotate__() must return a dict, not %T", + annotations); Py_DECREF(annotations); Py_DECREF(annotate); Py_DECREF(dict); @@ -3506,10 +3507,9 @@ mro_check(PyTypeObject *type, PyObject *mro) for (i = 0; i < n; i++) { PyObject *obj = PyTuple_GET_ITEM(mro, i); if (!PyType_Check(obj)) { - PyErr_Format( - PyExc_TypeError, - "mro() returned a non-class ('%.500s')", - Py_TYPE(obj)->tp_name); + PyErr_Format(PyExc_TypeError, + "%s.mro() returned a non-class ('%T')", + type->tp_name, obj); return -1; } PyTypeObject *base = (PyTypeObject*)obj; @@ -3517,8 +3517,8 @@ mro_check(PyTypeObject *type, PyObject *mro) if (!is_subtype_with_mro(lookup_tp_mro(solid), solid, solid_base(base))) { PyErr_Format( PyExc_TypeError, - "mro() returned base with unsuitable layout ('%.500s')", - base->tp_name); + "%s.mro() returned base with unsuitable layout ('%.500s')", + type->tp_name, base->tp_name); return -1; } } @@ -10415,9 +10415,8 @@ slot_nb_bool(PyObject *self) } else { PyErr_Format(PyExc_TypeError, - "__bool__ should return " - "bool, returned %s", - Py_TYPE(value)->tp_name); + "%T.__bool__() must return a bool, not %T", + self, value); result = -1; } Py_DECREF(value); @@ -10892,7 +10891,8 @@ slot_bf_getbuffer(PyObject *self, Py_buffer *buffer, int flags) } if (!PyMemoryView_Check(ret)) { PyErr_Format(PyExc_TypeError, - "__buffer__ returned non-memoryview object"); + "%T.__buffer__() must return a memoryview, not %T", + self, ret); goto fail; }