Skip to content

Commit 968f6e5

Browse files
authored
gh-130821: Add type information to error messages for invalid return type (GH-130835)
1 parent c9d7065 commit 968f6e5

14 files changed

+76
-76
lines changed

Lib/test/test_coroutines.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,7 @@ async def foo():
10081008
return (await Awaitable())
10091009

10101010
with self.assertRaisesRegex(
1011-
TypeError, "__await__.*returned non-iterator of type"):
1011+
TypeError, "__await__.*must return an iterator, not"):
10121012

10131013
run_async(foo())
10141014

@@ -1106,7 +1106,7 @@ async def foo():
11061106
return await Awaitable()
11071107

11081108
with self.assertRaisesRegex(
1109-
TypeError, r"__await__\(\) returned a coroutine"):
1109+
TypeError, r"__await__\(\) must return an iterator, not coroutine"):
11101110
run_async(foo())
11111111

11121112
c.close()
@@ -1120,7 +1120,7 @@ async def foo():
11201120
return await Awaitable()
11211121

11221122
with self.assertRaisesRegex(
1123-
TypeError, "__await__.*returned non-iterator of type"):
1123+
TypeError, "__await__.*must return an iterator, not"):
11241124

11251125
run_async(foo())
11261126

@@ -2490,7 +2490,7 @@ async def foo():
24902490
return (await future)
24912491

24922492
with self.assertRaisesRegex(
2493-
TypeError, "__await__.*returned non-iterator of type 'int'"):
2493+
TypeError, "__await__.*must return an iterator, not int"):
24942494
self.assertEqual(foo().send(None), 1)
24952495

24962496

Lib/test/test_type_annotations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ def check_annotations(self, f):
309309
print(f.__annotations__)
310310

311311
f.__annotate__ = lambda x: 42
312-
with self.assertRaisesRegex(TypeError, r"__annotate__ returned non-dict of type 'int'"):
312+
with self.assertRaisesRegex(TypeError, r"__annotate__\(\) must return a dict, not int"):
313313
print(f.__annotations__)
314314

315315
f.__annotate__ = lambda x: {"x": x}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Enhance wrong type error messages and make them more consistent. Patch by
2+
Semyon Moroz.

Objects/abstract.c

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,9 @@ PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue)
132132
return defaultvalue;
133133
}
134134
if (!PyLong_Check(result)) {
135-
PyErr_Format(PyExc_TypeError, "__length_hint__ must be an integer, not %.100s",
136-
Py_TYPE(result)->tp_name);
135+
PyErr_Format(PyExc_TypeError,
136+
"%T.__length_hint__() must return an int, not %T",
137+
o, result);
137138
Py_DECREF(result);
138139
return -1;
139140
}
@@ -143,7 +144,8 @@ PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue)
143144
return -1;
144145
}
145146
if (res < 0) {
146-
PyErr_Format(PyExc_ValueError, "__length_hint__() should return >= 0");
147+
PyErr_Format(PyExc_ValueError,
148+
"%T.__length_hint__() must return a non-negative int", o);
147149
return -1;
148150
}
149151
return res;
@@ -887,8 +889,8 @@ PyObject_Format(PyObject *obj, PyObject *format_spec)
887889

888890
if (result && !PyUnicode_Check(result)) {
889891
PyErr_Format(PyExc_TypeError,
890-
"__format__ must return a str, not %.200s",
891-
Py_TYPE(result)->tp_name);
892+
"%T.__format__() must return a str, not %T",
893+
obj, result);
892894
Py_SETREF(result, NULL);
893895
goto done;
894896
}
@@ -1421,17 +1423,17 @@ _PyNumber_Index(PyObject *item)
14211423

14221424
if (!PyLong_Check(result)) {
14231425
PyErr_Format(PyExc_TypeError,
1424-
"__index__ returned non-int (type %.200s)",
1425-
Py_TYPE(result)->tp_name);
1426+
"%T.__index__() must return an int, not %T",
1427+
item, result);
14261428
Py_DECREF(result);
14271429
return NULL;
14281430
}
14291431
/* Issue #17576: warn if 'result' not of exact type int. */
14301432
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
1431-
"__index__ returned non-int (type %.200s). "
1433+
"%T.__index__() must return an int, not %T. "
14321434
"The ability to return an instance of a strict subclass of int "
14331435
"is deprecated, and may be removed in a future version of Python.",
1434-
Py_TYPE(result)->tp_name)) {
1436+
item, result)) {
14351437
Py_DECREF(result);
14361438
return NULL;
14371439
}
@@ -1531,17 +1533,17 @@ PyNumber_Long(PyObject *o)
15311533

