Skip to content

gh-130821: Add type information to wrong type error messages #130835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix error message for methods return type
  • Loading branch information
donBarbos committed Apr 28, 2025
commit d97667670f7317a3076686778a0603b6599d5755
39 changes: 21 additions & 18 deletions Objects/abstract.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,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 type int (not %T)",
o, result);
Py_DECREF(result);
return -1;
}
Expand All @@ -140,7 +141,9 @@ 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 positive int (not %T)",
o, result);
return -1;
}
return res;
Expand Down Expand Up @@ -884,8 +887,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 type str (not %T)",
obj, result);
Py_SETREF(result, NULL);
goto done;
}
Expand Down Expand Up @@ -1418,13 +1421,14 @@ _PyNumber_Index(PyObject *item)

if (!PyLong_Check(result)) {
PyErr_Format(PyExc_TypeError,
"%T.__index__ returned non-int (type %T)", item, result);
"%T.__index__ must return type 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,
"%T.__index__ returned non-int (type %T). "
"%T.__index__ must return type 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.",
item, result)) {
Expand Down Expand Up @@ -1527,13 +1531,13 @@ PyNumber_Long(PyObject *o)

if (!PyLong_Check(result)) {
PyErr_Format(PyExc_TypeError,
"%T.__int__ returned non-int (type %T)", o, result);
"%T.__int__ must return type 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,
"%T.__int__ returned non-int (type %T). "
"%T.__int__ must return type 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.",
o, result)) {
Expand Down Expand Up @@ -1604,13 +1608,14 @@ PyNumber_Float(PyObject *o)

if (!PyFloat_Check(res)) {
PyErr_Format(PyExc_TypeError,
"%T.__float__ returned non-float (type %T)", o, res);
"%T.__float__ must return type 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,
"%T.__float__ returned non-float (type %T). "
"%T.__float__ must return type 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.",
o, res)) {
Expand Down Expand Up @@ -2429,10 +2434,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)",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I strongly prefer the old message. Iterable is not a type, it's a category of types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about new message: "%T.%U() must return an iterable, not %T"

Py_TYPE(o)->tp_name,
meth,
Py_TYPE(meth_output)->tp_name);
"%T.%U() must return type iterable (not %T)",
o, meth, meth_output);
}
Py_DECREF(meth_output);
return NULL;
Expand Down Expand Up @@ -2812,7 +2815,7 @@ PyObject_GetIter(PyObject *o)
PyObject *res = (*f)(o);
if (res != NULL && !PyIter_Check(res)) {
PyErr_Format(PyExc_TypeError,
"%T.iter() returned non-iterator of type '%T'",
"%T.iter() must return type iterator of type '%T'",
o, res);
Py_SETREF(res, NULL);
}
Expand All @@ -2832,8 +2835,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 type async iterator of type '%T'",
o, it);
Py_SETREF(it, NULL);
}
return it;
Expand Down
4 changes: 2 additions & 2 deletions Objects/bytesobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen)
return NULL;
if (!PyBytes_Check(result)) {
PyErr_Format(PyExc_TypeError,
"%T.__bytes__ returned non-bytes (type %T)",
"%T.__bytes__ must return type bytes (not %T)",
v, result);
Py_DECREF(result);
return NULL;
Expand Down Expand Up @@ -2762,7 +2762,7 @@ bytes_new_impl(PyTypeObject *type, PyObject *x, const char *encoding,
return NULL;
if (!PyBytes_Check(bytes)) {
PyErr_Format(PyExc_TypeError,
"%T.__bytes__ returned non-bytes (type %T)",
"%T.__bytes__ must return type bytes (not %T)",
x, bytes);
Py_DECREF(bytes);
return NULL;
Expand Down
4 changes: 2 additions & 2 deletions Objects/complexobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -499,14 +499,14 @@ try_complex_special_method(PyObject *op)
}
if (!PyComplex_Check(res)) {
PyErr_Format(PyExc_TypeError,
"%T.__complex__ returned non-complex (type %T)",
"%T.__complex__ must return type 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,
"%T.__complex__ returned non-complex (type %T). "
"%T.__complex__ must return type 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.",
op, res)) {
Expand Down
8 changes: 4 additions & 4 deletions Objects/fileobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ PyFile_GetLine(PyObject *f, int n)
}
if (result != NULL && !PyBytes_Check(result) &&
!PyUnicode_Check(result)) {
PyErr_Format(PyExc_TypeError,
"%T.readline() must return type 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)) {
Expand Down Expand Up @@ -191,8 +191,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 type int (not %T)", o, fno);
Py_DECREF(fno);
return -1;
}
Expand Down
8 changes: 4 additions & 4 deletions Objects/floatobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,11 @@
if (nb && nb->nb_index) {
PyObject *res = _PyNumber_Index(op);
if (!res) {
return -1;

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Hypothesis tests on Ubuntu

expected ‘)’ before ‘{’ token

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Cross build Linux

expected ‘)’ before ‘{’ token

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04)

expected ‘)’ before ‘{’ token

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

syntax error: missing ')' before '{' [C:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

syntax error: missing ')' before '{' [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64)

syntax error: missing ')' before '{' [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64)

syntax error: missing ')' before '{' [C:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Address sanitizer (ubuntu-24.04)

expected ‘)’ before ‘{’ token

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04)

expected ‘)’ before ‘{’ token

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04-arm)

expected ‘)’ before ‘{’ token

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu (bolt) / build and test (ubuntu-24.04)

expected ‘)’ before ‘{’ token

Check failure on line 300 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04-arm)

expected ‘)’ before ‘{’ token
}
double val = PyLong_AsDouble(res);
Py_DECREF(res);
return val;

Check failure on line 304 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Hypothesis tests on Ubuntu

expected expression before ‘}’ token

Check failure on line 304 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Cross build Linux

expected expression before ‘}’ token

