gh-130821: Add type information to error messages for invalid return type (GH-130835)

This commit is contained in:
Semyon Moroz 2025-08-14 08:04:41 +00:00 committed by GitHub
parent c9d7065188
commit 968f6e523a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 76 additions and 76 deletions

View file

@ -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)

View file

@ -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}

View file

@ -0,0 +1,2 @@
Enhance wrong type error messages and make them more consistent. Patch by
Semyon Moroz.

View file

@ -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 non-negative 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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -2168,8 +2168,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);
@ -3510,10 +3511,8 @@ 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,
"%N.mro() returned a non-class ('%T')", type, obj);
return -1;
}
PyTypeObject *base = (PyTypeObject*)obj;
@ -3521,8 +3520,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);
"%N.mro() returned base with unsuitable layout ('%N')",
type, base);
return -1;
}
}
@ -10419,9 +10418,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);
@ -10901,7 +10899,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;
}