15321534
if (!PyLong_Check(result)) {
15331535
PyErr_Format(PyExc_TypeError,
1534-
"__int__ returned non-int (type %.200s)",
1535-
Py_TYPE(result)->tp_name);
1536+
"%T.__int__() must return an int, not %T",
1537+
o, result);
15361538
Py_DECREF(result);
15371539
return NULL;
15381540
}
15391541
/* Issue #17576: warn if 'result' not of exact type int. */
15401542
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
1541-
"__int__ returned non-int (type %.200s). "
1543+
"%T.__int__() must return an int, not %T. "
15421544
"The ability to return an instance of a strict subclass of int "
15431545
"is deprecated, and may be removed in a future version of Python.",
1544-
Py_TYPE(result)->tp_name)) {
1546+
o, result)) {
15451547
Py_DECREF(result);
15461548
return NULL;
15471549
}
@@ -1609,17 +1611,16 @@ PyNumber_Float(PyObject *o)
16091611

16101612
if (!PyFloat_Check(res)) {
16111613
PyErr_Format(PyExc_TypeError,
1612-
"%.50s.__float__ returned non-float (type %.50s)",
1613-
Py_TYPE(o)->tp_name, Py_TYPE(res)->tp_name);
1614+
"%T.__float__() must return a float, not %T", o, res);
16141615
Py_DECREF(res);
16151616
return NULL;
16161617
}
16171618
/* Issue #26983: warn if 'res' not of exact type float. */
16181619
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
1619-
"%.50s.__float__ returned non-float (type %.50s). "
1620+
"%T.__float__() must return a float, not %T. "
16201621
"The ability to return an instance of a strict subclass of float "
16211622
"is deprecated, and may be removed in a future version of Python.",
1622-
Py_TYPE(o)->tp_name, Py_TYPE(res)->tp_name)) {
1623+
o, res)) {
16231624
Py_DECREF(res);
16241625
return NULL;
16251626
}
@@ -2435,10 +2436,8 @@ method_output_as_list(PyObject *o, PyObject *meth)
24352436
PyThreadState *tstate = _PyThreadState_GET();
24362437
if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError)) {
24372438
_PyErr_Format(tstate, PyExc_TypeError,
2438-
"%.200s.%U() returned a non-iterable (type %.200s)",
2439-
Py_TYPE(o)->tp_name,
2440-
meth,
2441-
Py_TYPE(meth_output)->tp_name);
2439+
"%T.%U() must return an iterable, not %T",
2440+
o, meth, meth_output);
24422441
}
24432442
Py_DECREF(meth_output);
24442443
return NULL;
@@ -2818,9 +2817,8 @@ PyObject_GetIter(PyObject *o)
28182817
PyObject *res = (*f)(o);
28192818
if (res != NULL && !PyIter_Check(res)) {
28202819
PyErr_Format(PyExc_TypeError,
2821-
"iter() returned non-iterator "
2822-
"of type '%.100s'",
2823-
Py_TYPE(res)->tp_name);
2820+
"%T.__iter__() must return an iterator, not %T",
2821+
o, res);
28242822
Py_SETREF(res, NULL);
28252823
}
28262824
return res;
@@ -2839,8 +2837,8 @@ PyObject_GetAIter(PyObject *o) {
28392837
PyObject *it = (*f)(o);
28402838
if (it != NULL && !PyAIter_Check(it)) {
28412839
PyErr_Format(PyExc_TypeError,
2842-
"aiter() returned not an async iterator of type '%.100s'",
2843-
Py_TYPE(it)->tp_name);
2840+
"%T.__aiter__() must return an async iterator, not %T",
2841+
o, it);
28442842
Py_SETREF(it, NULL);
28452843
}
28462844
return it;

Objects/bytesobject.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -566,8 +566,8 @@ format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen)
566566
return NULL;
567567
if (!PyBytes_Check(result)) {
568568
PyErr_Format(PyExc_TypeError,
569-
"__bytes__ returned non-bytes (type %.200s)",
570-
Py_TYPE(result)->tp_name);
569+
"%T.__bytes__() must return a bytes, not %T",
570+
v, result);
571571
Py_DECREF(result);
572572
return NULL;
573573
}
@@ -2793,8 +2793,8 @@ bytes_new_impl(PyTypeObject *type, PyObject *x, const char *encoding,
27932793
return NULL;
27942794
if (!PyBytes_Check(bytes)) {
27952795
PyErr_Format(PyExc_TypeError,
2796-
"__bytes__ returned non-bytes (type %.200s)",
2797-
Py_TYPE(bytes)->tp_name);
2796+
"%T.__bytes__() must return a bytes, not %T",
2797+
x, bytes);
27982798
Py_DECREF(bytes);
27992799
return NULL;
28002800
}

Objects/complexobject.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -515,17 +515,17 @@ try_complex_special_method(PyObject *op)
515515
}
516516
if (!PyComplex_Check(res)) {
517517
PyErr_Format(PyExc_TypeError,
518-
"__complex__ returned non-complex (type %.200s)",
519-
Py_TYPE(res)->tp_name);
518+
"%T.__complex__() must return a complex, not %T",
519+
op, res);
520520
Py_DECREF(res);
521521
return NULL;
522522
}
523523
/* Issue #29894: warn if 'res' not of exact type complex. */
524524
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
525-
"__complex__ returned non-complex (type %.200s). "
525+
"%T.__complex__() must return a complex, not %T. "
526526
"The ability to return an instance of a strict subclass of complex "
527527
"is deprecated, and may be removed in a future version of Python.",
528-
Py_TYPE(res)->tp_name)) {
528+
op, res)) {
529529
Py_DECREF(res);
530530
return NULL;
531531
}

Objects/fileobject.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ PyFile_GetLine(PyObject *f, int n)
6868
}
6969
if (result != NULL && !PyBytes_Check(result) &&
7070
!PyUnicode_Check(result)) {
71+
PyErr_Format(PyExc_TypeError,
72+
"%T.readline() must return a str, not %T", f, result);
7173
Py_SETREF(result, NULL);
72-
PyErr_SetString(PyExc_TypeError,
73-
"object.readline() returned non-string");
7474
}
7575

7676
if (n < 0 && result != NULL && PyBytes_Check(result)) {
@@ -193,8 +193,8 @@ PyObject_AsFileDescriptor(PyObject *o)
193193
Py_DECREF(fno);
194194
}
195195
else {
196-
PyErr_SetString(PyExc_TypeError,
197-
"fileno() returned a non-integer");
196+
PyErr_Format(PyExc_TypeError,
197+
"%T.fileno() must return an int, not %T", o, fno);
198198
Py_DECREF(fno);
199199
return -1;
200200
}

Objects/floatobject.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,16 +288,16 @@ PyFloat_AsDouble(PyObject *op)
288288
if (!PyFloat_CheckExact(res)) {
289289
if (!PyFloat_Check(res)) {
290290
PyErr_Format(PyExc_TypeError,
291-
"%.50s.__float__ returned non-float (type %.50s)",
292-
Py_TYPE(op)->tp_name, Py_TYPE(res)->tp_name);
291+
"%T.__float__() must return a float, not %T",
292+
op, res);
293293
Py_DECREF(res);
294294
return -1;
295295
}
296296
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
297-
"%.50s.__float__ returned non-float (type %.50s). "
297+
"%T.__float__() must return a float, not %T. "
298298
"The ability to return an instance of a strict subclass of float "
299299
"is deprecated, and may be removed in a future version of Python.",
300-
Py_TYPE(op)->tp_name, Py_TYPE(res)->tp_name)) {
300+
op, res)) {
301301
Py_DECREF(res);
302302
return -1;
303303
}

Objects/funcobject.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,8 +560,9 @@ func_get_annotation_dict(PyFunctionObject *op)
560560
return NULL;
561561
}
562562
if (!PyDict_Check(ann_dict)) {
563-
PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'",
564-
Py_TYPE(ann_dict)->tp_name);
563+
PyErr_Format(PyExc_TypeError,
564+
"__annotate__() must return a dict, not %T",
565+
ann_dict);
565566
Py_DECREF(ann_dict);
566567
return NULL;
567568
}

Objects/genobject.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,14 +1092,14 @@ _PyCoro_GetAwaitableIter(PyObject *o)
10921092
if (PyCoro_CheckExact(res) || gen_is_coroutine(res)) {
10931093
/* __await__ must return an *iterator*, not
10941094
a coroutine or another awaitable (see PEP 492) */
1095-
PyErr_SetString(PyExc_TypeError,
1096-
"__await__() returned a coroutine");
1095+
PyErr_Format(PyExc_TypeError,
1096+
"%T.__await__() must return an iterator, "
1097+
"not coroutine", o);
10971098
Py_CLEAR(res);
10981099
} else if (!PyIter_Check(res)) {
10991100
PyErr_Format(PyExc_TypeError,
1100-
"__await__() returned non-iterator "
1101-
"of type '%.100s'",
1102-
Py_TYPE(res)->tp_name);
1101+
"%T.__await__() must return an iterator, "
1102+
"not %T", o, res);
11031103
Py_CLEAR(res);
11041104
}
11051105
}

0 commit comments

Comments
 (0)