Generalize filter(f, seq) to work with iterators.  This also generalizes
filter() to no longer insist that len(seq) be defined.
NEEDS DOC CHANGES.
diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py
index 5b9bf65..952ab66 100644
--- a/Lib/test/test_iter.py
+++ b/Lib/test/test_iter.py
@@ -275,4 +275,48 @@
             except OSError:
                 pass
 
+    # Test filter()'s use of iterators.
+    def test_builtin_filter(self):
+        self.assertEqual(filter(None, SequenceClass(5)), range(1, 5))
+        self.assertEqual(filter(None, SequenceClass(0)), [])
+        self.assertEqual(filter(None, ()), ())
+        self.assertEqual(filter(None, "abc"), "abc")
+
+        d = {"one": 1, "two": 2, "three": 3}
+        self.assertEqual(filter(None, d), d.keys())
+
+        self.assertRaises(TypeError, filter, None, list)
+        self.assertRaises(TypeError, filter, None, 42)
+
+        class Boolean:
+            def __init__(self, truth):
+                self.truth = truth
+            def __nonzero__(self):
+                return self.truth
+        True = Boolean(1)
+        False = Boolean(0)
+
+        class Seq:
+            def __init__(self, *args):
+                self.vals = args
+            def __iter__(self):
+                class SeqIter:
+                    def __init__(self, vals):
+                        self.vals = vals
+                        self.i = 0
+                    def __iter__(self):
+                        return self
+                    def next(self):
+                        i = self.i
+                        self.i = i + 1
+                        if i < len(self.vals):
+                            return self.vals[i]
+                        else:
+                            raise StopIteration
+                return SeqIter(self.vals)
+
+        seq = Seq(*([True, False] * 25))
+        self.assertEqual(filter(lambda x: not x, seq), [False]*25)
+        self.assertEqual(filter(lambda x: not x, iter(seq)), [False]*25)
+
 run_unittest(TestCase)
diff --git a/Misc/NEWS b/Misc/NEWS
index f121bac..bbd2ac3 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -17,7 +17,8 @@
 
 - The following functions were generalized to work nicely with iterator
   arguments:
-  list()
+    filter()
+    list()
 
 
 What's New in Python 2.1 (final)?
@@ -236,7 +237,7 @@
 - IDLE: syntax warnings in interactive mode are changed into errors.
 
 - Some improvements to Tools/webchecker (ignore some more URL types,
-  follow some more links). 
+  follow some more links).
 
 - Brought the Tools/compiler package up to date.
 
@@ -324,23 +325,23 @@
   in Flags and take an extra argument, a PyCompilerFlags *; examples:
   PyRun_AnyFileExFlags(), PyRun_InteractiveLoopFlags().  These
   variants may be removed in Python 2.2, when nested scopes are
-  mandatory. 
+  mandatory.
 
 Distutils
 
 - the sdist command now writes a PKG-INFO file, as described in PEP 241,
   into the release tree.
 
-- several enhancements to the bdist_wininst command from Thomas Heller 
+- several enhancements to the bdist_wininst command from Thomas Heller
   (an uninstaller, more customization of the installer's display)
 
 - from Jack Jansen: added Mac-specific code to generate a dialog for
   users to specify the command-line (because providing a command-line with
-  MacPython is awkward).  Jack also made various fixes for the Mac 
+  MacPython is awkward).  Jack also made various fixes for the Mac
   and the Metrowerks compiler.
