Mondo changes to the iterator stuff, without changing how Python code
sees it (test_iter.py is unchanged).
- Added a tp_iternext slot, which calls the iterator's next() method;
this is much faster for built-in iterators over built-in types
such as lists and dicts, speeding up pybench's ForLoop with about
25% compared to Python 2.1. (Now there's a good argument for
iterators. ;-)
- Renamed the built-in sequence iterator SeqIter, affecting the C API
functions for it. (This frees up the PyIter prefix for generic
iterator operations.)
- Added PyIter_Check(obj), which checks that obj's type has a
tp_iternext slot and that the proper feature flag is set.
- Added PyIter_Next(obj) which calls the tp_iternext slot. It has a
somewhat complex return condition due to the need for speed: when it
returns NULL, it may not have set an exception condition, meaning
the iterator is exhausted; when the exception StopIteration is set
(or a derived exception class), it means the same thing; any other
exception means some other error occurred.
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 8a6df76..f656747 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -1748,10 +1748,32 @@
f = t->tp_iter;
if (f == NULL) {
if (PySequence_Check(o))
- return PyIter_New(o);
+ return PySeqIter_New(o);
PyErr_SetString(PyExc_TypeError, "iter() of non-sequence");
return NULL;
}
- else
- return (*f)(o);
+ else {
+ PyObject *res = (*f)(o);
+ if (res != NULL && !PyIter_Check(res)) {
+ PyErr_Format(PyExc_TypeError,
+ "iter() returned non-iterator "
+ "of type '%.100s'",
+ res->ob_type->tp_name);
+ Py_DECREF(res);
+ res = NULL;
+ }
+ return res;
+ }
+}
+
+PyObject *
+PyIter_Next(PyObject *iter)
+{
+ if (!PyIter_Check(iter)) {
+ PyErr_Format(PyExc_TypeError,
+ "'%.100s' object is not an iterator",
+ iter->ob_type->tp_name);
+ return NULL;
+ }
+ return (*iter->ob_type->tp_iternext)(iter);
}