Index: Modules/itertoolsmodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/itertoolsmodule.c,v retrieving revision 1.40 diff -c -w -r1.40 itertoolsmodule.c *** Modules/itertoolsmodule.c 5 Dec 2004 09:25:51 -0000 1.40 --- Modules/itertoolsmodule.c 26 Jan 2005 12:59:17 -0000 *************** *** 606,611 **** --- 606,612 ---- PyObject *it; PyObject *saved; int firstpass; + PyObject *cycles; } cycleobject; static PyTypeObject cycle_type; *************** *** 616,626 **** PyObject *it; PyObject *iterable; PyObject *saved; cycleobject *lz; ! if (!PyArg_UnpackTuple(args, "cycle", 1, 1, &iterable)) return NULL; /* Get iterator. */ it = PyObject_GetIter(iterable); if (it == NULL) --- 617,634 ---- PyObject *it; PyObject *iterable; PyObject *saved; + PyObject *cycles = NULL; + PyObject *zero = NULL; cycleobject *lz; ! if (!PyArg_UnpackTuple(args, "cycle", 1, 2, &iterable, &cycles)) return NULL; + if (cycles && !PyNumber_Check(cycles)) { + PyErr_SetString(PyExc_TypeError, "number of cycles must be a number"); + return NULL; + } + /* Get iterator. */ it = PyObject_GetIter(iterable); if (it == NULL) *************** *** 632,647 **** --- 640,672 ---- return NULL; } + zero = PyInt_FromLong(0); + if (zero == NULL) { + Py_DECREF(it); + Py_DECREF(saved); + return NULL; + } + + if (PyObject_RichCompareBool(cycles, zero, Py_LT)) { + cycles = zero; + } else { + Py_DECREF(zero); + zero = NULL; + Py_INCREF(cycles); + } + /* create cycleobject structure */ lz = (cycleobject *)type->tp_alloc(type, 0); if (lz == NULL) { Py_DECREF(it); Py_DECREF(saved); + Py_DECREF(cycles); return NULL; } lz->it = it; lz->saved = saved; lz->firstpass = 0; + lz->cycles = cycles; return (PyObject *)lz; } *************** *** 652,657 **** --- 677,683 ---- PyObject_GC_UnTrack(lz); Py_XDECREF(lz->saved); Py_XDECREF(lz->it); + Py_XDECREF(lz->cycles); lz->ob_type->tp_free(lz); } *************** *** 660,665 **** --- 686,692 ---- { Py_VISIT(lz->it); Py_VISIT(lz->saved); + Py_VISIT(lz->cycles); return 0; } *************** *** 671,676 **** --- 698,706 ---- PyObject *tmp; while (1) { + if (lz->cycles && PyObject_Not(lz->cycles)) { + return NULL; + } item = PyIter_Next(lz->it); if (item != NULL) { if (!lz->firstpass) *************** *** 692,705 **** lz->it = it; lz->firstpass = 1; Py_DECREF(tmp); } } PyDoc_STRVAR(cycle_doc, ! "cycle(iterable) --> cycle object\n\ \n\ Return elements from the iterable until it is exhausted.\n\ ! Then repeat the sequence indefinitely."); static PyTypeObject cycle_type = { PyObject_HEAD_INIT(NULL) --- 722,749 ---- lz->it = it; lz->firstpass = 1; Py_DECREF(tmp); + if (lz->cycles) { + PyObject *one = PyInt_FromLong(1); + if (one == NULL) { + return NULL; + } + tmp = lz->cycles; + lz->cycles = PyNumber_Subtract(lz->cycles, one); + Py_DECREF(one); + if (lz->cycles == NULL) { + lz->cycles = tmp; + return NULL; + } + Py_DECREF(tmp); + } } } PyDoc_STRVAR(cycle_doc, ! "cycle(iterable [,times]) --> cycle object\n\ \n\ Return elements from the iterable until it is exhausted.\n\ ! Then repeat the sequence given number of times, or indefinitely."); static PyTypeObject cycle_type = { PyObject_HEAD_INIT(NULL) Index: Objects/abstract.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/abstract.c,v retrieving revision 2.135 diff -c -w -r2.135 abstract.c *** Objects/abstract.c 18 Dec 2004 19:00:59 -0000 2.135 --- Objects/abstract.c 26 Jan 2005 12:59:18 -0000 *************** *** 106,111 **** --- 106,126 ---- return type_error("sequence index must be integer"); } + if (PyIter_Check(o)) { + return PyIter_GetItem(o, key); + } + + if (o->ob_type->tp_iter) { + PyObject *result = NULL; + PyObject *iterator = PyObject_GetIter(o); + if (iterator == NULL) { + return NULL; + } + result = PyIter_GetItem(o, key); + Py_DECREF(iterator); + return result; + } + return type_error("unsubscriptable object"); } *************** *** 616,621 **** --- 631,649 ---- } if (result == Py_NotImplemented) { Py_DECREF(result); + if (PyIter_Check(v)) { + return PyIter_Concat(v, w); + } + + if (v->ob_type->tp_iter) { + PyObject *v_itr = PyObject_GetIter(v); + if (v_itr == NULL) { + return NULL; + } + result = PyIter_Concat(v_itr, w); + Py_DECREF(v_itr); + return result; + } return binop_type_error(v, w, "+"); } } *************** *** 680,685 **** --- 708,737 ---- else if (mw && mw->sq_repeat) { return sequence_repeat(mw->sq_repeat, w, v); } + if (PyIter_Check(v)) { + return sequence_repeat(&PyIter_Repeat, v, w); + } + if (PyIter_Check(w)) { + return sequence_repeat(&PyIter_Repeat, w, v); + } + if (v->ob_type->tp_iter) { + PyObject *v_itr = PyObject_GetIter(v); + if (v_itr == NULL) { + return NULL; + } + result = sequence_repeat(&PyIter_Repeat, v_itr, w); + Py_DECREF(v_itr); + return result; + } + if (w->ob_type->tp_iter) { + PyObject *w_itr = PyObject_GetIter(w); + if (w_itr == NULL) { + return NULL; + } + result = sequence_repeat(&PyIter_Repeat, w_itr, v); + Py_DECREF(w_itr); + return result; + } result = binop_type_error(v, w, "*"); } return result; *************** *** 2219,2221 **** --- 2271,2395 ---- PyErr_Clear(); return result; } + + /* Iterator slicing + * If an error occurs, return NULL. PyErr_Occurred() will be true. + * For a simple index, consumes the iterator up to that point and + * returns the item at the supplied index. PyErr_Occurred() will be false. + * For a slice index, constructs an itertools.islice object based on the + * iterator and returns it. PyErr_Occurred() will be false. + */ + PyObject * + PyIter_GetItem(PyObject *iter, PyObject *key) + { + if (iter == NULL || key == NULL) { + return null_error(); + } + + if (!PyIter_Check(iter)) { + return type_error("Not an iterator"); + } + + if (PySlice_Check(key)) { + PyObject * itertools = NULL; + PySliceObject *slice = (PySliceObject *) key; + PyObject * iterslice = NULL; + + itertools = PyImport_ImportModule("itertools"); + if (itertools == NULL) { + return NULL; + } + + iterslice = PyObject_CallMethod(itertools, "islice", "OOOO", + iter, slice->start, slice->stop, slice->step); + Py_DECREF(itertools); + return iterslice; + } else { + long current = 0; + long key_value = 0; + PyObject * item = NULL; + if (PyInt_Check(key)) { + key_value = PyInt_AsLong(key); + } else if (PyLong_Check(key)) { + key_value = PyLong_AsLong(key); + if (key_value == -1 && PyErr_Occurred()) { + return NULL; + } + } else { + return type_error("iterator index must be integer or slice"); + } + if (key_value < 0) { + PyErr_SetString(PyExc_IndexError, "iterator requires non-negative index"); + return NULL; + } + /* Retrieve items until we reach the desired index */ + while ((item = PyIter_Next(iter)) && (current < key_value)) { + ++current; + /* Note this decref does NOT fire for the item at the key index! */ + Py_DECREF(item); + } + if (PyErr_Occurred()) { + return NULL; + } + if (current < key_value) { + PyErr_SetString(PyExc_IndexError, "iterator exhausted before reaching index"); + return NULL; + } + return item; + } + } + + PyObject * + PyIter_Concat(PyObject *iter, PyObject *o) + { + PyObject *itertools = NULL; + PyObject *other_iter = NULL; + PyObject *result = NULL; + + if (iter == NULL || o == NULL) + return null_error(); + + if (!PyIter_Check(iter)) { + return type_error("Not an iterator"); + } + + itertools = PyImport_ImportModule("itertools"); + if (itertools == NULL) { + return NULL; + } + + other_iter = PyObject_GetIter(o); + if (other_iter == NULL) { + Py_DECREF(itertools); + return type_error("can only concatenate iterator with iterator or iterable"); + } + + result = PyObject_CallMethod(itertools, "chain", "OO", + iter, other_iter); + Py_DECREF(itertools); + Py_DECREF(other_iter); + return result; + } + + PyObject * + PyIter_Repeat(PyObject *iter, int count) + { + PyObject *itertools = NULL; + PyObject *result = NULL; + + if (iter == NULL) + return null_error(); + + if (!PyIter_Check(iter)) { + return type_error("Not an iterator"); + } + + itertools = PyImport_ImportModule("itertools"); + if (itertools == NULL) { + return NULL; + } + + result = PyObject_CallMethod(itertools, "cycle", "Oi", iter, count); + Py_DECREF(itertools); + return result; + } Index: Include/abstract.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/abstract.h,v retrieving revision 2.51 diff -c -w -r2.51 abstract.h *** Include/abstract.h 12 Mar 2004 16:38:17 -0000 2.51 --- Include/abstract.h 26 Jan 2005 12:59:18 -0000 *************** *** 523,533 **** --- 523,558 ---- (obj)->ob_type->tp_iternext != NULL) PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *); + /* Takes an iterator object and calls its tp_iternext slot, returning the next value. If the iterator is exhausted, this returns NULL without setting an exception. NULL with an exception means an error occurred. */ + PyAPI_FUNC(PyObject *) PyIter_Concat(PyObject *o1, PyObject *o2); + + /* + Return an itertools.chain object that chains o1 and o2 on success, and NULL on + failure. This is the equivalent of the Python + expression: o1+o2. */ + + PyAPI_FUNC(PyObject *) PyIter_Repeat(PyObject *o, int count); + + /* + Return an itertools.repeat object that repeats iterator o count times, + or NULL on failure. This is the equivalent of the Python + expression: o1*count. */ + + PyAPI_FUNC(PyObject *) PyIter_GetItem(PyObject *o, PyObject *key); + + /* + Return the ith element of o, or NULL on failure. This is the + equivalent of the Python expression: o[i]. + Negative indices are not allowed. + Extended slicing is supported (positive steps only) via itertools.islice + Consumes the iterator up to the given index, or the start of the slice + */ + /* Number Protocol:*/ PyAPI_FUNC(int) PyNumber_Check(PyObject *o);