-  
-- added 'platforms' and 'keywords' to the set of metadata that can be 
-  specified for a distribution.  
+
+- added 'platforms' and 'keywords' to the set of metadata that can be
+  specified for a distribution.
 
 - applied patches from Jason Tishler to make the compiler class work with
   Cygwin.
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 8572cc8..1051374 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -162,53 +162,65 @@
 static PyObject *
 builtin_filter(PyObject *self, PyObject *args)
 {
-	PyObject *func, *seq, *result;
-	PySequenceMethods *sqf;
-	int len;
+	PyObject *func, *seq, *result, *it;
+	int len;   /* guess for result list size */
 	register int i, j;
 
 	if (!PyArg_ParseTuple(args, "OO:filter", &func, &seq))
 		return NULL;
 
-	if (PyString_Check(seq)) {
-		PyObject *r = filterstring(func, seq);
-		return r;
+	/* Strings and tuples return a result of the same type. */
+	if (PyString_Check(seq))
+		return filterstring(func, seq);
+	if (PyTuple_Check(seq))
+		return filtertuple(func, seq);
+
+	/* Get iterator. */
+	it = PyObject_GetIter(seq);
+	if (it == NULL)
+		return NULL;
+
+	/* Guess a result list size. */
+	len = -1;   /* unknown */
+	if (PySequence_Check(seq) &&
+	    seq->ob_type->tp_as_sequence->sq_length) {
+		len = PySequence_Size(seq);
+		if (len < 0)
+			PyErr_Clear();
 	}
+	if (len < 0)
+		len = 8;  /* arbitrary */
 
-	if (PyTuple_Check(seq)) {
-		PyObject *r = filtertuple(func, seq);
-		return r;
-	}
-
-	sqf = seq->ob_type->tp_as_sequence;
-	if (sqf == NULL || sqf->sq_length == NULL || sqf->sq_item == NULL) {
-		PyErr_SetString(PyExc_TypeError,
-			   "filter() arg 2 must be a sequence");
-		goto Fail_2;
-	}
-
-	if ((len = (*sqf->sq_length)(seq)) < 0)
-		goto Fail_2;
-
+	/* Get a result list. */
 	if (PyList_Check(seq) && seq->ob_refcnt == 1) {
+		/* Eww - can modify the list in-place. */
 		Py_INCREF(seq);
 		result = seq;
 	}
 	else {
-		if ((result = PyList_New(len)) == NULL)
-			goto Fail_2;
+		result = PyList_New(len);
+		if (result == NULL)
+			goto Fail_it;
 	}
 
+	/* Build the result list. */
 	for (i = j = 0; ; ++i) {
 		PyObject *item, *good;
 		int ok;
 
-		if ((item = (*sqf->sq_item)(seq, i)) == NULL) {
-			if (PyErr_ExceptionMatches(PyExc_IndexError)) {
-				PyErr_Clear();
-				break;
+		item = PyIter_Next(it);
+		if (item == NULL) {
+			/* We're out of here in any case, but if this is a
+			 * StopIteration exception it's expected, but if
+			 * any other kind of exception it's an error.
+			 */
+			if (PyErr_Occurred()) {
+				if (PyErr_ExceptionMatches(PyExc_StopIteration))
+					PyErr_Clear();
+				else
+					goto Fail_result_it;
 			}
-			goto Fail_1;
+			break;
 		}
 
 		if (func == Py_None) {
@@ -217,43 +229,45 @@
 		}
 		else {
 			PyObject *arg = Py_BuildValue("(O)", item);
-			if (arg == NULL)
-				goto Fail_1;
+			if (arg == NULL) {
+				Py_DECREF(item);
+				goto Fail_result_it;
+			}
 			good = PyEval_CallObject(func, arg);
 			Py_DECREF(arg);
 			if (good == NULL) {
 				Py_DECREF(item);
-				goto Fail_1;
+				goto Fail_result_it;
 			}
 		}
 		ok = PyObject_IsTrue(good);
 		Py_DECREF(good);
 		if (ok) {
-			if (j < len) {
-				if (PyList_SetItem(result, j++, item) < 0)
-					goto Fail_1;
-			}
+			if (j < len)
+				PyList_SET_ITEM(result, j, item);
 			else {
 				int status = PyList_Append(result, item);
-				j++;
 				Py_DECREF(item);
 				if (status < 0)
-					goto Fail_1;
+					goto Fail_result_it;
 			}
-		} else {
-			Py_DECREF(item);
+			++j;
 		}
+		else
+			Py_DECREF(item);
 	}
 
 
+	/* Cut back result list if len is too big. */
 	if (j < len && PyList_SetSlice(result, j, len, NULL) < 0)
-		goto Fail_1;
+		goto Fail_result_it;
 
 	return result;
 
-Fail_1:
+Fail_result_it:
 	Py_DECREF(result);
-Fail_2:
+Fail_it:
+	Py_DECREF(it);
 	return NULL;
 }