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 09:56:05 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 09:56:05 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_int_subclass_from___index__(self):
+ class BadInt:
+ def __index__(self):
+ return True
+
+ bad_int = BadInt()
+ with self.assertRaises(TypeError):
+ 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 09:56:05 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,40 @@
with self.assertRaises(TypeError):
int(TruncReturnsBadInt())
+ def test_int_subclass_from___int__(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 self.assertRaises(TypeError):
+ int(bad_int)
+
+ bad_int = Foo3()
+ with self.assertRaises(TypeError):
+ int(bad_int)
+
+ bad_int = TruncReturnsBadInt()
+ with self.assertRaises(TypeError):
+ 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 09:56:05 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,13 +1163,13 @@
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)) {
+ if (result && !PyLong_CheckExact(result)) {
PyErr_Format(PyExc_TypeError,
"__index__ returned non-int "
"(type %.200s)",
@@ -1233,34 +1233,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 +1269,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 +1280,16 @@
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);
+ 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 09:56:05 2013 +0100
@@ -122,6 +122,46 @@
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 *as_int;
+
+ /* 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_SetString(PyExc_TypeError,
+ "an integer is required");
+ return NULL;
+ }
+
+ /* Convert using the nb_int slot, which should return something
+ of exact type int. */
+ as_int = nb->nb_int(integral);
+ if (as_int == NULL)
+ return NULL;
+ if (!PyLong_CheckExact(as_int)) {
+ PyErr_Format(PyExc_TypeError,
+ "__int__ returned non-int (type %.200s)",
+ Py_TYPE(as_int)->tp_name);
+ Py_DECREF(as_int);
+ return NULL;
+ }
+ return (PyLongObject *)as_int;
+}
+
+
/* Allocate a new long int object with size digits.
Return NULL and set exception if we run out of memory. */
@@ -345,7 +385,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 +392,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 +439,7 @@
}
}
exit:
- if (do_decref) {
- Py_DECREF(vv);
- }
+ Py_DECREF(v);
return res;
}
@@ -636,36 +656,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 +1177,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 +1276,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 +1307,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 +1314,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 +1361,7 @@
}
}
exit:
- if (do_decref) {
- Py_DECREF(vv);
- }
+ Py_DECREF(vv);
return res;
}