summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/abstract.h3
-rw-r--r--Include/object.h1
-rw-r--r--Lib/test/crashers/iter.py53
-rw-r--r--Lib/test/test_iter.py22
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/itertoolsmodule.c8
-rw-r--r--Objects/abstract.c1
-rw-r--r--Objects/object.c14
-rw-r--r--Objects/typeobject.c6
-rw-r--r--Python/bltinmodule.c3
10 files changed, 47 insertions, 67 deletions
diff --git a/Include/abstract.h b/Include/abstract.h
index 4fb16d0..b953308 100644
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -612,7 +612,8 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
is an iterator, this returns itself. */
#define PyIter_Check(obj) \
- ((obj)->ob_type->tp_iternext != NULL)
+ ((obj)->ob_type->tp_iternext != NULL && \
+ (obj)->ob_type->tp_iternext != &_PyObject_NextNotImplemented)
PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *);
/* Takes an iterator object and calls its tp_iternext slot,
diff --git a/Include/object.h b/Include/object.h
index 0d20ecc..f3fdbda 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -438,6 +438,7 @@ PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *);
PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *);
PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *);
+PyAPI_FUNC(PyObject *) _PyObject_NextNotImplemented(PyObject *);
PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *,
PyObject *, PyObject *);
diff --git a/Lib/test/crashers/iter.py b/Lib/test/crashers/iter.py
deleted file mode 100644
index 0d8540e..0000000
--- a/Lib/test/crashers/iter.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Calls to PyIter_Next, or direct calls to tp_iternext, on an object
-# which might no longer be an iterable because its 'next' method was
-# removed. These are all variants of Issue3720.
-
-"""
-Run this script with an argument between 1 and <N> to test for
-different crashes.
-"""
-N = 8
-
-import sys
-
-class Foo(object):
- def __iter__(self):
- return self
- def next(self):
- del Foo.next
- return (1, 2)
-
-def case1():
- list(enumerate(Foo()))
-
-def case2():
- x, y = Foo()
-
-def case3():
- filter(None, Foo())
-
-def case4():
- map(None, Foo(), Foo())
-
-def case5():
- max(Foo())
-
-def case6():
- sum(Foo(), ())
-
-def case7():
- dict(Foo())
-
-def case8():
- sys.stdout.writelines(Foo())
-
-# etc...
-
-
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print(__doc__.replace('<N>', str(N)))
- else:
- n = int(sys.argv[1])
- func = globals()['case%d' % n]
- func()
diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py
index eba9728..7788502 100644
--- a/Lib/test/test_iter.py
+++ b/Lib/test/test_iter.py
@@ -120,6 +120,13 @@ class TestCase(unittest.TestCase):
def test_seq_class_iter(self):
self.check_iterator(iter(SequenceClass(10)), list(range(10)))
+ # Test a new_style class with __iter__ but no next() method
+ def test_new_style_iter_class(self):
+ class IterClass(object):
+ def __iter__(self):
+ return self
+ self.assertRaises(TypeError, iter, IterClass())
+
# Test two-argument iter() with callable instance
def test_iter_callable(self):
class C:
@@ -853,6 +860,21 @@ class TestCase(unittest.TestCase):
self.assertEqual(list(b), list(zip(range(5), range(5))))
self.assertEqual(list(b), [])
+ def test_3720(self):
+ # Avoid a crash, when an iterator deletes its next() method.
+ class BadIterator(object):
+ def __iter__(self):
+ return self
+ def __next__(self):
+ del BadIterator.__next__
+ return 1
+
+ try:
+ for i in BadIterator() :
+ pass
+ except TypeError:
+ pass
+
def test_main():
run_unittest(TestCase)
diff --git a/Misc/NEWS b/Misc/NEWS
index 9e7afee..6695a24 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 3.1 alpha 0
Core and Builtins
-----------------
+- Issue #3720: Fix a crash when an iterator modifies its class and removes its
+ __next__ method.
+
- Issue #4910: Builtin int() function and PyNumber_Long/PyNumber_Int API
function no longer attempt to call the __long__ slot to convert an object
to an integer. Only the __int__ and __trunc__ slots are examined.
diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c
index 8125dcb..dcf6aba 100644
--- a/Modules/itertoolsmodule.c
+++ b/Modules/itertoolsmodule.c
@@ -886,7 +886,6 @@ dropwhile_next(dropwhileobject *lz)
long ok;
PyObject *(*iternext)(PyObject *);
- assert(PyIter_Check(it));
iternext = *Py_TYPE(it)->tp_iternext;
for (;;) {
item = iternext(it);
@@ -1031,7 +1030,6 @@ takewhile_next(takewhileobject *lz)
if (lz->stop == 1)
return NULL;
- assert(PyIter_Check(it));
item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL)
return NULL;
@@ -1218,7 +1216,6 @@ islice_next(isliceobject *lz)
Py_ssize_t oldnext;
PyObject *(*iternext)(PyObject *);
- assert(PyIter_Check(it));
iternext = *Py_TYPE(it)->tp_iternext;
while (lz->cnt < lz->next) {
item = iternext(it);
@@ -1229,7 +1226,6 @@ islice_next(isliceobject *lz)
}
if (lz->stop != -1 && lz->cnt >= lz->stop)
return NULL;
- assert(PyIter_Check(it));
item = iternext(it);
if (item == NULL)
return NULL;
@@ -1361,7 +1357,6 @@ starmap_next(starmapobject *lz)
PyObject *result;
PyObject *it = lz->it;
- assert(PyIter_Check(it));
args = (*Py_TYPE(it)->tp_iternext)(it);
if (args == NULL)
return NULL;
@@ -2403,7 +2398,6 @@ filterfalse_next(filterfalseobject *lz)
long ok;
PyObject *(*iternext)(PyObject *);
- assert(PyIter_Check(it));
iternext = *Py_TYPE(it)->tp_iternext;
for (;;) {
item = iternext(it);
@@ -2888,7 +2882,6 @@ zip_longest_next(ziplongestobject *lz)
Py_INCREF(lz->fillvalue);
item = lz->fillvalue;
} else {
- assert(PyIter_Check(it));
item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) {
lz->numactive -= 1;
@@ -2917,7 +2910,6 @@ zip_longest_next(ziplongestobject *lz)
Py_INCREF(lz->fillvalue);
item = lz->fillvalue;
} else {
- assert(PyIter_Check(it));
item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) {
lz->numactive -= 1;
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 0a0333c..1f988ec 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2736,7 +2736,6 @@ PyObject *
PyIter_Next(PyObject *iter)
{
PyObject *result;
- assert(PyIter_Check(iter));
result = (*iter->ob_type->tp_iternext)(iter);
if (result == NULL &&
PyErr_Occurred() &&
diff --git a/Objects/object.c b/Objects/object.c
index 15d26a7..00657de 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1020,6 +1020,20 @@ PyObject_SelfIter(PyObject *obj)
return obj;
}
+/* Helper used when the __next__ method is removed from a type:
+ tp_iternext is never NULL and can be safely called without checking
+ on every iteration.
+ */
+
+PyObject *
+_PyObject_NextNotImplemented(PyObject *self)
+{
+ PyErr_Format(PyExc_TypeError,
+ "'%.200s' object is not iterable",
+ Py_TYPE(self)->tp_name);
+ return NULL;
+}
+
/* Generic GetAttr functions - put these in your tp_[gs]etattro slot */
PyObject *
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 8f2e01e..8921b5f 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -5630,8 +5630,12 @@ update_one_slot(PyTypeObject *type, slotdef *p)
}
do {
descr = _PyType_Lookup(type, p->name_strobj);
- if (descr == NULL)
+ if (descr == NULL) {
+ if (ptr == (void**)&type->tp_iternext) {
+ specific = _PyObject_NextNotImplemented;
+ }
continue;
+ }
if (Py_TYPE(descr) == &PyWrapperDescr_Type) {
void **tptr = resolve_slotdups(type, p->name_strobj);
if (tptr == NULL || tptr == ptr)
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 4e9ec85..f87fdd2 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -375,7 +375,6 @@ filter_next(filterobject *lz)
long ok;
PyObject *(*iternext)(PyObject *);
- assert(PyIter_Check(it));
iternext = *Py_TYPE(it)->tp_iternext;
for (;;) {
item = iternext(it);
@@ -2144,7 +2143,6 @@ zip_next(zipobject *lz)
Py_INCREF(result);
for (i=0 ; i < tuplesize ; i++) {
it = PyTuple_GET_ITEM(lz->ittuple, i);
- assert(PyIter_Check(it));
item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) {
Py_DECREF(result);
@@ -2160,7 +2158,6 @@ zip_next(zipobject *lz)
return NULL;
for (i=0 ; i < tuplesize ; i++) {
it = PyTuple_GET_ITEM(lz->ittuple, i);
- assert(PyIter_Check(it));
item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) {
Py_DECREF(result);