diff -r d86aec3f61b0 Include/longobject.h --- a/Include/longobject.h Sat Aug 03 17:14:50 2013 +0100 +++ b/Include/longobject.h Sun Aug 04 11:05:46 2013 +0100 @@ -164,6 +164,12 @@ unsigned char* bytes, size_t n, int little_endian, int is_signed); +/* _PyLong_FromNbInt: Convert the given object to a PyLongObject + using the nb_int slot, if available. Raise TypeError if either the + nb_int slot is not available or the result of the call to nb_int + returns something not of type int. +*/ +PyAPI_FUNC(PyLongObject *)_PyLong_FromNbInt(PyObject *); /* _PyLong_Format: Convert the long to a string object with given base, appending a base prefix of 0[box] if base is 2, 8 or 16. */ diff -r d86aec3f61b0 Lib/test/test_index.py --- a/Lib/test/test_index.py Sat Aug 03 17:14:50 2013 +0100 +++ b/Lib/test/test_index.py Sun Aug 04 11:05:46 2013 +0100 @@ -9,7 +9,7 @@ class TrapInt(int): def __index__(self): - return self + return int(self) class BaseTestCase(unittest.TestCase): def setUp(self): @@ -55,6 +55,31 @@ self.assertRaises(TypeError, slice(self.o).indices, 0) self.assertRaises(TypeError, slice(self.n).indices, 0) + def test_int_subclass_with_index(self): + # __index__ should be used when computing indices, even for int + # subclasses. See issue #17576. + class MyInt(int): + def __index__(self): + return int(self) + 1 + + my_int = MyInt(7) + direct_index = my_int.__index__() + operator_index = operator.index(my_int) + self.assertEqual(direct_index, 8) + self.assertEqual(operator_index, 8) + # Both results should be of exact type int. + self.assertIs(type(direct_index), int) + self.assertIs(type(operator_index), int) + + def test_index_returns_int_subclass(self): + class BadInt: + def __index__(self): + return True + + bad_int = BadInt() + with support.check_warnings(('', DeprecationWarning)): + operator.index(bad_int) + class SeqTestCase: # This test case isn't run directly. It just defines common tests diff -r d86aec3f61b0 Lib/test/test_int.py --- a/Lib/test/test_int.py Sat Aug 03 17:14:50 2013 +0100 +++ b/Lib/test/test_int.py Sun Aug 04 11:05:46 2013 +0100 @@ -320,10 +320,6 @@ def __int__(self): return 42 - class Foo3(int): - def __int__(self): - return self - class Foo4(int): def __int__(self): return 42 @@ -335,7 +331,6 @@ self.assertEqual(int(Foo0()), 42) self.assertEqual(int(Foo1()), 42) self.assertEqual(int(Foo2()), 42) - self.assertEqual(int(Foo3()), 0) self.assertEqual(int(Foo4()), 42) self.assertRaises(TypeError, int, Foo5()) @@ -400,6 +395,49 @@ with self.assertRaises(TypeError): int(TruncReturnsBadInt()) + def test_int_subclass_with_int(self): + class MyInt(int): + def __int__(self): + return 42 + + my_int = MyInt(7) + self.assertEqual(my_int, 7) + self.assertEqual(int(my_int), 42) + + def test_int_returns_int_subclass(self): + class BadInt: + def __int__(self): + return True + + class Foo3(int): + def __int__(self): + return self + + class TruncReturnsBadInt: + def __trunc__(self): + return BadInt() + + class TruncReturnsIntSubclass: + def __trunc__(self): + return True + + bad_int = BadInt() + with support.check_warnings(('', DeprecationWarning)): + int(bad_int) + + bad_int = Foo3() + with support.check_warnings(('', DeprecationWarning)): + int(bad_int) + + bad_int = TruncReturnsBadInt() + with support.check_warnings(('', DeprecationWarning)): + int(bad_int) + + good_int = TruncReturnsIntSubclass() + n = int(good_int) + self.assertIs(type(n), int) + self.assertEqual(n, 1) + def test_error_message(self): testlist = ('\xbd', '123\xbd', ' 123 456 ') for s in testlist: diff -r d86aec3f61b0 Objects/abstract.c --- a/Objects/abstract.c Sat Aug 03 17:14:50 2013 +0100 +++ b/Objects/abstract.c Sun Aug 04 11:05:46 2013 +0100 @@ -1153,8 +1153,8 @@ return type_error("bad operand type for abs(): '%.200s'", o); } -/* Return a Python Int or Long from the object item - Raise TypeError if the result is not an int-or-long +/* Return a Python int from the object item. + Raise TypeError if the result is not an int or if the object cannot be interpreted as an index. */ PyObject * @@ -1163,19 +1163,36 @@ PyObject *result = NULL; if (item == NULL) return null_error(); - if (PyLong_Check(item)) { + if (PyLong_CheckExact(item)) { Py_INCREF(item); return item; } if (PyIndex_Check(item)) { result = item->ob_type->tp_as_number->nb_index(item); - if (result && !PyLong_Check(result)) { - PyErr_Format(PyExc_TypeError, - "__index__ returned non-int " - "(type %.200s)", - result->ob_type->tp_name); - Py_DECREF(result); - return NULL; + if (result && !PyLong_CheckExact(result)) { + int exception_raised = 0; + if (!PyLong_Check(result)) { + PyErr_Format(PyExc_TypeError, + "__index__ returned non-int " + "(type %.200s)", + result->ob_type->tp_name); + exception_raised = 1; + } + else { + /* Issue #17576: warn if 'result' not of exact type int. */ + exception_raised = PyErr_WarnFormat( + PyExc_DeprecationWarning, + 1, + "__index__ returned non-int " + "(type %.200s). The ability to return an instance of " + "a strict subclass of int is deprecated, and " + "may be removed in a future version of Python.", + result->ob_type->tp_name); + } + if (exception_raised) { + Py_DECREF(result); + return NULL; + } } } else { @@ -1233,34 +1250,6 @@ } -/* - Returns the Integral instance converted to an int. The instance is expected - to be an int or have an __int__ method. Steals integral's - reference. error_format will be used to create the TypeError if integral - isn't actually an Integral instance. error_format should be a format string - that can accept a char* naming integral's type. -*/ -static PyObject * -convert_integral_to_int(PyObject *integral, const char *error_format) -{ - PyNumberMethods *nb; - if (PyLong_Check(integral)) - return integral; - nb = Py_TYPE(integral)->tp_as_number; - if (nb->nb_int) { - PyObject *as_int = nb->nb_int(integral); - if (!as_int || PyLong_Check(as_int)) { - Py_DECREF(integral); - return as_int; - } - Py_DECREF(as_int); - } - PyErr_Format(PyExc_TypeError, error_format, Py_TYPE(integral)->tp_name); - Py_DECREF(integral); - return NULL; -} - - /* Add a check for embedded NULL-bytes in the argument. */ static PyObject * long_from_string(const char *s, Py_ssize_t len) @@ -1297,18 +1286,8 @@ } m = o->ob_type->tp_as_number; if (m && m->nb_int) { /* This should include subclasses of int */ - PyObject *res = m->nb_int(o); - if (res && !PyLong_Check(res)) { - PyErr_Format(PyExc_TypeError, - "__int__ returned non-int (type %.200s)", - res->ob_type->tp_name); - Py_DECREF(res); - return NULL; - } - return res; + return (PyObject *)_PyLong_FromNbInt(o); } - if (PyLong_Check(o)) /* An int subclass without nb_int */ - return _PyLong_Copy((PyLongObject *)o); trunc_func = _PyObject_LookupSpecial(o, &PyId___trunc__); if (trunc_func) { PyObject *truncated = PyEval_CallObject(trunc_func, NULL); @@ -1318,8 +1297,17 @@ return NULL; /* __trunc__ is specified to return an Integral type, but int() needs to return a int. */ - int_instance = convert_integral_to_int(truncated, - "__trunc__ returned non-Integral (type %.200s)"); + m = truncated->ob_type->tp_as_number; + if (m == NULL || m->nb_int == NULL) { + PyErr_Format( + PyExc_TypeError, + "__trunc__ returned non-Integral (type %.200s)", + truncated->ob_type->tp_name); + Py_DECREF(truncated); + return NULL; + } + int_instance = (PyObject *)_PyLong_FromNbInt(truncated); + Py_DECREF(truncated); return int_instance; } if (PyErr_Occurred()) diff -r d86aec3f61b0 Objects/longobject.c --- a/Objects/longobject.c Sat Aug 03 17:14:50 2013 +0100 +++ b/Objects/longobject.c Sun Aug 04 11:05:46 2013 +0100 @@ -122,6 +122,63 @@ return v; } +/* _PyLong_FromNbInt: Convert the given object to a PyLongObject + using the nb_int slot, if available. Raise TypeError if either the + nb_int slot is not available or the result of the call to nb_int + returns something not of type int. +*/ +PyLongObject * +_PyLong_FromNbInt(PyObject *integral) +{ + PyNumberMethods *nb; + PyObject *result; + + /* Fast path for the case that we already have an int. */ + if (PyLong_CheckExact(integral)) { + Py_INCREF(integral); + return (PyLongObject *)integral; + } + + nb = Py_TYPE(integral)->tp_as_number; + if (nb == NULL || nb->nb_int == NULL) { + PyErr_Format(PyExc_TypeError, + "an integer is required (got type %.200s)", + Py_TYPE(integral)->tp_name); + return NULL; + } + + /* Convert using the nb_int slot, which should return something + of exact type int. */ + result = nb->nb_int(integral); + if (result && !PyLong_CheckExact(result)) { + int exception_raised = 0; + if (!PyLong_Check(result)) { + PyErr_Format(PyExc_TypeError, + "__int__ returned non-int " + "(type %.200s)", + result->ob_type->tp_name); + exception_raised = 1; + } + else { + /* Issue #17576: warn if 'result' not of exact type int. */ + exception_raised = PyErr_WarnFormat( + PyExc_DeprecationWarning, + 1, + "__int__ returned non-int " + "(type %.200s). The ability to return an instance of " + "a strict subclass of int is deprecated, and " + "may be removed in a future version of Python.", + result->ob_type->tp_name); + } + if (exception_raised) { + Py_DECREF(result); + return NULL; + } + } + return (PyLongObject *)result; +} + + /* Allocate a new long int object with size digits. Return NULL and set exception if we run out of memory. */ @@ -345,7 +402,6 @@ long res; Py_ssize_t i; int sign; - int do_decref = 0; /* if nb_int was called */ *overflow = 0; if (vv == NULL) { @@ -353,28 +409,11 @@ return -1; } - if (!PyLong_Check(vv)) { - PyNumberMethods *nb; - nb = vv->ob_type->tp_as_number; - if (nb == NULL || nb->nb_int == NULL) { - PyErr_SetString(PyExc_TypeError, - "an integer is required"); - return -1; - } - vv = (*nb->nb_int) (vv); - if (vv == NULL) - return -1; - do_decref = 1; - if (!PyLong_Check(vv)) { - Py_DECREF(vv); - PyErr_SetString(PyExc_TypeError, - "nb_int should return int object"); - return -1; - } - } + v = _PyLong_FromNbInt(vv); + if (v == NULL) + return -1; res = -1; - v = (PyLongObject *)vv; i = Py_SIZE(v); switch (i) { @@ -417,9 +456,7 @@ } } exit: - if (do_decref) { - Py_DECREF(vv); - } + Py_DECREF(v); return res; } @@ -636,36 +673,21 @@ unsigned long PyLong_AsUnsignedLongMask(register PyObject *op) { - PyNumberMethods *nb; PyLongObject *lo; unsigned long val; - if (op && PyLong_Check(op)) - return _PyLong_AsUnsignedLongMask(op); - - if (op == NULL || (nb = op->ob_type->tp_as_number) == NULL || - nb->nb_int == NULL) { - PyErr_SetString(PyExc_TypeError, "an integer is required"); + if (op == NULL) { + PyErr_BadInternalCall(); return (unsigned long)-1; } - lo = (PyLongObject*) (*nb->nb_int) (op); + lo = _PyLong_FromNbInt(op); if (lo == NULL) return (unsigned long)-1; - if (PyLong_Check(lo)) { - val = _PyLong_AsUnsignedLongMask((PyObject *)lo); - Py_DECREF(lo); - if (PyErr_Occurred()) - return (unsigned long)-1; - return val; - } - else - { - Py_DECREF(lo); - PyErr_SetString(PyExc_TypeError, - "nb_int should return int object"); - return (unsigned long)-1; - } + + val = _PyLong_AsUnsignedLongMask((PyObject *)lo); + Py_DECREF(lo); + return val; } int @@ -1172,35 +1194,26 @@ PyErr_BadInternalCall(); return -1; } - if (!PyLong_Check(vv)) { - PyNumberMethods *nb; - PyObject *io; - if ((nb = vv->ob_type->tp_as_number) == NULL || - nb->nb_int == NULL) { - PyErr_SetString(PyExc_TypeError, "an integer is required"); - return -1; - } - io = (*nb->nb_int) (vv); - if (io == NULL) - return -1; - if (PyLong_Check(io)) { - bytes = PyLong_AsLongLong(io); - Py_DECREF(io); - return bytes; - } - Py_DECREF(io); - PyErr_SetString(PyExc_TypeError, "integer conversion failed"); + v = _PyLong_FromNbInt(vv); + if (v == NULL) return -1; - } - - v = (PyLongObject*)vv; + + res = 0; switch(Py_SIZE(v)) { - case -1: return -(sdigit)v->ob_digit[0]; - case 0: return 0; - case 1: return v->ob_digit[0]; - } - res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes, - SIZEOF_LONG_LONG, PY_LITTLE_ENDIAN, 1); + case -1: + bytes = -(sdigit)v->ob_digit[0]; + break; + case 0: + bytes = 0; + break; + case 1: + bytes = v->ob_digit[0]; + break; + default: + res = _PyLong_AsByteArray((PyLongObject *)v, (unsigned char *)&bytes, + SIZEOF_LONG_LONG, PY_LITTLE_ENDIAN, 1); + } + Py_DECREF(v); /* Plan 9 can't handle PY_LONG_LONG in ? : expressions */ if (res < 0) @@ -1280,36 +1293,16 @@ unsigned PY_LONG_LONG PyLong_AsUnsignedLongLongMask(register PyObject *op) { - PyNumberMethods *nb; PyLongObject *lo; unsigned PY_LONG_LONG val; - if (op && PyLong_Check(op)) - return _PyLong_AsUnsignedLongLongMask(op); - - if (op == NULL || (nb = op->ob_type->tp_as_number) == NULL || - nb->nb_int == NULL) { - PyErr_SetString(PyExc_TypeError, "an integer is required"); - return (unsigned PY_LONG_LONG)-1; - } - - lo = (PyLongObject*) (*nb->nb_int) (op); + lo = _PyLong_FromNbInt(op); if (lo == NULL) return (unsigned PY_LONG_LONG)-1; - if (PyLong_Check(lo)) { - val = _PyLong_AsUnsignedLongLongMask((PyObject *)lo); - Py_DECREF(lo); - if (PyErr_Occurred()) - return (unsigned PY_LONG_LONG)-1; - return val; - } - else - { - Py_DECREF(lo); - PyErr_SetString(PyExc_TypeError, - "nb_int should return int object"); - return (unsigned PY_LONG_LONG)-1; - } + + val = _PyLong_AsUnsignedLongLongMask((PyObject *)lo); + Py_DECREF(lo); + return val; } /* Get a C long long int from a long int object or any object that has an @@ -1331,7 +1324,6 @@ PY_LONG_LONG res; Py_ssize_t i; int sign; - int do_decref = 0; /* if nb_int was called */ *overflow = 0; if (vv == NULL) { @@ -1339,28 +1331,11 @@ return -1; } - if (!PyLong_Check(vv)) { - PyNumberMethods *nb; - nb = vv->ob_type->tp_as_number; - if (nb == NULL || nb->nb_int == NULL) { - PyErr_SetString(PyExc_TypeError, - "an integer is required"); - return -1; - } - vv = (*nb->nb_int) (vv); - if (vv == NULL) - return -1; - do_decref = 1; - if (!PyLong_Check(vv)) { - Py_DECREF(vv); - PyErr_SetString(PyExc_TypeError, - "nb_int should return int object"); - return -1; - } - } + v = _PyLong_FromNbInt(vv); + if (v == NULL) + return -1; res = -1; - v = (PyLongObject *)vv; i = Py_SIZE(v); switch (i) { @@ -1403,9 +1378,7 @@ } } exit: - if (do_decref) { - Py_DECREF(vv); - } + Py_DECREF(vv); return res; }