diff options
-rw-r--r-- | Doc/api/abstract.tex | 25 | ||||
-rw-r--r-- | Include/abstract.h | 20 | ||||
-rw-r--r-- | Include/object.h | 2 | ||||
-rw-r--r-- | Lib/test/test_index.py | 174 | ||||
-rw-r--r-- | Misc/NEWS | 5 | ||||
-rw-r--r-- | Modules/arraymodule.c | 12 | ||||
-rw-r--r-- | Modules/mmapmodule.c | 30 | ||||
-rw-r--r-- | Modules/operator.c | 12 | ||||
-rw-r--r-- | Objects/abstract.c | 105 | ||||
-rw-r--r-- | Objects/classobject.c | 24 | ||||
-rw-r--r-- | Objects/intobject.c | 17 | ||||
-rw-r--r-- | Objects/listobject.c | 12 | ||||
-rw-r--r-- | Objects/longobject.c | 49 | ||||
-rw-r--r-- | Objects/sliceobject.c | 2 | ||||
-rw-r--r-- | Objects/stringobject.c | 7 | ||||
-rw-r--r-- | Objects/tupleobject.c | 7 | ||||
-rw-r--r-- | Objects/typeobject.c | 25 | ||||
-rw-r--r-- | Objects/unicodeobject.c | 7 | ||||
-rw-r--r-- | Python/ceval.c | 18 |
19 files changed, 319 insertions, 234 deletions
diff --git a/Doc/api/abstract.tex b/Doc/api/abstract.tex index f740efb..9c39403 100644 --- a/Doc/api/abstract.tex +++ b/Doc/api/abstract.tex @@ -693,12 +693,31 @@ determination. \samp{float(\var{o})}.\bifuncindex{float} \end{cfuncdesc} -\begin{cfuncdesc}{Py_ssize_t}{PyNumber_Index}{PyObject *o} - Returns the \var{o} converted to a Py_ssize_t integer on success, or - -1 with an exception raised on failure. +\begin{cfuncdesc}{PyObject*}{PyNumber_Index}{PyObject *o} + Returns the \var{o} converted to a Python int or long on success or \NULL{} + with a TypeError exception raised on failure. \versionadded{2.5} \end{cfuncdesc} +\begin{cfuncdesc}{Py_ssize_t}{PyNumber_AsSsize_t}{PyObject *o, PyObject *exc} + Returns \var{o} converted to a Py_ssize_t value if \var{o} + can be interpreted as an integer. If \var{o} can be converted to a Python + int or long but the attempt to convert to a Py_ssize_t value + would raise an \exception{OverflowError}, then the \var{exc} argument + is the type of exception that will be raised (usually \exception{IndexError} + or \exception{OverflowError}). If \var{exc} is \NULL{}, then the exception + is cleared and the value is clipped to \var{PY_SSIZE_T_MIN} + for a negative integer or \var{PY_SSIZE_T_MAX} for a positive integer. + \versionadded{2.5} +\end{cfuncdesc} + +\begin{cfuncdesc}{int}{PyIndex_Check}{PyObject *o} + Returns True if \var{o} is an index integer (has the nb_index slot of + the tp_as_number structure filled in). + \versionadded{2.5} +\end{cfuncdesc} + + \section{Sequence Protocol \label{sequence}} \begin{cfuncdesc}{int}{PySequence_Check}{PyObject *o} diff --git a/Include/abstract.h b/Include/abstract.h index f96b297..9b0b3f0 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -758,13 +758,27 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ */ - PyAPI_FUNC(Py_ssize_t) PyNumber_Index(PyObject *); +#define PyIndex_Check(obj) \ + ((obj)->ob_type->tp_as_number != NULL && \ + PyType_HasFeature((obj)->ob_type, Py_TPFLAGS_HAVE_INDEX) && \ + (obj)->ob_type->tp_as_number->nb_index != NULL) + + PyAPI_FUNC(PyObject *) PyNumber_Index(PyObject *o); /* - Returns the object converted to Py_ssize_t on success - or -1 with an error raised on failure. + Returns the object converted to a Python long or int + or NULL with an error raised on failure. */ + PyAPI_FUNC(Py_ssize_t) PyNumber_AsSsize_t(PyObject *o, PyObject *exc); + + /* + Returns the object converted to Py_ssize_t by going through + PyNumber_Index first. If an overflow error occurs while + converting the int-or-long to Py_ssize_t, then the second argument + is the error-type to return. If it is NULL, then the overflow error + is cleared and the value is clipped. + */ PyAPI_FUNC(PyObject *) PyNumber_Int(PyObject *o); diff --git a/Include/object.h b/Include/object.h index 4b0e080..b0817e6 100644 --- a/Include/object.h +++ b/Include/object.h @@ -208,7 +208,7 @@ typedef struct { binaryfunc nb_inplace_true_divide; /* Added in release 2.5 */ - lenfunc nb_index; + unaryfunc nb_index; } PyNumberMethods; typedef struct { diff --git a/Lib/test/test_index.py b/Lib/test/test_index.py index 45b3b2b..b224a50 100644 --- a/Lib/test/test_index.py +++ b/Lib/test/test_index.py @@ -1,6 +1,7 @@ import unittest from test import test_support import operator +from sys import maxint class oldstyle: def __index__(self): @@ -10,68 +11,115 @@ class newstyle(object): def __index__(self): return self.ind +class TrapInt(int): + def __index__(self): + return self + +class TrapLong(long): + def __index__(self): + return self + class BaseTestCase(unittest.TestCase): def setUp(self): self.o = oldstyle() self.n = newstyle() - self.o2 = oldstyle() - self.n2 = newstyle() def test_basic(self): self.o.ind = -2 self.n.ind = 2 - assert(self.seq[self.n] == self.seq[2]) - assert(self.seq[self.o] == self.seq[-2]) - assert(operator.index(self.o) == -2) - assert(operator.index(self.n) == 2) + self.assertEqual(operator.index(self.o), -2) + self.assertEqual(operator.index(self.n), 2) + + def test_slice(self): + self.o.ind = 1 + self.n.ind = 2 + slc = slice(self.o, self.o, self.o) + check_slc = slice(1, 1, 1) + self.assertEqual(slc.indices(self.o), check_slc.indices(1)) + slc = slice(self.n, self.n, self.n) + check_slc = slice(2, 2, 2) + self.assertEqual(slc.indices(self.n), check_slc.indices(2)) + def test_wrappers(self): + self.o.ind = 4 + self.n.ind = 5 + self.assertEqual(6 .__index__(), 6) + self.assertEqual(-7L.__index__(), -7) + self.assertEqual(self.o.__index__(), 4) + self.assertEqual(self.n.__index__(), 5) + + def test_infinite_recursion(self): + self.failUnlessRaises(TypeError, operator.index, TrapInt()) + self.failUnlessRaises(TypeError, operator.index, TrapLong()) + self.failUnless(slice(TrapInt()).indices(0)==(0,0,1)) + self.failUnlessRaises(TypeError, slice(TrapLong()).indices, 0) + def test_error(self): self.o.ind = 'dumb' self.n.ind = 'bad' - myfunc = lambda x, obj: obj.seq[x] self.failUnlessRaises(TypeError, operator.index, self.o) self.failUnlessRaises(TypeError, operator.index, self.n) - self.failUnlessRaises(TypeError, myfunc, self.o, self) - self.failUnlessRaises(TypeError, myfunc, self.n, self) + self.failUnlessRaises(TypeError, slice(self.o).indices, 0) + self.failUnlessRaises(TypeError, slice(self.n).indices, 0) + + +class SeqTestCase(unittest.TestCase): + # This test case isn't run directly. It just defines common tests + # to the different sequence types below + def setUp(self): + self.o = oldstyle() + self.n = newstyle() + self.o2 = oldstyle() + self.n2 = newstyle() + + def test_index(self): + self.o.ind = -2 + self.n.ind = 2 + self.assertEqual(self.seq[self.n], self.seq[2]) + self.assertEqual(self.seq[self.o], self.seq[-2]) def test_slice(self): self.o.ind = 1 self.o2.ind = 3 self.n.ind = 2 self.n2.ind = 4 - assert(self.seq[self.o:self.o2] == self.seq[1:3]) - assert(self.seq[self.n:self.n2] == self.seq[2:4]) + self.assertEqual(self.seq[self.o:self.o2], self.seq[1:3]) + self.assertEqual(self.seq[self.n:self.n2], self.seq[2:4]) def test_repeat(self): self.o.ind = 3 self.n.ind = 2 - assert(self.seq * self.o == self.seq * 3) - assert(self.seq * self.n == self.seq * 2) - assert(self.o * self.seq == self.seq * 3) - assert(self.n * self.seq == self.seq * 2) + self.assertEqual(self.seq * self.o, self.seq * 3) + self.assertEqual(self.seq * self.n, self.seq * 2) + self.assertEqual(self.o * self.seq, self.seq * 3) + self.assertEqual(self.n * self.seq, self.seq * 2) def test_wrappers(self): - n = self.n - n.ind = 5 - assert n.__index__() == 5 - assert 6 .__index__() == 6 - assert -7L.__index__() == -7 - assert self.seq.__getitem__(n) == self.seq[5] - assert self.seq.__mul__(n) == self.seq * 5 - assert self.seq.__rmul__(n) == self.seq * 5 - - def test_infinite_recusion(self): - class Trap1(int): - def __index__(self): - return self - class Trap2(long): - def __index__(self): - return self - self.failUnlessRaises(TypeError, operator.getitem, self.seq, Trap1()) - self.failUnlessRaises(TypeError, operator.getitem, self.seq, Trap2()) - - -class ListTestCase(BaseTestCase): + self.o.ind = 4 + self.n.ind = 5 + self.assertEqual(self.seq.__getitem__(self.o), self.seq[4]) + self.assertEqual(self.seq.__mul__(self.o), self.seq * 4) + self.assertEqual(self.seq.__rmul__(self.o), self.seq * 4) + self.assertEqual(self.seq.__getitem__(self.n), self.seq[5]) + self.assertEqual(self.seq.__mul__(self.n), self.seq * 5) + self.assertEqual(self.seq.__rmul__(self.n), self.seq * 5) + + def test_infinite_recursion(self): + self.failUnlessRaises(TypeError, operator.getitem, self.seq, TrapInt()) + self.failUnlessRaises(TypeError, operator.getitem, self.seq, TrapLong()) + + def test_error(self): + self.o.ind = 'dumb' + self.n.ind = 'bad' + indexobj = lambda x, obj: obj.seq[x] + self.failUnlessRaises(TypeError, indexobj, self.o, self) + self.failUnlessRaises(TypeError, indexobj, self.n, self) + sliceobj = lambda x, obj: obj.seq[x:] + self.failUnlessRaises(TypeError, sliceobj, self.o, self) + self.failUnlessRaises(TypeError, sliceobj, self.n, self) + + +class ListTestCase(SeqTestCase): seq = [0,10,20,30,40,50] def test_setdelitem(self): @@ -82,36 +130,36 @@ class ListTestCase(BaseTestCase): del lst[self.n] lst[self.o] = 'X' lst[self.n] = 'Y' - assert lst == list('abYdefghXj') + self.assertEqual(lst, list('abYdefghXj')) lst = [5, 6, 7, 8, 9, 10, 11] lst.__setitem__(self.n, "here") - assert lst == [5, 6, "here", 8, 9, 10, 11] + self.assertEqual(lst, [5, 6, "here", 8, 9, 10, 11]) lst.__delitem__(self.n) - assert lst == [5, 6, 8, 9, 10, 11] + self.assertEqual(lst, [5, 6, 8, 9, 10, 11]) def test_inplace_repeat(self): self.o.ind = 2 self.n.ind = 3 lst = [6, 4] lst *= self.o - assert lst == [6, 4, 6, 4] + self.assertEqual(lst, [6, 4, 6, 4]) lst *= self.n - assert lst == [6, 4, 6, 4] * 3 + self.assertEqual(lst, [6, 4, 6, 4] * 3) lst = [5, 6, 7, 8, 9, 11] l2 = lst.__imul__(self.n) - assert l2 is lst - assert lst == [5, 6, 7, 8, 9, 11] * 3 + self.assert_(l2 is lst) + self.assertEqual(lst, [5, 6, 7, 8, 9, 11] * 3) -class TupleTestCase(BaseTestCase): +class TupleTestCase(SeqTestCase): seq = (0,10,20,30,40,50) -class StringTestCase(BaseTestCase): +class StringTestCase(SeqTestCase): seq = "this is a test" -class UnicodeTestCase(BaseTestCase): +class UnicodeTestCase(SeqTestCase): seq = u"this is a test" @@ -120,17 +168,47 @@ class XRangeTestCase(unittest.TestCase): def test_xrange(self): n = newstyle() n.ind = 5 - assert xrange(1, 20)[n] == 6 - assert xrange(1, 20).__getitem__(n) == 6 + self.assertEqual(xrange(1, 20)[n], 6) + self.assertEqual(xrange(1, 20).__getitem__(n), 6) + +class OverflowTestCase(unittest.TestCase): + + def setUp(self): + self.pos = 2**100 + self.neg = -self.pos + + def test_large_longs(self): + self.assertEqual(self.pos.__index__(), self.pos) + self.assertEqual(self.neg.__index__(), self.neg) + + def test_getitem(self): + class GetItem(object): + def __len__(self): + return maxint + def __getitem__(self, key): + return key + def __getslice__(self, i, j): + return i, j + x = GetItem() + self.assertEqual(x[self.pos], self.pos) + self.assertEqual(x[self.neg], self.neg) + self.assertEqual(x[self.neg:self.pos], (-1, maxint)) + self.assertEqual(x[self.neg:self.pos:1].indices(maxint), (0, maxint, 1)) + + def test_sequence_repeat(self): + self.failUnlessRaises(OverflowError, lambda: "a" * self.pos) + self.failUnlessRaises(OverflowError, lambda: "a" * self.neg) def test_main(): test_support.run_unittest( + BaseTestCase, ListTestCase, TupleTestCase, StringTestCase, UnicodeTestCase, XRangeTestCase, + OverflowTestCase, ) if __name__ == "__main__": @@ -12,6 +12,11 @@ What's New in Python 2.5 release candidate 1? Core and builtins ----------------- +- Patch #1538606, Fix __index__() clipping. There were some problems + discovered with the API and how integers that didn't fit into Py_ssize_t + were handled. This patch attempts to provide enough alternatives + to effectively use __index__. + - Bug #1536021: __hash__ may now return long int; the final hash value is obtained by invoking hash on the long int. diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 14e5e5d..efa7835 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1572,14 +1572,11 @@ array_repr(arrayobject *a) return s; } -#define HASINDEX(o) PyType_HasFeature((o)->ob_type, Py_TPFLAGS_HAVE_INDEX) - static PyObject* array_subscr(arrayobject* self, PyObject* item) { - PyNumberMethods *nb = item->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(item) && nb->nb_index != NULL) { - Py_ssize_t i = nb->nb_index(item); + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i==-1 && PyErr_Occurred()) { return NULL; } @@ -1627,9 +1624,8 @@ array_subscr(arrayobject* self, PyObject* item) static int array_ass_subscr(arrayobject* self, PyObject* item, PyObject* value) { - PyNumberMethods *nb = item->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(item) && nb->nb_index != NULL) { - Py_ssize_t i = nb->nb_index(item); + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i==-1 && PyErr_Occurred()) return -1; if (i < 0) diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index b2dd675..53df275 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -808,8 +808,6 @@ static PyTypeObject mmap_object_type = { }; -#define HASINDEX(o) PyType_HasFeature((o)->ob_type, Py_TPFLAGS_HAVE_INDEX) - /* extract the map size from the given PyObject Returns -1 on error, with an appropriate Python exception raised. On @@ -817,31 +815,19 @@ static PyTypeObject mmap_object_type = { static Py_ssize_t _GetMapSize(PyObject *o) { - PyNumberMethods *nb = o->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(o) && nb->nb_index != NULL) { - Py_ssize_t i = nb->nb_index(o); + if (PyIndex_Check(o)) { + Py_ssize_t i = PyNumber_AsSsize_t(o, PyExc_OverflowError); if (i==-1 && PyErr_Occurred()) return -1; - if (i < 0) - goto onnegoverflow; - if (i==PY_SSIZE_T_MAX) - goto onposoverflow; + if (i < 0) { + PyErr_SetString(PyExc_OverflowError, + "memory mapped size must be positive"); + return -1; + } return i; } - else { - PyErr_SetString(PyExc_TypeError, - "map size must be an integral value"); - return -1; - } - - onnegoverflow: - PyErr_SetString(PyExc_OverflowError, - "memory mapped size must be positive"); - return -1; - onposoverflow: - PyErr_SetString(PyExc_OverflowError, - "memory mapped size is too large (limited by C int)"); + PyErr_SetString(PyExc_TypeError, "map size must be an integral value"); return -1; } diff --git a/Modules/operator.c b/Modules/operator.c index 7fc1f8a..7479a53 100644 --- a/Modules/operator.c +++ b/Modules/operator.c @@ -139,15 +139,7 @@ op_ipow(PyObject *s, PyObject *a) static PyObject * op_index(PyObject *s, PyObject *a) { - Py_ssize_t i; - PyObject *a1; - if (!PyArg_UnpackTuple(a,"index", 1, 1, &a1)) - return NULL; - i = PyNumber_Index(a1); - if (i == -1 && PyErr_Occurred()) - return NULL; - else - return PyInt_FromSsize_t(i); + return PyNumber_Index(a); } static PyObject* @@ -249,7 +241,7 @@ spam1o(isMappingType, spam1(is_, "is_(a, b) -- Same as a is b.") spam1(is_not, "is_not(a, b) -- Same as a is not b.") -spam2(index, __index__, "index(a) -- Same as a.__index__()") +spam2o(index, __index__, "index(a) -- Same as a.__index__()") spam2(add,__add__, "add(a, b) -- Same as a + b.") spam2(sub,__sub__, "sub(a, b) -- Same as a - b.") spam2(mul,__mul__, "mul(a, b) -- Same as a * b.") diff --git a/Objects/abstract.c b/Objects/abstract.c index bad9f96..c8e9ddc 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -8,8 +8,6 @@ #define NEW_STYLE_NUMBER(o) PyType_HasFeature((o)->ob_type, \ Py_TPFLAGS_CHECKTYPES) -#define HASINDEX(o) PyType_HasFeature((o)->ob_type, Py_TPFLAGS_HAVE_INDEX) - /* Shorthands to return certain errors */ @@ -122,9 +120,9 @@ PyObject_GetItem(PyObject *o, PyObject *key) return m->mp_subscript(o, key); if (o->ob_type->tp_as_sequence) { - PyNumberMethods *nb = key->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(key) && nb->nb_index != NULL) { - Py_ssize_t key_value = nb->nb_index(key); + if (PyIndex_Check(key)) { + Py_ssize_t key_value; + key_value = PyNumber_AsSsize_t(key, PyExc_IndexError); if (key_value == -1 && PyErr_Occurred()) return NULL; return PySequence_GetItem(o, key_value); @@ -151,9 +149,9 @@ PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value) return m->mp_ass_subscript(o, key, value); if (o->ob_type->tp_as_sequence) { - PyNumberMethods *nb = key->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(key) && nb->nb_index != NULL) { - Py_ssize_t key_value = nb->nb_index(key); + if (PyIndex_Check(key)) { + Py_ssize_t key_value; + key_value = PyNumber_AsSsize_t(key, PyExc_IndexError); if (key_value == -1 && PyErr_Occurred()) return -1; return PySequence_SetItem(o, key_value, value); @@ -183,9 +181,9 @@ PyObject_DelItem(PyObject *o, PyObject *key) return m->mp_ass_subscript(o, key, (PyObject*)NULL); if (o->ob_type->tp_as_sequence) { - PyNumberMethods *nb = key->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(key) && nb->nb_index != NULL) { - Py_ssize_t key_value = nb->nb_index(key); + if (PyIndex_Check(key)) { + Py_ssize_t key_value; + key_value = PyNumber_AsSsize_t(key, PyExc_IndexError); if (key_value == -1 && PyErr_Occurred()) return -1; return PySequence_DelItem(o, key_value); @@ -653,9 +651,8 @@ static PyObject * sequence_repeat(ssizeargfunc repeatfunc, PyObject *seq, PyObject *n) { Py_ssize_t count; - PyNumberMethods *nb = n->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(n) && nb->nb_index != NULL) { - count = nb->nb_index(n); + if (PyIndex_Check(n)) { + count = PyNumber_AsSsize_t(n, PyExc_OverflowError); if (count == -1 && PyErr_Occurred()) return NULL; } @@ -938,23 +935,89 @@ int_from_string(const char *s, Py_ssize_t len) return x; } -/* Return a Py_ssize_t integer from the object item */ -Py_ssize_t +/* Return a Python Int or Long from the object item + Raise TypeError if the result is not an int-or-long + or if the object cannot be interpreted as an index. +*/ +PyObject * PyNumber_Index(PyObject *item) { - Py_ssize_t value = -1; - PyNumberMethods *nb = item->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(item) && nb->nb_index != NULL) { - value = nb->nb_index(item); + PyObject *result = NULL; + if (item == NULL) + return null_error(); + /* XXX(nnorwitz): should these be CheckExact? Aren't subclasses ok? */ + if (PyInt_CheckExact(item) || PyLong_CheckExact(item)) { + Py_INCREF(item); + return item; + } + if (PyIndex_Check(item)) { + result = item->ob_type->tp_as_number->nb_index(item); + /* XXX(nnorwitz): Aren't subclasses ok here too? */ + if (result && + !PyInt_CheckExact(result) && !PyLong_CheckExact(result)) { + PyErr_Format(PyExc_TypeError, + "__index__ returned non-(int,long) " \ + "(type %.200s)", + result->ob_type->tp_name); + Py_DECREF(result); + return NULL; + } } else { PyErr_Format(PyExc_TypeError, "'%.200s' object cannot be interpreted " "as an index", item->ob_type->tp_name); } - return value; + return result; +} + +/* Return an error on Overflow only if err is not NULL*/ + +Py_ssize_t +PyNumber_AsSsize_t(PyObject *item, PyObject *err) +{ + Py_ssize_t result; + PyObject *runerr; + PyObject *value = PyNumber_Index(item); + if (value == NULL) + return -1; + + /* We're done if PyInt_AsSsize_t() returns without error. */ + result = PyInt_AsSsize_t(value); + if (result != -1 || !(runerr = PyErr_Occurred())) + goto finish; + + /* Error handling code -- only manage OverflowError differently */ + if (!PyErr_GivenExceptionMatches(runerr, PyExc_OverflowError)) + goto finish; + + PyErr_Clear(); + /* If no error-handling desired then the default clipping + is sufficient. + */ + if (!err) { + assert(PyLong_Check(value)); + /* Whether or not it is less than or equal to + zero is determined by the sign of ob_size + */ + if (_PyLong_Sign(value) < 0) + result = PY_SSIZE_T_MIN; + else + result = PY_SSIZE_T_MAX; + } + else { + /* Otherwise replace the error with caller's error object. */ + PyErr_Format(err, + "cannot fit '%.200s' into an index-sized integer", + item->ob_type->tp_name); + } + + finish: + Py_DECREF(value); + return result; } + PyObject * PyNumber_Int(PyObject *o) { diff --git a/Objects/classobject.c b/Objects/classobject.c index 56bf29c..1e93908 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -1670,40 +1670,28 @@ instance_nonzero(PyInstanceObject *self) return outcome > 0; } -static Py_ssize_t +static PyObject * instance_index(PyInstanceObject *self) { PyObject *func, *res; - Py_ssize_t outcome; static PyObject *indexstr = NULL; if (indexstr == NULL) { indexstr = PyString_InternFromString("__index__"); if (indexstr == NULL) - return -1; + return NULL; } if ((func = instance_getattr(self, indexstr)) == NULL) { if (!PyErr_ExceptionMatches(PyExc_AttributeError)) - return -1; + return NULL; PyErr_Clear(); PyErr_SetString(PyExc_TypeError, "object cannot be interpreted as an index"); - return -1; + return NULL; } res = PyEval_CallObject(func, (PyObject *)NULL); Py_DECREF(func); - if (res == NULL) - return -1; - if (PyInt_Check(res) || PyLong_Check(res)) { - outcome = res->ob_type->tp_as_number->nb_index(res); - } - else { - PyErr_SetString(PyExc_TypeError, - "__index__ must return an int or a long"); - outcome = -1; - } - Py_DECREF(res); - return outcome; + return res; } @@ -2026,7 +2014,7 @@ static PyNumberMethods instance_as_number = { instance_truediv, /* nb_true_divide */ instance_ifloordiv, /* nb_inplace_floor_divide */ instance_itruediv, /* nb_inplace_true_divide */ - (lenfunc)instance_index, /* nb_index */ + (unaryfunc)instance_index, /* nb_index */ }; PyTypeObject PyInstance_Type = { diff --git a/Objects/intobject.c b/Objects/intobject.c index 2062bee..c7137df 100644 --- a/Objects/intobject.c +++ b/Objects/intobject.c @@ -193,16 +193,21 @@ PyInt_AsSsize_t(register PyObject *op) PyIntObject *io; Py_ssize_t val; #endif - if (op && !PyInt_CheckExact(op) && PyLong_Check(op)) + + if (op == NULL) { + PyErr_SetString(PyExc_TypeError, "an integer is required"); + return -1; + } + + if (PyInt_Check(op)) + return PyInt_AS_LONG((PyIntObject*) op); + if (PyLong_Check(op)) return _PyLong_AsSsize_t(op); #if SIZEOF_SIZE_T == SIZEOF_LONG return PyInt_AsLong(op); #else - if (op && PyInt_Check(op)) - return PyInt_AS_LONG((PyIntObject*) op); - - if (op == NULL || (nb = op->ob_type->tp_as_number) == NULL || + if ((nb = op->ob_type->tp_as_number) == NULL || (nb->nb_int == NULL && nb->nb_long == 0)) { PyErr_SetString(PyExc_TypeError, "an integer is required"); return -1; @@ -1079,7 +1084,7 @@ static PyNumberMethods int_as_number = { int_true_divide, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ - PyInt_AsSsize_t, /* nb_index */ + (unaryfunc)int_int, /* nb_index */ }; PyTypeObject PyInt_Type = { diff --git a/Objects/listobject.c b/Objects/listobject.c index f917385..ad27644 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -2450,14 +2450,13 @@ PyDoc_STRVAR(list_doc, "list() -> new list\n" "list(sequence) -> new list initialized from sequence's items"); -#define HASINDEX(o) PyType_HasFeature((o)->ob_type, Py_TPFLAGS_HAVE_INDEX) static PyObject * list_subscript(PyListObject* self, PyObject* item) { - PyNumberMethods *nb = item->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(item) && nb->nb_index != NULL) { - Py_ssize_t i = nb->nb_index(item); + if (PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return NULL; if (i < 0) @@ -2504,9 +2503,8 @@ list_subscript(PyListObject* self, PyObject* item) static int list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value) { - PyNumberMethods *nb = item->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(item) && nb->nb_index != NULL) { - Py_ssize_t i = nb->nb_index(item); + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return -1; if (i < 0) diff --git a/Objects/longobject.c b/Objects/longobject.c index 4ce9479..e32c425 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -240,8 +240,11 @@ PyLong_AsLong(PyObject *vv) return -1; } -static Py_ssize_t -_long_as_ssize_t(PyObject *vv) { +/* Get a Py_ssize_t from a long int object. + Returns -1 and sets an error condition if overflow occurs. */ + +Py_ssize_t +_PyLong_AsSsize_t(PyObject *vv) { register PyLongObject *v; size_t x, prev; Py_ssize_t i; @@ -277,45 +280,7 @@ _long_as_ssize_t(PyObject *vv) { overflow: PyErr_SetString(PyExc_OverflowError, "long int too large to convert to int"); - if (sign > 0) - return PY_SSIZE_T_MAX; - else - return PY_SSIZE_T_MIN; -} - -/* Get a Py_ssize_t from a long int object. - Returns -1 and sets an error condition if overflow occurs. */ - -Py_ssize_t -_PyLong_AsSsize_t(PyObject *vv) -{ - Py_ssize_t x; - - x = _long_as_ssize_t(vv); - if (PyErr_Occurred()) return -1; - return x; -} - - -/* Get a Py_ssize_t from a long int object. - Silently reduce values larger than PY_SSIZE_T_MAX to PY_SSIZE_T_MAX, - and silently boost values less than -PY_SSIZE_T_MAX-1 to -PY_SSIZE_T_MAX-1. - On error, return -1 with an exception set. -*/ - -static Py_ssize_t -long_index(PyObject *vv) -{ - Py_ssize_t x; - - x = _long_as_ssize_t(vv); - if (PyErr_Occurred()) { - /* If overflow error, ignore the error */ - if (x != -1) { - PyErr_Clear(); - } - } - return x; + return -1; } /* Get a C unsigned long int from a long int object. @@ -3405,7 +3370,7 @@ static PyNumberMethods long_as_number = { long_true_divide, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ - long_index, /* nb_index */ + long_long, /* nb_index */ }; PyTypeObject PyLong_Type = { diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 271a9ad..d8a2465 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -252,7 +252,7 @@ slice_indices(PySliceObject* self, PyObject* len) { Py_ssize_t ilen, start, stop, step, slicelength; - ilen = PyInt_AsSsize_t(len); + ilen = PyNumber_AsSsize_t(len, PyExc_OverflowError); if (ilen == -1 && PyErr_Occurred()) { return NULL; diff --git a/Objects/stringobject.c b/Objects/stringobject.c index 91f0103..bbbeaa6 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -1184,14 +1184,11 @@ string_hash(PyStringObject *a) return x; } -#define HASINDEX(o) PyType_HasFeature((o)->ob_type, Py_TPFLAGS_HAVE_INDEX) - static PyObject* string_subscript(PyStringObject* self, PyObject* item) { - PyNumberMethods *nb = item->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(item) && nb->nb_index != NULL) { - Py_ssize_t i = nb->nb_index(item); + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return NULL; if (i < 0) diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 2161ab9..6f3711f 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -577,14 +577,11 @@ static PySequenceMethods tuple_as_sequence = { (objobjproc)tuplecontains, /* sq_contains */ }; -#define HASINDEX(o) PyType_HasFeature((o)->ob_type, Py_TPFLAGS_HAVE_INDEX) - static PyObject* tuplesubscript(PyTupleObject* self, PyObject* item) { - PyNumberMethods *nb = item->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(item) && nb->nb_index != NULL) { - Py_ssize_t i = nb->nb_index(item); + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return NULL; if (i < 0) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 485d2bb..517d4db 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3527,7 +3527,7 @@ wrap_indexargfunc(PyObject *self, PyObject *args, void *wrapped) if (!PyArg_UnpackTuple(args, "", 1, 1, &o)) return NULL; - i = PyNumber_Index(o); + i = PyNumber_AsSsize_t(o, PyExc_OverflowError); if (i == -1 && PyErr_Occurred()) return NULL; return (*func)(self, i); @@ -3538,7 +3538,7 @@ getindex(PyObject *self, PyObject *arg) { Py_ssize_t i; - i = PyNumber_Index(arg); + i = PyNumber_AsSsize_t(arg, PyExc_OverflowError); if (i == -1 && PyErr_Occurred()) return -1; if (i < 0) { @@ -4344,26 +4344,11 @@ slot_nb_nonzero(PyObject *self) } -static Py_ssize_t +static PyObject * slot_nb_index(PyObject *self) { static PyObject *index_str; - PyObject *temp = call_method(self, "__index__", &index_str, "()"); - Py_ssize_t result; - - if (temp == NULL) - return -1; - if (PyInt_CheckExact(temp) || PyLong_CheckExact(temp)) { - result = temp->ob_type->tp_as_number->nb_index(temp); - } - else { - PyErr_Format(PyExc_TypeError, - "__index__ must return an int or a long, " - "not '%.200s'", temp->ob_type->tp_name); - result = -1; - } - Py_DECREF(temp); - return result; + return call_method(self, "__index__", &index_str, "()"); } @@ -5109,7 +5094,7 @@ static slotdef slotdefs[] = { "oct(x)"), UNSLOT("__hex__", nb_hex, slot_nb_hex, wrap_unaryfunc, "hex(x)"), - NBSLOT("__index__", nb_index, slot_nb_index, wrap_lenfunc, + NBSLOT("__index__", nb_index, slot_nb_index, wrap_unaryfunc, "x[y:z] <==> x[y.__index__():z.__index__()]"), IBSLOT("__iadd__", nb_inplace_add, slot_nb_inplace_add, wrap_binaryfunc, "+"), diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 096dfc6..ababda1 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -6985,14 +6985,11 @@ static PySequenceMethods unicode_as_sequence = { PyUnicode_Contains, /* sq_contains */ }; -#define HASINDEX(o) PyType_HasFeature((o)->ob_type, Py_TPFLAGS_HAVE_INDEX) - static PyObject* unicode_subscript(PyUnicodeObject* self, PyObject* item) { - PyNumberMethods *nb = item->ob_type->tp_as_number; - if (nb != NULL && HASINDEX(item) && nb->nb_index != NULL) { - Py_ssize_t i = nb->nb_index(item); + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return NULL; if (i < 0) diff --git a/Python/ceval.c b/Python/ceval.c index a0e8b30..cd8ff9b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3866,12 +3866,14 @@ _PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi) if (v != NULL) { Py_ssize_t x; if (PyInt_Check(v)) { - x = PyInt_AsSsize_t(v); + /* XXX(nnorwitz): I think PyInt_AS_LONG is correct, + however, it looks like it should be AsSsize_t. + There should be a comment here explaining why. + */ + x = PyInt_AS_LONG(v); } - else if (v->ob_type->tp_as_number && - PyType_HasFeature(v->ob_type, Py_TPFLAGS_HAVE_INDEX) - && v->ob_type->tp_as_number->nb_index) { - x = v->ob_type->tp_as_number->nb_index(v); + else if (PyIndex_Check(v)) { + x = PyNumber_AsSsize_t(v, NULL); if (x == -1 && PyErr_Occurred()) return 0; } @@ -3887,10 +3889,8 @@ _PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi) } #undef ISINDEX -#define ISINDEX(x) ((x) == NULL || PyInt_Check(x) || PyLong_Check(x) || \ - ((x)->ob_type->tp_as_number && \ - PyType_HasFeature((x)->ob_type, Py_TPFLAGS_HAVE_INDEX) \ - && (x)->ob_type->tp_as_number->nb_index)) +#define ISINDEX(x) ((x) == NULL || \ + PyInt_Check(x) || PyLong_Check(x) || PyIndex_Check(x)) static PyObject * apply_slice(PyObject *u, PyObject *v, PyObject *w) /* return u[v:w] */ |