SF bug #730685:  itertools.islice stop argument is not optional

* itertools.islice() stop argument did not perform as documented.
* beefed-up test suite
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 @@
   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 @@
 
   \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 @@
 
 >>> 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 @@
                 ]:
             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 @@
 ...     "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 @@
             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 @@
 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 @@
 		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);