Issue 33039: int() and math.trunc don't accept objects that only define index (original) (raw)

Created on 2018-03-10 08:57 by ncoghlan, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 13106 closed remi.lapeyre,2019-05-06 14:02
Messages (10)
msg313515 - (view) Author: Alyssa Coghlan (ncoghlan) * (Python committer) Date: 2018-03-10 08:57
(Note: I haven't categorised this yet, as I'm not sure how it *should* be categorised) Back when the __index__/nb_index slot was added, the focus was on allowing 3rd party integer types to be used in places where potentially lossy conversion with __int__/nb_int *wasn't* permitted. However, this has led to an anomaly where the lossless conversion method *isn't* tried implicitly for the potentially lossy int() and math.trunc() calls, but is tried automatically in other contexts: ``` >>> import math >>> class MyInt: ... def __index__(self): ... return 42 ... >>> int(MyInt()) Traceback (most recent call last): File "", line 1, in TypeError: int() argument must be a string, a bytes-like object or a number, not 'MyInt' >>> math.trunc(MyInt()) Traceback (most recent call last): File "", line 1, in TypeError: type MyInt doesn't define __trunc__ method >>> hex(MyInt()) '0x2a' >>> len("a" * MyInt()) 42 ``` Supporting int() requires also setting `__int__`: ``` >>> MyInt.__int__ = MyInt.__index__ >>> int(MyInt()) 42 ``` Supporting math.trunc() requires also setting `__trunc__`: ``` >>> MyInt.__trunc__ = MyInt.__index__ >>> math.trunc(MyInt()) 42 ``` (This anomaly was noticed by Eric Appelt while updating the int() docs to cover the fallback to trying __trunc__ when __int__ isn't defined: https://github.com/python/cpython/pull/6022#issuecomment-371695913)
msg313522 - (view) Author: Alyssa Coghlan (ncoghlan) * (Python committer) Date: 2018-03-10 11:34
Marking this as a documentation enhancement request for now, but I think we should also consider changing the type creation behaviour in 3.8 to implicitly add __int__ and __trunc__ definitions when __index__ is defined, but they aren't. That way, no behaviour will change for classes that explicitly define __int__ or __trunc__, but classes that only define __index__ without defining the other methods will behave more intuitively.
msg332819 - (view) Author: Rémi Lapeyre (remi.lapeyre) * Date: 2018-12-31 14:52
>I think we should also consider changing the type creation behaviour in 3.8 @ncoghlan is this what's being done in PyTypeReady?
msg333546 - (view) Author: Alyssa Coghlan (ncoghlan) * (Python committer) Date: 2019-01-13 07:47
@Rémi Aye, filling out derived slots is one of the things PyType_Ready does. This would need a discussion on python-dev before going ahead and doing it though, as the closest equivalent we have to this right now is the "negative" derivation, where overriding __eq__ without overriding __hash__ implicitly marks the derived class as unhashable (look for "type->tp_hash = PyObject_HashNotImplemented;").
msg336044 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-02-20 06:56
See also .
msg336201 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-02-21 10:45
Is not this a duplicate of ?
msg336234 - (view) Author: Rémi Lapeyre (remi.lapeyre) * Date: 2019-02-21 16:10
Yes it is. Thanks for finding that @Serhiy. Since nobody objected to the change on the mailing list and people seem to agree in issue 20092: [R. David Murray] To summarize for anyone like me who didn't follow that issue: __index__ means the object can be losslessly converted to an int (is a true int), while __int__ may be an approximate conversion. Thus it makes sense for an object to have an __int__ but not __index__, but vice-versa does not make sense. I will post my patch tonight.
msg340453 - (view) Author: Cheryl Sabella (cheryl.sabella) * (Python committer) Date: 2019-04-17 22:43
Rémi, Are you still working on the patch for this? Thanks!
msg341505 - (view) Author: Rémi Lapeyre (remi.lapeyre) * Date: 2019-05-06 14:11
Hi Cheryl, thanks for the ping. I wasn't sure my patch was correct but reading typeobject.c:add_operators(), it is actually more straight-forward than I thought. Serhiy Storchaka: This is indeed a duplicate of . I believe the solution proposed by Nick Coghlan is better than the one of Amitava Bhattacharyya, "adding a call to `nb_index` (if that slot exists) in `_PyLong_FromNbInt`" though. One thing to note regarding the proposed patch: the following stops to work and raises a RecursionError since __index__ == __int__: class MyInt(int): def __index__(self): return int(self) + 1 I changed test_int_subclass_with_index() as `int(self) + 1` is the same thing as `self + 1` for int subclasses. I don't think this sort of code should appear in the wild but if you think it is important not to break compatibility here, I think I could check for number subclasses before overriding __index__.
msg341508 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-05-06 14:29
Then let to continue the discussion on the older issue which has larger discussion.
History
Date User Action Args
2022-04-11 14:58:58 admin set github: 77220
2019-05-06 14:29:29 serhiy.storchaka set status: open -> closedsuperseder: type() constructor should bind __int__ to __index__ when __index__ is defined and __int__ is notmessages: + resolution: duplicatestage: patch review -> resolved
2019-05-06 14:11:13 remi.lapeyre set messages: +
2019-05-06 14:02:00 remi.lapeyre set keywords: + patchstage: needs patch -> patch reviewpull_requests: + <pull%5Frequest13020>
2019-04-17 22:43:11 cheryl.sabella set nosy: + cheryl.sabellamessages: +
2019-02-21 16:10:40 remi.lapeyre set messages: +
2019-02-21 10:45:51 serhiy.storchaka set messages: +
2019-02-20 06:56:34 serhiy.storchaka set nosy: + serhiy.storchakamessages: +
2019-01-13 07:47:32 ncoghlan set messages: +
2018-12-31 14:52:24 remi.lapeyre set nosy: + remi.lapeyremessages: +
2018-09-22 17:00:28 xtreak set nosy: + xtreak
2018-03-10 11:34:44 ncoghlan set assignee: docs@pythontype: enhancementcomponents: + Documentationversions: + Python 3.6, Python 3.7, Python 3.8nosy: + docs@pythonmessages: + stage: needs patch
2018-03-10 08:57:11 ncoghlan create