[Python-checkins] r68561 - in python/branches/py3k: Include/abstract.h Include/object.h Lib/test/crashers/iter.py Lib/test/test_iter.py Misc/NEWS Modules/itertoolsmodule.c Objects/abstract.c Objects/object.c Objects/typeobject.c Python/bltinmodule.c

amaury.forgeotdarc python-checkins at python.org
Tue Jan 13 00:58:21 CET 2009


Author: amaury.forgeotdarc
Date: Tue Jan 13 00:58:21 2009
New Revision: 68561

Log:
Merged revisions 68560 via svnmerge from 
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r68560 | amaury.forgeotdarc | 2009-01-13 00:36:55 +0100 (mar., 13 janv. 2009) | 6 lines
  
  #3720: Interpreter crashes when an evil iterator removes its own next function.
  
  Now the slot is filled with a function that always raises.
  
  Will not backport: extensions compiled with 2.6.x would not run on 2.6.0.
........


Removed:
   python/branches/py3k/Lib/test/crashers/iter.py
Modified:
   python/branches/py3k/   (props changed)
   python/branches/py3k/Include/abstract.h
   python/branches/py3k/Include/object.h
   python/branches/py3k/Lib/test/test_iter.py
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Modules/itertoolsmodule.c
   python/branches/py3k/Objects/abstract.c
   python/branches/py3k/Objects/object.c
   python/branches/py3k/Objects/typeobject.c
   python/branches/py3k/Python/bltinmodule.c

Modified: python/branches/py3k/Include/abstract.h
==============================================================================
--- python/branches/py3k/Include/abstract.h	(original)
+++ python/branches/py3k/Include/abstract.h	Tue Jan 13 00:58:21 2009
@@ -612,7 +612,8 @@
 	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,

Modified: python/branches/py3k/Include/object.h
==============================================================================
--- python/branches/py3k/Include/object.h	(original)
+++ python/branches/py3k/Include/object.h	Tue Jan 13 00:58:21 2009
@@ -438,6 +438,7 @@
 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 *);

Deleted: python/branches/py3k/Lib/test/crashers/iter.py
==============================================================================
--- python/branches/py3k/Lib/test/crashers/iter.py	Tue Jan 13 00:58:21 2009
+++ (empty file)
@@ -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()

Modified: python/branches/py3k/Lib/test/test_iter.py
==============================================================================
--- python/branches/py3k/Lib/test/test_iter.py	(original)
+++ python/branches/py3k/Lib/test/test_iter.py	Tue Jan 13 00:58:21 2009
@@ -120,6 +120,13 @@
     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 @@
         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)

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Tue Jan 13 00:58:21 2009
@@ -12,6 +12,9 @@
 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.

Modified: python/branches/py3k/Modules/itertoolsmodule.c
==============================================================================
--- python/branches/py3k/Modules/itertoolsmodule.c	(original)
+++ python/branches/py3k/Modules/itertoolsmodule.c	Tue Jan 13 00:58:21 2009
@@ -886,7 +886,6 @@
 	long ok;
 	PyObject *(*iternext)(PyObject *);
 
-	assert(PyIter_Check(it));
 	iternext = *Py_TYPE(it)->tp_iternext;
 	for (;;) {
 		item = iternext(it);
@@ -1031,7 +1030,6 @@
 	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 @@
 	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 @@
 	}
 	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 @@
 	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 @@
 	long ok;
 	PyObject *(*iternext)(PyObject *);
 
-	assert(PyIter_Check(it));
 	iternext = *Py_TYPE(it)->tp_iternext;
 	for (;;) {
 		item = iternext(it);
@@ -2888,7 +2882,6 @@
                                 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 @@
                                 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;      

Modified: python/branches/py3k/Objects/abstract.c
==============================================================================
--- python/branches/py3k/Objects/abstract.c	(original)
+++ python/branches/py3k/Objects/abstract.c	Tue Jan 13 00:58:21 2009
@@ -2736,7 +2736,6 @@
 PyIter_Next(PyObject *iter)
 {
 	PyObject *result;
-	assert(PyIter_Check(iter));
 	result = (*iter->ob_type->tp_iternext)(iter);
 	if (result == NULL &&
 	    PyErr_Occurred() &&

Modified: python/branches/py3k/Objects/object.c
==============================================================================
--- python/branches/py3k/Objects/object.c	(original)
+++ python/branches/py3k/Objects/object.c	Tue Jan 13 00:58:21 2009
@@ -1020,6 +1020,20 @@
 	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 *

Modified: python/branches/py3k/Objects/typeobject.c
==============================================================================
--- python/branches/py3k/Objects/typeobject.c	(original)
+++ python/branches/py3k/Objects/typeobject.c	Tue Jan 13 00:58:21 2009
@@ -5630,8 +5630,12 @@
 	}
 	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)

Modified: python/branches/py3k/Python/bltinmodule.c
==============================================================================
--- python/branches/py3k/Python/bltinmodule.c	(original)
+++ python/branches/py3k/Python/bltinmodule.c	Tue Jan 13 00:58:21 2009
@@ -375,7 +375,6 @@
 	long ok;
 	PyObject *(*iternext)(PyObject *);
 
-	assert(PyIter_Check(it));
 	iternext = *Py_TYPE(it)->tp_iternext;
 	for (;;) {
 		item = iternext(it);
@@ -2144,7 +2143,6 @@
 		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 @@
 			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);


More information about the Python-checkins mailing list