Check failure on line 304 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04)

expected expression before ‘}’ token

Check failure on line 304 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Address sanitizer (ubuntu-24.04)

expected expression before ‘}’ token

Check failure on line 304 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04)

expected expression before ‘}’ token

Check failure on line 304 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04-arm)

expected expression before ‘}’ token

Check failure on line 304 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu (bolt) / build and test (ubuntu-24.04)

expected expression before ‘}’ token

Check failure on line 304 in Objects/floatobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04-arm)

expected expression before ‘}’ token
}
PyErr_Format(PyExc_TypeError, "must be real number, not %.50s",
Py_TYPE(op)->tp_name);
Expand All @@ -315,16 +315,16 @@
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 type 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 type 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;
}
Expand Down
5 changes: 3 additions & 2 deletions Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,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 type dict of type '%T'",
ann_dict);
Py_DECREF(ann_dict);
return NULL;
}
Expand Down
10 changes: 5 additions & 5 deletions Objects/genobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1076,14 +1076,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__ returned a 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 type iterator "
"of type '%T'",
o, res);
Py_CLEAR(res);
}
}
Expand Down
5 changes: 3 additions & 2 deletions Objects/iterobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,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 type iterable (not %T)",
obj, awaitable);
Py_DECREF(awaitable);
return NULL;
}
Expand Down
6 changes: 4 additions & 2 deletions Objects/moduleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1258,8 +1258,10 @@ 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,
"%T.__annotate__ must return type dict "
"of type '%T'",
self, annotations);
Py_DECREF(annotate);
Py_DECREF(annotations);
Py_DECREF(dict);
Expand Down
6 changes: 3 additions & 3 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ PyObject_Repr(PyObject *v)
}
if (!PyUnicode_Check(res)) {
_PyErr_Format(tstate, PyExc_TypeError,
"%T.__repr__ returned non-string (type %T)", v, res);
"%T.__repr__ must return type str (not %T)", v, res);
Py_DECREF(res);
return NULL;
}
Expand Down Expand Up @@ -821,7 +821,7 @@ PyObject_Str(PyObject *v)
}
if (!PyUnicode_Check(res)) {
_PyErr_Format(tstate, PyExc_TypeError,
"%T.__str__ returned non-string (type %T)", v, res);
"%T.__str__ must return type str (not %T)", v, res);
Py_DECREF(res);
return NULL;
}
Expand Down Expand Up @@ -876,7 +876,7 @@ PyObject_Bytes(PyObject *v)
return NULL;
if (!PyBytes_Check(result)) {
PyErr_Format(PyExc_TypeError,
"%T.__bytes__ returned non-bytes (type %T)",
"%T.__bytes__ must return type bytes (not %T)",
v, result);
Py_DECREF(result);
return NULL;
Expand Down
16 changes: 8 additions & 8 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2010,8 +2010,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,
"%T.__annotate__ must return type dict of "
"type '%T'", tp, annotations);
Py_DECREF(annotations);
Py_DECREF(annotate);
Py_DECREF(dict);
Expand Down Expand Up @@ -3238,10 +3239,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,
"%T.mro() must return class (not %T)", type, obj);
return -1;
}
PyTypeObject *base = (PyTypeObject*)obj;
Expand Down Expand Up @@ -9920,7 +9919,7 @@ slot_nb_bool(PyObject *self)
}
else {
PyErr_Format(PyExc_TypeError,
"%T.__bool__ returned non-bool (type %T)", self, value);
"%T.__bool__ must return type bool (not %T)", self, value);
result = -1;
}

