summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/api/abstract.tex25
-rw-r--r--Include/abstract.h20
-rw-r--r--Include/object.h2
-rw-r--r--Lib/test/test_index.py174
-rw-r--r--Misc/NEWS5
-rw-r--r--Modules/arraymodule.c12
-rw-r--r--Modules/mmapmodule.c30
-rw-r--r--Modules/operator.c12
-rw-r--r--Objects/abstract.c105
-rw-r--r--Objects/classobject.c24
-rw-r--r--Objects/intobject.c17
-rw-r--r--Objects/listobject.c12
-rw-r--r--Objects/longobject.c49
-rw-r--r--Objects/sliceobject.c2
-rw-r--r--Objects/stringobject.c7
-rw-r--r--Objects/tupleobject.c7
-rw-r--r--Objects/typeobject.c25
-rw-r--r--Objects/unicodeobject.c7
-rw-r--r--Python/ceval.c18
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__":
diff --git a/Misc/NEWS b/Misc/NEWS
index 26047f6..49de4b6 100644
--- 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.
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] */