diff --git a/Lib/test/seq_tests.py b/Lib/test/seq_tests.py index df29a33665d..2ea50945ed2 100644 --- a/Lib/test/seq_tests.py +++ b/Lib/test/seq_tests.py @@ -4,6 +4,7 @@ Tests common to tuple, list and UserList.UserList import unittest from test import test_support +import sys # Various iterables # This is used for checking the constructor (here and in test_deque.py) @@ -328,3 +329,64 @@ class CommonTest(unittest.TestCase): self.assertEqual(a.__getitem__(slice(3,5)), self.type2test([])) self.assertRaises(ValueError, a.__getitem__, slice(0, 10, 0)) self.assertRaises(TypeError, a.__getitem__, 'x') + + def test_count(self): + a = self.type2test([0, 1, 2])*3 + self.assertEqual(a.count(0), 3) + self.assertEqual(a.count(1), 3) + self.assertEqual(a.count(3), 0) + + self.assertRaises(TypeError, a.count) + + class BadExc(Exception): + pass + + class BadCmp: + def __eq__(self, other): + if other == 2: + raise BadExc() + return False + + self.assertRaises(BadExc, a.count, BadCmp()) + + def test_index(self): + u = self.type2test([0, 1]) + self.assertEqual(u.index(0), 0) + self.assertEqual(u.index(1), 1) + self.assertRaises(ValueError, u.index, 2) + + u = self.type2test([-2, -1, 0, 0, 1, 2]) + self.assertEqual(u.count(0), 2) + self.assertEqual(u.index(0), 2) + self.assertEqual(u.index(0, 2), 2) + self.assertEqual(u.index(-2, -10), 0) + self.assertEqual(u.index(0, 3), 3) + self.assertEqual(u.index(0, 3, 4), 3) + self.assertRaises(ValueError, u.index, 2, 0, -10) + + self.assertRaises(TypeError, u.index) + + class BadExc(Exception): + pass + + class BadCmp: + def __eq__(self, other): + if other == 2: + raise BadExc() + return False + + a = self.type2test([0, 1, 2, 3]) + self.assertRaises(BadExc, a.index, BadCmp()) + + a = self.type2test([-2, -1, 0, 0, 1, 2]) + self.assertEqual(a.index(0), 2) + self.assertEqual(a.index(0, 2), 2) + self.assertEqual(a.index(0, -4), 2) + self.assertEqual(a.index(-2, -10), 0) + self.assertEqual(a.index(0, 3), 3) + self.assertEqual(a.index(0, -3), 3) + self.assertEqual(a.index(0, 3, 4), 3) + self.assertEqual(a.index(0, -3, -2), 3) + self.assertEqual(a.index(0, -4*sys.maxint, 4*sys.maxint), 2) + self.assertRaises(ValueError, a.index, 0, 4*sys.maxint,-4*sys.maxint) + self.assertRaises(ValueError, a.index, 2, 0, -10) diff --git a/Misc/NEWS b/Misc/NEWS index 172678e4b91..d15531dc139 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -25,6 +25,9 @@ Core and builtins - Issue #1678380: Fix a bug that identifies 0j and -0j when they appear in the same code unit. +- Issue #2025 : Add tuple.count() and tuple.index() methods to comply with + the collections.Sequence API. + - Patch #1970 by Antoine Pitrou: Speedup unicode whitespace and linebreak detection diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index b609b54aa00..69190d53e1d 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -455,6 +455,53 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n) return (PyObject *) np; } +static PyObject * +tupleindex(PyTupleObject *self, PyObject *args) +{ + Py_ssize_t i, start=0, stop=Py_SIZE(self); + PyObject *v; + + if (!PyArg_ParseTuple(args, "O|O&O&:index", &v, + _PyEval_SliceIndex, &start, + _PyEval_SliceIndex, &stop)) + return NULL; + if (start < 0) { + start += Py_SIZE(self); + if (start < 0) + start = 0; + } + if (stop < 0) { + stop += Py_SIZE(self); + if (stop < 0) + stop = 0; + } + for (i = start; i < stop && i < Py_SIZE(self); i++) { + int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ); + if (cmp > 0) + return PyLong_FromSsize_t(i); + else if (cmp < 0) + return NULL; + } + PyErr_SetString(PyExc_ValueError, "tuple.index(x): x not in list"); + return NULL; +} + +static PyObject * +tuplecount(PyTupleObject *self, PyObject *v) +{ + Py_ssize_t count = 0; + Py_ssize_t i; + + for (i = 0; i < Py_SIZE(self); i++) { + int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ); + if (cmp > 0) + count++; + else if (cmp < 0) + return NULL; + } + return PyLong_FromSsize_t(count); +} + static int tupletraverse(PyTupleObject *o, visitproc visit, void *arg) { @@ -661,8 +708,15 @@ tuple_getnewargs(PyTupleObject *v) } +PyDoc_STRVAR(index_doc, +"T.index(value, [start, [stop]]) -> integer -- return first index of value"); +PyDoc_STRVAR(count_doc, +"T.count(value) -> integer -- return number of occurrences of value"); + static PyMethodDef tuple_methods[] = { {"__getnewargs__", (PyCFunction)tuple_getnewargs, METH_NOARGS}, + {"index", (PyCFunction)tupleindex, METH_VARARGS, index_doc}, + {"count", (PyCFunction)tuplecount, METH_O, count_doc}, {NULL, NULL} /* sentinel */ };