Expand Down Expand Up @@ -10445,7 +10444,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 type memoryview (not %T)",
self, ret);
goto fail;
}

Expand Down
22 changes: 9 additions & 13 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3705,10 +3705,9 @@ PyUnicode_Decode(const char *s,
goto onError;
if (!PyUnicode_Check(unicode)) {
PyErr_Format(PyExc_TypeError,
"'%.400s' decoder returned '%.400s' instead of 'str'; "
"%s decoder must return type str (not %T); "
"use codecs.decode() to decode to arbitrary types",
encoding,
Py_TYPE(unicode)->tp_name);
encoding, unicode);
Py_DECREF(unicode);
goto onError;
}
Expand Down Expand Up @@ -3768,10 +3767,9 @@ PyUnicode_AsDecodedUnicode(PyObject *unicode,
goto onError;
if (!PyUnicode_Check(v)) {
PyErr_Format(PyExc_TypeError,
"'%.400s' decoder returned '%.400s' instead of 'str'; "
"%s decoder must return type str (not %T); "
"use codecs.decode() to decode to arbitrary types",
encoding,
Py_TYPE(unicode)->tp_name);
encoding, unicode);
Py_DECREF(v);
goto onError;
}
Expand Down Expand Up @@ -3981,7 +3979,7 @@ PyUnicode_AsEncodedString(PyObject *unicode,
PyObject *b;

error = PyErr_WarnFormat(PyExc_RuntimeWarning, 1,
"encoder %s returned bytearray instead of bytes; "
"%s encoder must return type bytes (not bytearray); "
"use codecs.encode() to encode to arbitrary types",
encoding);
if (error) {
Expand All @@ -3996,10 +3994,9 @@ PyUnicode_AsEncodedString(PyObject *unicode,
}

PyErr_Format(PyExc_TypeError,
"'%.400s' encoder returned '%.400s' instead of 'bytes'; "
"%s encoder must return type bytes (not %T); "
"use codecs.encode() to encode to arbitrary types",
encoding,
Py_TYPE(v)->tp_name);
encoding, v);
Py_DECREF(v);
return NULL;
}
Expand Down Expand Up @@ -4030,10 +4027,9 @@ PyUnicode_AsEncodedUnicode(PyObject *unicode,
goto onError;
if (!PyUnicode_Check(v)) {
PyErr_Format(PyExc_TypeError,
"'%.400s' encoder returned '%.400s' instead of 'str'; "
"%s encoder must return type str (not %T); "
"use codecs.encode() to encode to arbitrary types",
encoding,
Py_TYPE(v)->tp_name);
encoding, v);
Py_DECREF(v);
goto onError;
}
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy