summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/lib/libitertools.tex23
-rw-r--r--Lib/test/test_itertools.py74
-rw-r--r--Modules/itertoolsmodule.c46
3 files changed, 117 insertions, 26 deletions
diff --git a/Doc/lib/libitertools.tex b/Doc/lib/libitertools.tex
index 93116ea..d0e1269 100644
--- a/Doc/lib/libitertools.tex
+++ b/Doc/lib/libitertools.tex
@@ -197,9 +197,9 @@ by functions or loops that truncate the stream.
If \var{start} is non-zero, then elements from the iterable are skipped
until start is reached. Afterward, elements are returned consecutively
unless \var{step} is set higher than one which results in items being
- skipped. If \var{stop} is specified, then iteration stops at the
- specified element position; otherwise, it continues indefinitely or
- until the iterable is exhausted. Unlike regular slicing,
+ skipped. If \var{stop} is not specified or is \code{None}, then iteration
+ continues indefinitely; otherwise, it stops at the specified position.
+ Unlike regular slicing,
\function{islice()} does not support negative values for \var{start},
\var{stop}, or \var{step}. Can be used to extract related fields
from data where the internal structure has been flattened (for
@@ -208,17 +208,20 @@ by functions or loops that truncate the stream.
\begin{verbatim}
def islice(iterable, *args):
- s = slice(*args)
- next = s.start or 0
- stop = s.stop
- step = s.step or 1
+ if args:
+ s = slice(*args)
+ next = s.start or 0
+ stop = s.stop
+ step = s.step or 1
+ else:
+ next, stop, step = 0, None, 1
for cnt, element in enumerate(iterable):
if cnt < next:
continue
- if cnt >= stop:
+ if stop is not None and cnt >= stop:
break
yield element
- next += step
+ next += step
\end{verbatim}
\end{funcdesc}
@@ -360,7 +363,7 @@ from building blocks.
>>> def pairwise(seq):
... "s -> (s0,s1), (s1,s2), (s2, s3), ..."
-... return izip(seq, islice(seq,1,len(seq)))
+... return izip(seq, islice(seq,1,None))
>>> def padnone(seq):
... "Returns the sequence elements and then returns None indefinitely"
diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index 2a60959..d0b1ce8 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -77,12 +77,23 @@ class TestBasicOps(unittest.TestCase):
]:
self.assertEqual(list(islice(xrange(100), *args)), range(*tgtargs))
- self.assertRaises(TypeError, islice, xrange(10))
+ # Test stop=None
+ self.assertEqual(list(islice(xrange(10))), range(10))
+ self.assertEqual(list(islice(xrange(10), None)), range(10))
+ self.assertEqual(list(islice(xrange(10), 2, None)), range(2, 10))
+ self.assertEqual(list(islice(xrange(10), 1, None, 2)), range(1, 10, 2))
+
+ # Test invalid arguments
self.assertRaises(TypeError, islice, xrange(10), 1, 2, 3, 4)
self.assertRaises(ValueError, islice, xrange(10), -5, 10, 1)
self.assertRaises(ValueError, islice, xrange(10), 1, -5, -1)
self.assertRaises(ValueError, islice, xrange(10), 1, 10, -1)
self.assertRaises(ValueError, islice, xrange(10), 1, 10, 0)
+ self.assertRaises(ValueError, islice, xrange(10), 'a')
+ self.assertRaises(ValueError, islice, xrange(10), 'a', 1)
+ self.assertRaises(ValueError, islice, xrange(10), 1, 'a')
+ self.assertRaises(ValueError, islice, xrange(10), 'a', 1, 1)
+ self.assertRaises(ValueError, islice, xrange(10), 1, 'a', 1)
self.assertEqual(len(list(islice(count(), 1, 10, sys.maxint))), 1)
def test_takewhile(self):
@@ -155,16 +166,69 @@ Samuele
... "s -> (s0,s1), (s1,s2), (s2, s3), ..."
... return izip(seq, islice(seq,1,len(seq)))
+>>> def padnone(seq):
+... "Returns the sequence elements and then returns None indefinitely"
+... return chain(seq, repeat(None))
+
+>>> def ncycles(seq, n):
+... "Returns the sequence elements n times"
+... return chain(*repeat(seq, n))
+
+>>> def dotproduct(vec1, vec2):
+... return sum(imap(operator.mul, vec1, vec2))
+
+
+This is not part of the examples but it tests to make sure the definitions
+perform as purported.
+
+>>> list(enumerate('abc'))
+[(0, 'a'), (1, 'b'), (2, 'c')]
+
+>>> list(islice(tabulate(lambda x: 2*x), 4))
+[0, 2, 4, 6]
+
+>>> nth('abcde', 3)
+['d']
+
+>>> all(lambda x: x%2==0, [2, 4, 6, 8])
+True
+
+>>> all(lambda x: x%2==0, [2, 3, 6, 8])
+False
+
+>>> some(lambda x: x%2==0, [2, 4, 6, 8])
+True
+
+>>> some(lambda x: x%2==0, [1, 3, 5, 9])
+False
+
+>>> no(lambda x: x%2==0, [1, 3, 5, 9])
+True
+
+>>> no(lambda x: x%2==0, [1, 2, 5, 9])
+False
+
+>>> list(pairwise('abc'))
+[('a', 'b'), ('b', 'c')]
+
+>>> list(islice(padnone('abc'), 0, 6))
+['a', 'b', 'c', None, None, None]
+
+>>> list(ncycles('abc', 3))
+['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
+
+>>> dotproduct([1,2,3], [4,5,6])
+32
+
+
"""
__test__ = {'libreftest' : libreftest}
def test_main(verbose=None):
- import test_itertools
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestBasicOps))
test_support.run_suite(suite)
- test_support.run_doctest(test_itertools, verbose)
# verify reference counting
import sys
@@ -175,5 +239,9 @@ def test_main(verbose=None):
counts.append(sys.gettotalrefcount()-i)
print counts
+ # doctest the examples in the library reference
+ import doctest
+ doctest.testmod(sys.modules[__name__])
+
if __name__ == "__main__":
test_main(verbose=True)
diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c
index 35fa1d0..f05ebd6 100644
--- a/Modules/itertoolsmodule.c
+++ b/Modules/itertoolsmodule.c
@@ -471,27 +471,47 @@ static PyObject *
islice_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *seq;
- long a1=0, a2=0, a3=0, start=0, stop=0, step=1;
- PyObject *it;
+ long start=0, stop=-1, step=1;
+ PyObject *it, *a1=NULL, *a2=NULL;
int numargs;
isliceobject *lz;
numargs = PyTuple_Size(args);
- if (!PyArg_ParseTuple(args, "Ol|ll:islice", &seq, &a1, &a2, &a3))
+ if (!PyArg_ParseTuple(args, "O|OOl:islice", &seq, &a1, &a2, &step))
return NULL;
if (numargs == 2) {
- stop = a1;
- } else if (numargs == 3) {
- start = a1;
- stop = a2;
- } else {
- start = a1;
- stop = a2;
- step = a3;
+ if (a1 != Py_None) {
+ stop = PyInt_AsLong(a1);
+ if (stop == -1) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ PyErr_SetString(PyExc_ValueError,
+ "Stop argument must be an integer or None.");
+ return NULL;
+ }
+ }
+ } else if (numargs == 3 || numargs == 4) {
+ start = PyInt_AsLong(a1);
+ if (start == -1 && PyErr_Occurred()) {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_ValueError,
+ "Start argument must be an integer.");
+ return NULL;
+ }
+ if (a2 != Py_None) {
+ stop = PyInt_AsLong(a2);
+ if (stop == -1) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ PyErr_SetString(PyExc_ValueError,
+ "Stop argument must be an integer or None.");
+ return NULL;
+ }
+ }
}
- if (start<0 || stop<0) {
+ if (start<0 || stop<-1) {
PyErr_SetString(PyExc_ValueError,
"Indices for islice() must be positive.");
return NULL;
@@ -554,7 +574,7 @@ islice_next(isliceobject *lz)
Py_DECREF(item);
lz->cnt++;
}
- if (lz->cnt >= lz->stop)
+ if (lz->stop != -1 && lz->cnt >= lz->stop)
return NULL;
assert(PyIter_Check(it));
item = (*it->ob_type->tp_iternext)(it);