diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 9fc9f3a3848a4e..5f7aa88ea31110 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2389,10 +2389,8 @@ left undefined. .. note:: - In order to have a coherent integer type class, when :meth:`__index__` is - defined :meth:`__int__` should also be defined, and both should return - the same value. - + If :meth:`__index__` is defined and not :meth:`__int__`, :meth:`__int__` + automatically defaults to :meth:`__index__`. .. method:: object.__round__(self, [,ndigits]) object.__trunc__(self) diff --git a/Lib/test/test_index.py b/Lib/test/test_index.py index a2ac32132e2343..72a4a96a881a22 100644 --- a/Lib/test/test_index.py +++ b/Lib/test/test_index.py @@ -60,7 +60,7 @@ def test_int_subclass_with_index(self): # subclasses. See issue #17576. class MyInt(int): def __index__(self): - return int(self) + 1 + return self + 1 my_int = MyInt(7) direct_index = my_int.__index__() @@ -89,6 +89,13 @@ def __index__(self): n = operator.index(bad_int) self.assertEqual(n, 0) + def test_int_defaults_to_index(self): + class Test: + def __index__(self): + return 4 + + self.assertEqual(int(Test()), 4) + class SeqTestCase: # This test case isn't run directly. It just defines common tests diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-05-06-16-00-50.bpo-33039.5YKYPc.rst b/Misc/NEWS.d/next/Core and Builtins/2019-05-06-16-00-50.bpo-33039.5YKYPc.rst new file mode 100644 index 00000000000000..79d0742afbbb43 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-05-06-16-00-50.bpo-33039.5YKYPc.rst @@ -0,0 +1,2 @@ +:meth:`__int__` defaults to :meth:`__index__` when only the latter is +defined. Patch contributed by Rémi Lapeyre. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 37df4d23e4c192..ece9a43756f6fa 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5393,8 +5393,9 @@ PyType_Ready(PyTypeObject *type) } } - /* Hack for tp_hash and __hash__. - If after all that, tp_hash is still NULL, and __hash__ is not in + /* Set default methods */ + + /* If after all that, tp_hash is still NULL, and __hash__ is not in tp_dict, set tp_hash to PyObject_HashNotImplemented and tp_dict['__hash__'] equal to None. This signals that __hash__ is not inherited. @@ -5435,6 +5436,14 @@ PyType_Ready(PyTypeObject *type) goto error; } + /* If __index__ is defined but not __int__, make it default to __index__. + Don't touch __float__ and __complex__ as there could be some loss of + precision. + */ + if (type->tp_as_number != NULL && type->tp_as_number->nb_int == NULL) { + type->tp_as_number->nb_int = type->tp_as_number->nb_index; + } + /* All done -- set the ready flag */ type->tp_flags = (type->tp_flags & ~Py_TPFLAGS_READYING) | Py_TPFLAGS_READY;