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; }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: