Repository navigation
Inconsistent error messages when returning the wrong type for the type-conversion magic methods #130821
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Mar 4, 2025 The difference is
Lines 1529 to 1534 in d780f0a
if (!PyLong_Check(result)) { PyErr_Format(PyExc_TypeError, "__int__ returned non-int (type %.200s)", Py_TYPE(result)->tp_name); Py_DECREF(result); return NULL;
vs
Lines 1607 to 1612 in d780f0a
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); Py_DECREF(res); return NULL; CC @serhiy-storchaka as author of 16931c3.
I don't think that inconsistency is a real bug. But that does make sense for me as a feature request: i.e. include type information to the exception message.
Reacted by Serhiy Storchaka- addedtype-featureA feature request or enhancementA feature request or enhancementinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)and removedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Mar 4, 2025 I've marked this as easy for anyone that wants to dive into the C code for the first time.
if no one minds I'm going to send a PR with a unified message using as a reference message:
Foo.__float__ returned non-float (type NoneType)but after "grep" sources i found more cases with
"returned non-"template, excluding tests and docs:grep output
./Objects/abstract.c: "__index__ returned non-int (type %.200s)", ./Objects/abstract.c: "__index__ returned non-int (type %.200s). " ./Objects/abstract.c: "__int__ returned non-int (type %.200s)", ./Objects/abstract.c: "__int__ returned non-int (type %.200s). " ./Objects/abstract.c: "%.50s.__float__ returned non-float (type %.50s)", ./Objects/abstract.c: "%.50s.__float__ returned non-float (type %.50s). " ./Objects/abstract.c: "iter() returned non-iterator " ./Objects/bytesobject.c: "__bytes__ returned non-bytes (type %.200s)", ./Objects/bytesobject.c: "__bytes__ returned non-bytes (type %.200s)", ./Objects/complexobject.c: "__complex__ returned non-complex (type %.200s)", ./Objects/complexobject.c: "__complex__ returned non-complex (type %.200s). " ./Objects/funcobject.c: PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'", ./Objects/moduleobject.c: PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'", ./Objects/object.c: "__repr__ returned non-string (type %.200s)", ./Objects/object.c: "__str__ returned non-string (type %.200s)", ./Objects/object.c: "__bytes__ returned non-bytes (type %.200s)", ./Objects/fileobject.c: "object.readline() returned non-string"); ./Objects/floatobject.c: "%.50s.__float__ returned non-float (type %.50s)", ./Objects/floatobject.c: "%.50s.__float__ returned non-float (type %.50s). " ./Objects/genobject.c: "__await__() returned non-iterator " ./Objects/typeobject.c: PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'", ./Objects/typeobject.c: "__buffer__ returned non-memoryview object"); ./Lib/subprocess.py: return "Command '%s' returned non-zero exit status %d." % ( ./Modules/_datetimemodule.c: "divmod() returned non-tuple (type %.200s)", ./Modules/_abc.c: "items() returned non-iterable"); ./Modules/_pickle.c: "read() returned non-bytes object (%R)",should we add type information to other methods and to methods from modules in stdlib?
Looks like a nice idea to me.
You can use
%Tand%Nto format type names in C.but after "grep" sources i found more cases with
"returned non-"template, excluding tests and docs:
grep outputshould we add type information to other methods and to methods from modules in stdlib?
here are more examples of error messages:
__len__() should return >= 0 __hash__ method should return an integer __init__() should return None, not '%.200s' __sizeof__() should return >= 0 __getnewargs_ex__ should return a tuple, not '%.200s' __getnewargs_ex__ should return a tuple of length 2, not %zd __length_hint__() should return >= 0 __length_hint__ must be an integer, not %.100s read() should return bytes readall() should return bytes decoder should return a string result, not '%.200s' encoder should return a bytes object, not '%.200s' iterator should return strings, not %.200s (the file should be opened in text mode)I suggest using the following template for all of them:
{TYPE}.__method__ should return ..., not ...(or must return)
unfortunately we don't have a PEP about error messages, but we had a similar PR #127345 and I found discussI like it. But since it's such a wide change, we need approval from more core developers. Could you open a discussion at https://discuss.python.org/?
I think also that there should be parentheses after the method name. This would help to distinguish them from attributes.
Reacted by Semyon MorozCould you open a discussion at https://discuss.python.org/?
yes, i can. but should i propose in this discussion to create PEP or just propose to change error messages of the methods i have cited?
EDIT: I don't mind if you open it. Maybe you know better what to use as arguments :)
9 remaining items
- added a commit that references this issue
on Feb 12, 2026 Hello All, I just tried this
minimal_repro.pyand here's the result on Termux with 3.13.13 version:# Run: python3 -c "..." # __int__ returns the wrong type class A: def __int__(self): return "not an int" class B: def __float__(self): return 42 # returns an int, not a float class C: def __bool__(self): return 1.0 # returns a float, not a bool class D: def __index__(self): return 3.14 try: int(A()) except TypeError as e: print(f"__int__: {e}") try: float(B()) except TypeError as e: print(f"__float__: {e}") try: bool(C()) except TypeError as e: print(f"__bool__: {e}") try: D()[0:1] except TypeError as e: print(f"__index__: {e}")
~ $ python3 minimal_repro.py __int__: __int__ returned non-int (type str) __float__: B.__float__ returned non-float (type int) __bool__: __bool__ should return bool, returned float __index__: 'D' object is not subscriptable ~ $
- added 3 commits that reference this issue
on Jun 22, 2026 - added a commit that references this issue
on Jul 24, 2026 I found that Modules/_abc.c is still using the old "items() returned non-iterable" error format. I am currently working on a patch to update it to the new consistent format with type information!
Since PR #144737 has gone stale, I would love to take over this issue. I can port over my fixes for _abc.c, tackle the other listed modules, and address the reviewer's feedback regarding whether we should switch to TypeError or keep ValueError in _pickle.c.
Opened #156120 for the
Modules/_abc.cpiece — narrower in scope than #144737 (just this one file), and fixes a NULL-dereference risk found in that PR's version of the same hunk (passingNULLas the message toPySequence_Fast(), which can reachPyErr_SetString(exc, NULL)on a genuine TypeError). Left a note on #144737 as well. Not touching the other Modules/ files yet since the _pickle.c ValueError/TypeError question there is still open and this issue is still flagged as needing broader consensus before a repo-wide sweep.
Bug report
Bug description:
I noticed the error messages between magic methods like
__int__and__float__were inconsistent. This seems like slightly undesirable behavior to me. I used the following code to generate many of them.And the output is as follows:
The first issue I've made, but seems like a reasonable bug. I'm not sure if there are other "type-conversion" magic methods out there that aren't consistent.
CPython versions tested on:
3.13, 3.12
Operating systems tested on:
Windows
Linked PRs