diff -r b92296e62450 Doc/lib/libarray.tex --- a/Doc/lib/libarray.tex Fri May 19 09:10:08 2006 +0200 +++ b/Doc/lib/libarray.tex Sat May 20 13:09:16 2006 +0200 @@ -177,8 +177,13 @@ Remove the first occurrence of \var{x} f Remove the first occurrence of \var{x} from the array. \end{methoddesc} -\begin{methoddesc}[array]{reverse}{} -Reverse the order of the items in the array. +\begin{methoddesc}[array]{reverse}{\optional{start\optional{, stop}}} +Reverse the order of the items in the array. In case you specify \var{start} +or \var{stop}, the respective slice is reversed in place. The default values +for \var{start} and \var{stop} are \code{None}, which signal the beginning and +end of the array, respectively. + +\versionchanged[Support for start and stop was added]{2.5} \end{methoddesc} \begin{methoddesc}[array]{tofile}{f} diff -r b92296e62450 Doc/lib/libfuncs.tex --- a/Doc/lib/libfuncs.tex Fri May 19 09:10:08 2006 +0200 +++ b/Doc/lib/libfuncs.tex Sat May 20 13:09:16 2006 +0200 @@ -991,11 +991,15 @@ except NameError: \end{funcdesc} \begin{funcdesc}{sorted}{iterable\optional{, cmp\optional{, - key\optional{, reverse}}}} + key\optional{, reverse\optional{, + start\optional{, stop}}}}}} Return a new sorted list from the items in \var{iterable}. - The optional arguments \var{cmp}, \var{key}, and \var{reverse} - have the same meaning as those for the \method{list.sort()} method. + The optional arguments \var{cmp}, \var{key}, \var{reverse}, \var{start} + and \var{stop} have the same meaning as those for the + \method{list.sort()} method. \versionadded{2.4} + + \versionchanged[\var{start} and \var{stop} added]{2.5} \end{funcdesc} \begin{funcdesc}{staticmethod}{function} diff -r b92296e62450 Doc/lib/libstdtypes.tex --- a/Doc/lib/libstdtypes.tex Fri May 19 09:10:08 2006 +0200 +++ b/Doc/lib/libstdtypes.tex Sat May 20 13:09:16 2006 +0200 @@ -1098,11 +1098,12 @@ The following operations are defined on {same as \code{\var{x} = \var{s}[\var{i}]; del \var{s}[\var{i}]; return \var{x}}}{(6)} \lineiii{\var{s}.remove(\var{x})} {same as \code{del \var{s}[\var{s}.index(\var{x})]}}{(4)} - \lineiii{\var{s}.reverse()} - {reverses the items of \var{s} in place}{(7)} + \lineiii{\var{s}.reverse(\optional{\var{start}\optional{, \var{stop}}})} + {reverses the items of \var{s} in place}{(7), (11)} \lineiii{\var{s}.sort(\optional{\var{cmp}\optional{, - \var{key}\optional{, \var{reverse}}}})} - {sort the items of \var{s} in place}{(7), (8), (9), (10)} + \var{key}\optional{, \var{reverse}\optional{, + \var{start}\optional{, \var{stop}}}}}})} + {sort the items of \var{s} in place}{(7), (8), (9), (10), (11)} \end{tableiii} \indexiv{operations on}{mutable}{sequence}{types} \indexiii{operations on}{sequence}{types} @@ -1188,6 +1189,14 @@ Notes: implementation of Python 2.3 and newer makes the list appear empty for the duration, and raises \exception{ValueError} if it can detect that the list has been mutated during a sort. + +\item[(11)] When specifying \var{start} or \var{stop} for \method{sort()} or + \method{reverse()}, the specified slice of the list is sorted or reversed + in place. \var{start} and \var{stop} are interpreted as normal slice + notation. In case you specify \code{None} for one of them, the list beginning + or end is used, respectively. + + \versionchanged[Support for \var{start} and \var{stop} was added]{2.5} \end{description} \subsection{Set Types --- diff -r b92296e62450 Doc/whatsnew/whatsnew25.tex --- a/Doc/whatsnew/whatsnew25.tex Fri May 19 09:10:08 2006 +0200 +++ b/Doc/whatsnew/whatsnew25.tex Sat May 20 13:09:16 2006 +0200 @@ -1096,6 +1096,12 @@ class C(): \end{verbatim} (Implemented by Brett Cannon.) +\item The methods \method{list.reverse()}, \method{array.reverse()} and +\method{list.sort()} now accept \var{start} and \var{stop} arguments to +specify a slice of the whole container to which the specified operation +is applied. The builtin function \function{sorted()} was also extended +to include this modus operandi. (Implemented by Heiko Wundram.) + \end{itemize} diff -r b92296e62450 Lib/UserList.py --- a/Lib/UserList.py Fri May 19 09:10:08 2006 +0200 +++ b/Lib/UserList.py Sat May 20 13:09:16 2006 +0200 @@ -76,7 +76,7 @@ class UserList: def remove(self, item): self.data.remove(item) def count(self, item): return self.data.count(item) def index(self, item, *args): return self.data.index(item, *args) - def reverse(self): self.data.reverse() + def reverse(self, *args, **kwds): self.data.reverse(*args, **kwds) def sort(self, *args, **kwds): self.data.sort(*args, **kwds) def extend(self, other): if isinstance(other, UserList): diff -r b92296e62450 Lib/test/list_tests.py --- a/Lib/test/list_tests.py Fri May 19 09:10:08 2006 +0200 +++ b/Lib/test/list_tests.py Sat May 20 13:09:16 2006 +0200 @@ -409,11 +409,20 @@ class CommonTest(seq_tests.CommonTest): u = self.type2test([-2, -1, 0, 1, 2]) u2 = u[:] u.reverse() - self.assertEqual(u, [2, 1, 0, -1, -2]) + self.assertEqual(u, self.type2test([2, 1, 0, -1, -2])) u.reverse() self.assertEqual(u, u2) - self.assertRaises(TypeError, u.reverse, 42) + self.assertRaises(TypeError, u.reverse, "a", "b") + + self.assertRaises(TypeError, u.reverse, 42, 42, 42) + + u = self.type2test([-2, -1, 0, 1, 2]) + u2 = u[:] + u.reverse(start=1, stop=-1) + self.assertEqual(u, self.type2test([-2, 1, 0, -1, 2])) + u.reverse(start=1, stop=-1) + self.assertEqual(u, u2) def test_sort(self): u = self.type2test([1, 0]) @@ -446,7 +455,17 @@ class CommonTest(seq_tests.CommonTest): self.assertRaises(TypeError, z.sort, lambda x, y: 's') - self.assertRaises(TypeError, z.sort, 42, 42, 42, 42) + self.assertRaises(TypeError, z.sort, None, None, False, "a", "b") + + self.assertRaises(TypeError, z.sort, 42, 42, 42, 42, 42, 42) + + u = self.type2test([2, 1, 0, -1, -2]) + u.sort(start=1, stop=-1) + self.assertEqual(u, self.type2test([2, -1, 0, 1, -2])) + + u = self.type2test([-2, -1, 0, 1, 2]) + u.sort(reverse=True, start=1, stop=-1) + self.assertEqual(u, self.type2test([-2, 1, 0, -1, 2])) def test_slice(self): u = self.type2test("spam") diff -r b92296e62450 Lib/test/test_array.py --- a/Lib/test/test_array.py Fri May 19 09:10:08 2006 +0200 +++ b/Lib/test/test_array.py Sat May 20 13:09:16 2006 +0200 @@ -598,11 +598,20 @@ class BaseTest(unittest.TestCase): def test_reverse(self): a = array.array(self.typecode, self.example) - self.assertRaises(TypeError, a.reverse, 42) + self.assertRaises(TypeError, a.reverse, "a", "b") + self.assertRaises(TypeError, a.reverse, 42, 42, 42) a.reverse() self.assertEqual( a, array.array(self.typecode, self.example[::-1]) + ) + a.reverse(start=1, stop=-1) + self.assertEqual( + a, + array.array(self.typecode, + [self.example[-1]]+ + list(self.example[1:-1])+ + [self.example[0]]) ) def test_extend(self): diff -r b92296e62450 Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py Fri May 19 09:10:08 2006 +0200 +++ b/Lib/test/test_builtin.py Sat May 20 13:09:16 2006 +0200 @@ -1541,6 +1541,11 @@ class TestSorted(unittest.TestCase): self.assertEqual(data, sorted(copy, reverse=1)) self.assertNotEqual(data, copy) + u = [-2, -1, 0, 1, 2] + u2 = u[:] + u.reverse(start=1, stop=-1) + self.assertEqual(u, sorted(u2, start=1, stop=-1, reverse=True)) + def test_inputtypes(self): s = 'abracadabra' types = [list, tuple] diff -r b92296e62450 Modules/arraymodule.c --- a/Modules/arraymodule.c Fri May 19 09:10:08 2006 +0200 +++ b/Modules/arraymodule.c Sat May 20 13:09:16 2006 +0200 @@ -1158,30 +1158,63 @@ PyDoc_STRVAR(array_doc, "Return state in PyDoc_STRVAR(array_doc, "Return state information for pickling."); static PyObject * -array_reverse(arrayobject *self, PyObject *unused) +array_reverse(arrayobject *self, PyObject *args, PyObject *kwds) { register Py_ssize_t itemsize = self->ob_descr->itemsize; register char *p, *q; + PyObject *ostart = NULL, *ostop = NULL; + Py_ssize_t start, stop; /* little buffer to hold items while swapping */ char tmp[256]; /* 8 is probably enough -- but why skimp */ + static char *kwlist[] = {"start", "stop", NULL}; + assert((size_t)itemsize <= sizeof(tmp)); - if (self->ob_size > 1) { - for (p = self->ob_item, - q = self->ob_item + (self->ob_size - 1)*itemsize; - p < q; - p += itemsize, q -= itemsize) { - /* memory areas guaranteed disjoint, so memcpy - * is safe (& memmove may be slower). - */ - memcpy(tmp, p, itemsize); - memcpy(p, q, itemsize); - memcpy(q, tmp, itemsize); - } - } - - Py_INCREF(Py_None); - return Py_None; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:reverse", + kwlist, &ostart, &ostop)) + return NULL; + + if (ostart && ostart != Py_None) { + if (!PyInt_Check(ostart)) { + PyErr_SetString(PyExc_TypeError, + "start must be an integer or None."); + return NULL; + } + start = PyInt_AS_LONG(ostart); + if (start < 0) + start += self->ob_size; + start = start > self->ob_size ? self->ob_size : start; + } else + start = 0; + if (ostop && ostop != Py_None) { + if (!PyInt_Check(ostop)) { + PyErr_SetString(PyExc_TypeError, + "stop must be an integer or None."); + return NULL; + } + stop = PyInt_AS_LONG(ostop); + if (stop < 0) + stop += self->ob_size; + stop = stop > self->ob_size ? self->ob_size : stop; + } else + stop = self->ob_size; + + if (stop-start < 2) + Py_RETURN_NONE; + + for (p = self->ob_item + start*itemsize, + q = self->ob_item + (stop - 1)*itemsize; + p < q; + p += itemsize, q -= itemsize) { + /* memory areas guaranteed disjoint, so memcpy + * is safe (& memmove may be slower). + */ + memcpy(tmp, p, itemsize); + memcpy(p, q, itemsize); + memcpy(q, tmp, itemsize); + } + + Py_RETURN_NONE; } PyDoc_STRVAR(reverse_doc, @@ -1519,7 +1552,7 @@ PyMethodDef array_methods[] = { array_doc}, {"remove", (PyCFunction)array_remove, METH_O, remove_doc}, - {"reverse", (PyCFunction)array_reverse, METH_NOARGS, + {"reverse", (PyCFunction)array_reverse, METH_VARARGS|METH_KEYWORDS, reverse_doc}, /* {"sort", (PyCFunction)array_sort, METH_VARARGS, sort_doc},*/ diff -r b92296e62450 Objects/listobject.c --- a/Objects/listobject.c Fri May 19 09:10:08 2006 +0200 +++ b/Objects/listobject.c Sat May 20 13:09:16 2006 +0200 @@ -1987,15 +1987,18 @@ listsort(PyListObject *self, PyObject *a PyObject *result = NULL; /* guilty until proved innocent */ int reverse = 0; PyObject *keyfunc = NULL; + PyObject *ostart = NULL; + PyObject *ostop = NULL; Py_ssize_t i; PyObject *key, *value, *kvpair; - static char *kwlist[] = {"cmp", "key", "reverse", 0}; + Py_ssize_t start, stop; + static char *kwlist[] = {"cmp", "key", "reverse", "start", "stop", 0}; assert(self != NULL); assert (PyList_Check(self)); if (args != NULL) { - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi:sort", - kwlist, &compare, &keyfunc, &reverse)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOiOO:sort", + kwlist, &compare, &keyfunc, &reverse, &ostart, &ostop)) return NULL; } if (compare == Py_None) @@ -2008,6 +2011,41 @@ listsort(PyListObject *self, PyObject *a return NULL; } else Py_XINCREF(compare); + + + /* Compute start, stop based on the available data. */ + if (ostart && ostart != Py_None) { + if (!PyInt_Check(ostart)) { + PyErr_SetString(PyExc_TypeError, + "start must be an integer or None"); + Py_XDECREF(compare); + return NULL; + } + start = PyInt_AS_LONG(ostart); + if (start < 0) + start += self->ob_size; + start = start > self->ob_size ? self->ob_size : start; + } else + start = 0; + if (ostop && ostop != Py_None) { + if (!PyInt_Check(ostop)) { + PyErr_SetString(PyExc_TypeError, + "stop must be an integer or None"); + Py_XDECREF(compare); + return NULL; + } + stop = PyInt_AS_LONG(ostop); + if (stop < 0) + stop += self->ob_size; + stop = stop > self->ob_size ? self->ob_size : stop; + } else + stop = self->ob_size; + + /* If there are less than two elements to sort, return immediately. */ + if ((nremaining = stop-start) < 2) { + Py_XDECREF(compare); + Py_RETURN_NONE; + } /* The list is temporarily made empty, so that mutations performed * by comparison functions can't affect the slice of memory we're @@ -2022,12 +2060,12 @@ listsort(PyListObject *self, PyObject *a self->allocated = -1; /* any operation will reset it to >= 0 */ if (keyfunc != NULL) { - for (i=0 ; i < saved_ob_size ; i++) { + for (i=start ; i < stop ; i++) { value = saved_ob_item[i]; key = PyObject_CallFunctionObjArgs(keyfunc, value, NULL); if (key == NULL) { - for (i=i-1 ; i>=0 ; i--) { + for (i=i-1 ; i>=start ; i--) { kvpair = saved_ob_item[i]; value = sortwrapper_getvalue(kvpair); saved_ob_item[i] = value; @@ -2036,27 +2074,31 @@ listsort(PyListObject *self, PyObject *a goto dsu_fail; } kvpair = build_sortwrapper(key, value); - if (kvpair == NULL) + /* The built pairs weren't freed here before...? */ + if (kvpair == NULL) { + for (i=i-1 ; i>=start ; i--) { + kvpair = saved_ob_item[i]; + value = sortwrapper_getvalue(kvpair); + saved_ob_item[i] = value; + Py_DECREF(kvpair); + } goto dsu_fail; + } saved_ob_item[i] = kvpair; } } /* Reverse sort stability achieved by initially reversing the list, applying a stable forward sort, then reversing the final result. */ - if (reverse && saved_ob_size > 1) - reverse_slice(saved_ob_item, saved_ob_item + saved_ob_size); + if (reverse) + reverse_slice(saved_ob_item + start, saved_ob_item + stop); merge_init(&ms, compare); - - nremaining = saved_ob_size; - if (nremaining < 2) - goto succeed; /* March over the array once, left to right, finding natural runs, * and extending short natural runs to minrun elements. */ - lo = saved_ob_item; + lo = saved_ob_item + start; hi = lo + nremaining; minrun = merge_compute_minrun(nremaining); do { @@ -2093,14 +2135,13 @@ listsort(PyListObject *self, PyObject *a if (merge_force_collapse(&ms) < 0) goto fail; assert(ms.n == 1); - assert(ms.pending[0].base == saved_ob_item); - assert(ms.pending[0].len == saved_ob_size); - -succeed: + assert(ms.pending[0].base == saved_ob_item + start); + assert(ms.pending[0].len == stop - start); + result = Py_None; fail: if (keyfunc != NULL) { - for (i=0 ; i < saved_ob_size ; i++) { + for (i=start ; i < stop ; i++) { kvpair = saved_ob_item[i]; value = sortwrapper_getvalue(kvpair); saved_ob_item[i] = value; @@ -2116,8 +2157,8 @@ fail: result = NULL; } - if (reverse && saved_ob_size > 1) - reverse_slice(saved_ob_item, saved_ob_item + saved_ob_size); + if (reverse) + reverse_slice(saved_ob_item + start, saved_ob_item + stop); merge_freemem(&ms); @@ -2157,10 +2198,53 @@ PyList_Sort(PyObject *v) } static PyObject * -listreverse(PyListObject *self) -{ - if (self->ob_size > 1) - reverse_slice(self->ob_item, self->ob_item + self->ob_size); +listreverse(PyListObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *ostart = NULL; + PyObject *ostop = NULL; + Py_ssize_t start, stop; + static char *kwlist[] = {"start", "stop", 0}; + + assert(self != NULL); + assert (PyList_Check(self)); + if (args != NULL) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:reverse", + kwlist, &ostart, &ostop)) + return NULL; + } + + /* Check start and stop, and convert to proper integer values. */ + if (ostart && ostart != Py_None) { + if (!PyInt_Check(ostart)) { + PyErr_SetString(PyExc_TypeError, + "start must be an integer or None"); + return NULL; + } + start = PyInt_AS_LONG(ostart); + if (start < 0) + start += self->ob_size; + start = start > self->ob_size ? self->ob_size : start; + } else + start = 0; + if (ostop && ostop != Py_None) { + if (!PyInt_Check(ostop)) { + PyErr_SetString(PyExc_TypeError, + "stop must be an integer or None"); + return NULL; + } + stop = PyInt_AS_LONG(ostop); + if (stop < 0) + stop += self->ob_size; + stop = stop > self->ob_size ? self->ob_size : stop; + } else + stop = self->ob_size; + + /* If there are less than two elements to reverse, return + immediately. */ + if (stop-start < 2) + Py_RETURN_NONE; + + reverse_slice(self->ob_item + start, self->ob_item + stop); Py_RETURN_NONE; } @@ -2409,9 +2493,10 @@ PyDoc_STRVAR(count_doc, PyDoc_STRVAR(count_doc, "L.count(value) -> integer -- return number of occurrences of value"); PyDoc_STRVAR(reverse_doc, -"L.reverse() -- reverse *IN PLACE*"); +"L.reverse(start=None, stop=None) -- reverse list *IN PLACE*"); PyDoc_STRVAR(sort_doc, -"L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;\n\ +"L.sort(cmp=None, key=None, reverse=False, start=None, stop=None)\n\ + -- stable sort *IN PLACE*;\n\ cmp(x, y) -> -1, 0, 1"); static PyObject *list_subscript(PyListObject*, PyObject*); @@ -2426,7 +2511,7 @@ static PyMethodDef list_methods[] = { {"remove", (PyCFunction)listremove, METH_O, remove_doc}, {"index", (PyCFunction)listindex, METH_VARARGS, index_doc}, {"count", (PyCFunction)listcount, METH_O, count_doc}, - {"reverse", (PyCFunction)listreverse, METH_NOARGS, reverse_doc}, + {"reverse", (PyCFunction)listreverse, METH_VARARGS | METH_KEYWORDS, reverse_doc}, {"sort", (PyCFunction)listsort, METH_VARARGS | METH_KEYWORDS, sort_doc}, {NULL, NULL} /* sentinel */ }; diff -r b92296e62450 Python/bltinmodule.c --- a/Python/bltinmodule.c Fri May 19 09:10:08 2006 +0200 +++ b/Python/bltinmodule.c Sat May 20 13:09:16 2006 +0200 @@ -1925,14 +1925,15 @@ static PyObject * static PyObject * builtin_sorted(PyObject *self, PyObject *args, PyObject *kwds) { - PyObject *newlist, *v, *seq, *compare=NULL, *keyfunc=NULL, *newargs; - PyObject *callable; - static char *kwlist[] = {"iterable", "cmp", "key", "reverse", 0}; + PyObject *newlist, *v, *seq, *compare=NULL, *keyfunc=NULL; + PyObject *start, *stop, *newargs, *callable; + static char *kwlist[] = {"iterable", "cmp", "key", "reverse", "start", + "stop", 0}; int reverse; /* args 1-4 should match listsort in Objects/listobject.c */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOi:sorted", - kwlist, &seq, &compare, &keyfunc, &reverse)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOiOO:sorted", + kwlist, &seq, &compare, &keyfunc, &reverse, &start, &stop)) return NULL; newlist = PySequence_List(seq); @@ -1945,7 +1946,7 @@ builtin_sorted(PyObject *self, PyObject return NULL; } - newargs = PyTuple_GetSlice(args, 1, 4); + newargs = PyTuple_GetSlice(args, 1, 6); if (newargs == NULL) { Py_DECREF(newlist); Py_DECREF(callable); @@ -1964,7 +1965,8 @@ builtin_sorted(PyObject *self, PyObject } PyDoc_STRVAR(sorted_doc, -"sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list"); +"sorted(iterable, cmp=None, key=None, reverse=False, start=None, stop=None)\ + --> new sorted list"); static PyObject * builtin_vars(PyObject *self, PyObject *args)