]> granicus.if.org Git - python/commitdiff
Patch #1538606, Patch to fix __index__() clipping.
authorNeal Norwitz <nnorwitz@gmail.com>
Sat, 12 Aug 2006 17:03:09 +0000 (17:03 +0000)
committerNeal Norwitz <nnorwitz@gmail.com>
Sat, 12 Aug 2006 17:03:09 +0000 (17:03 +0000)
I modified this patch some by fixing style, some error checking, and adding
XXX comments.  This patch requires review and some changes are to be expected.
I'm checking in now to get the greatest possible review and establish a
baseline for moving forward.  I don't want this to hold up release if possible.

19 files changed:
Doc/api/abstract.tex
Include/abstract.h
Include/object.h
Lib/test/test_index.py
Misc/NEWS
Modules/arraymodule.c
Modules/mmapmodule.c
Modules/operator.c
Objects/abstract.c
Objects/classobject.c
Objects/intobject.c
Objects/listobject.c
Objects/longobject.c
Objects/sliceobject.c
Objects/stringobject.c
Objects/tupleobject.c
Objects/typeobject.c
Objects/unicodeobject.c
Python/ceval.c

index f740efbc7edefe45a762c542ad10d205d100d238..9c39403dfa23bf511841a07ada08a9755c35fc2e 100644 (file)
@@ -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}
index f96b297eff94a3e0960947e314d92180e79a5ea3..9b0b3f0b1860e92c897212753b23563cfd826281 100644 (file)
@@ -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);
 
index 4b0e08010dbbc17094f189dcbbb388e0c47af7a7..b0817e6e31c5c901f16cced708cb2d1da10879b4 100644 (file)
@@ -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 {
index 45b3b2b6cde4006113c61adec5d0ecd86a1a773c..b224a5017be42c6b04cb370bd117ad202a97e5c6 100644 (file)
@@ -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__":
index 26047f6ce95e5af15343afce03af9c3ad4f85b2d..49de4b64166beaf2e44e7afd85ebb0b5178f851a 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.
 
index 14e5e5d2d78eb1eeaa0a88cfbefee0c13c34693c..efa7835c375d4f10b60d9ce27aca909bf0ca3b82 100644 (file)
@@ -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)
index b2dd6751818dd2648ba4ef430c6046b73c9763b3..53df27532977bdffe648e1e64106a4195ee72cf2 100644 (file)
@@ -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;
 }
 
index 7fc1f8abe820e3dc8cd17dbba2b97d91e78fcb69..7479a53553201b63d4644849aa2f3a1e1a786037 100644 (file)
@@ -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.")
index bad9f965415eeced89713d3691d8bb4e306a81b6..c8e9ddcf4942958474a41cb476127a4cf90f994a 100644 (file)
@@ -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)
 {
index 56bf29c5b5ab44c423378ce00f81bcdfc041236c..1e939083f093d34abba879b86a711f2385ed46ce 100644 (file)
@@ -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 = {
index 2062bee9de18d4940987634e9f07b6debb5fb46b..c7137df40ba83df80c063a22b6a305c65d62a63d 100644 (file)
@@ -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 = {
index f917385fa0f3f407f83ea2b4794f3753f80b55bf..ad276447d3f132f39d60caf492229752cc1ce29d 100644 (file)
@@ -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)
index 4ce947955a98baa57e1a7e1929d8435daec8d1ec..e32c42566eb24cbf25497751db16702fed06d0f2 100644 (file)
@@ -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 = {
index 271a9ad62d223823cc4b8508f98d5a84eddb5f8d..d8a24653a7d77b1176a8f3bf37eb9c13c4a87a33 100644 (file)
@@ -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;
index 91f010348615a80d871a697ee1df63fc8365c2c7..bbbeaa6c0a4c7ac6257cd0ddddea3f2f003c6535 100644 (file)
@@ -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)
index 2161ab9ec1088132c3da051a798c801ccfaf4e10..6f3711f1f457f290cc05d532a2e644c506b1b495 100644 (file)
@@ -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)
index 485d2bb59df0f332b1b3b4bf6ddb7f73625f5f7f..517d4db6341d492059bea33799099dc98ca93020 100644 (file)
@@ -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, "+"),
index 096dfc6ee1fa473681346a63475cab4a865a2dac..ababda1bb5b715609d5beaf6e14322bfc160579b 100644 (file)
@@ -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)
index a0e8b30c0526fc430ce216e1365659f7de69d9db..cd8ff9b57c4efa90f1ac388ec91c1fb0e6b1c9d3 100644 (file)
@@ -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] */