[Python-checkins] r69227 - in python/trunk: Include/abstract.h Lib/test/test_iterlen.py Misc/NEWS Objects/abstract.c Objects/bytearrayobject.c Objects/listobject.c Python/bltinmodule.c

raymond.hettinger python-checkins at python.org
Mon Feb 2 22:50:13 CET 2009


Author: raymond.hettinger
Date: Mon Feb  2 22:50:13 2009
New Revision: 69227

Log:
Issue 1242657: list(obj) can swallow KeyboardInterrupt.

Modified:
   python/trunk/Include/abstract.h
   python/trunk/Lib/test/test_iterlen.py
   python/trunk/Misc/NEWS
   python/trunk/Objects/abstract.c
   python/trunk/Objects/bytearrayobject.c
   python/trunk/Objects/listobject.c
   python/trunk/Python/bltinmodule.c

Modified: python/trunk/Include/abstract.h
==============================================================================
--- python/trunk/Include/abstract.h	(original)
+++ python/trunk/Include/abstract.h	Mon Feb  2 22:50:13 2009
@@ -438,7 +438,7 @@
        /*
          Guess the size of object o using len(o) or o.__length_hint__().
          If neither of those return a non-negative value, then return the
-         default value.  This function never fails. All exceptions are cleared.
+         default value.  If one of the calls fails, this function returns -1.
        */
 
      PyAPI_FUNC(PyObject *) PyObject_GetItem(PyObject *o, PyObject *key);

Modified: python/trunk/Lib/test/test_iterlen.py
==============================================================================
--- python/trunk/Lib/test/test_iterlen.py	(original)
+++ python/trunk/Lib/test/test_iterlen.py	Mon Feb  2 22:50:13 2009
@@ -195,6 +195,36 @@
         d.extend(xrange(20))
         self.assertEqual(len(it), 0)
 
+## -- Check to make sure exceptions are not suppressed by __length_hint__()
+
+
+class BadLen(object):
+    def __iter__(self): return iter(range(10))
+    def __len__(self):
+        raise RuntimeError('hello')
+
+class BadLengthHint(object):
+    def __iter__(self): return iter(range(10))
+    def __length_hint__(self):
+        raise RuntimeError('hello')
+
+class TestLengthHintExceptions(unittest.TestCase):
+
+    def test_issue1242657(self):
+        self.assertRaises(RuntimeError, list, BadLen())
+        self.assertRaises(RuntimeError, list, BadLengthHint())
+        self.assertRaises(RuntimeError, [].extend, BadLen())
+        self.assertRaises(RuntimeError, [].extend, BadLengthHint())
+        self.assertRaises(RuntimeError, zip, BadLen())
+        self.assertRaises(RuntimeError, zip, BadLengthHint())
+        self.assertRaises(RuntimeError, filter, None, BadLen())
+        self.assertRaises(RuntimeError, filter, None, BadLengthHint())
+        self.assertRaises(RuntimeError, map, chr, BadLen())
+        self.assertRaises(RuntimeError, map, chr, BadLengthHint())
+        b = bytearray(range(10))
+        self.assertRaises(RuntimeError, b.extend, BadLen())
+        self.assertRaises(RuntimeError, b.extend, BadLengthHint())
+
 def test_main():
     unittests = [
         TestRepeat,
@@ -209,6 +239,7 @@
         TestSet,
         TestList,
         TestListReversed,
+        TestLengthHintExceptions,
     ]
     test_support.run_unittest(*unittests)
 

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Mon Feb  2 22:50:13 2009
@@ -14,6 +14,10 @@
 
 - Issue #4978: Passing keyword arguments as unicode strings is now allowed.
 
+- Issue 1242657: the __len__() and __length_hint__() calls in several tools
+  were suppressing all exceptions.  These include list(), filter(), map(),
+  zip(), and bytearray().
+
 - os.ftruncate raises OSErrors instead of IOErrors for consistency with other os
   functions.
 

Modified: python/trunk/Objects/abstract.c
==============================================================================
--- python/trunk/Objects/abstract.c	(original)
+++ python/trunk/Objects/abstract.c	Mon Feb  2 22:50:13 2009
@@ -85,8 +85,8 @@
 
 /* The length hint function returns a non-negative value from o.__len__()
    or o.__length_hint__().  If those methods aren't found or return a negative
-   value, then the defaultvalue is returned.  This function never fails. 
-   Accordingly, it will mask exceptions raised in either method.
+   value, then the defaultvalue is returned.  If one of the calls fails,
+   this function returns -1.
 */
 
 Py_ssize_t
@@ -100,29 +100,32 @@
 	rv = PyObject_Size(o);
 	if (rv >= 0)
 		return rv;
-	if (PyErr_Occurred())
+	if (PyErr_Occurred()) {
+		if (!PyErr_ExceptionMatches(PyExc_TypeError) &&
+			!PyErr_ExceptionMatches(PyExc_AttributeError))
+				return -1;
 		PyErr_Clear();
+	}
 
 	/* cache a hashed version of the attribute string */
 	if (hintstrobj == NULL) {
 		hintstrobj = PyString_InternFromString("__length_hint__");
 		if (hintstrobj == NULL)
-			goto defaultcase;
+			return -1;
 	}
 
 	/* try o.__length_hint__() */
 	ro = PyObject_CallMethodObjArgs(o, hintstrobj, NULL);
-	if (ro == NULL)
-		goto defaultcase;
+	if (ro == NULL) {
+		if (!PyErr_ExceptionMatches(PyExc_TypeError) &&
+			!PyErr_ExceptionMatches(PyExc_AttributeError))
+				return -1;
+		PyErr_Clear();
+		return defaultvalue;
+	}
 	rv = PyInt_AsLong(ro);
 	Py_DECREF(ro);
-	if (rv >= 0)
-		return rv;
-
-defaultcase:
-	if (PyErr_Occurred())
-		PyErr_Clear();
-	return defaultvalue;
+	return rv;
 }
 
 PyObject *
@@ -2128,7 +2131,7 @@
 {
 	PyObject *it;  /* iter(v) */
 	Py_ssize_t n;         /* guess for result tuple size */
-	PyObject *result;
+	PyObject *result = NULL;
 	Py_ssize_t j;
 
 	if (v == NULL)
@@ -2153,6 +2156,8 @@
 
 	/* Guess result size and allocate space. */
 	n = _PyObject_LengthHint(v, 10);
+	if (n == -1)
+		goto Fail;
 	result = PyTuple_New(n);
 	if (result == NULL)
 		goto Fail;

Modified: python/trunk/Objects/bytearrayobject.c
==============================================================================
--- python/trunk/Objects/bytearrayobject.c	(original)
+++ python/trunk/Objects/bytearrayobject.c	Mon Feb  2 22:50:13 2009
@@ -2691,6 +2691,10 @@
 
     /* Try to determine the length of the argument. 32 is abitrary. */
     buf_size = _PyObject_LengthHint(arg, 32);
+	if (buf_size == -1) {
+		Py_DECREF(it);
+		return NULL;
+	}
 
     bytes_obj = PyByteArray_FromStringAndSize(NULL, buf_size);
     if (bytes_obj == NULL)

Modified: python/trunk/Objects/listobject.c
==============================================================================
--- python/trunk/Objects/listobject.c	(original)
+++ python/trunk/Objects/listobject.c	Mon Feb  2 22:50:13 2009
@@ -838,6 +838,10 @@
 
 	/* Guess a result list size. */
 	n = _PyObject_LengthHint(b, 8);
+	if (n == -1) {
+		Py_DECREF(it);
+		return NULL;
+	}
 	m = Py_SIZE(self);
 	mn = m + n;
 	if (mn >= m) {

Modified: python/trunk/Python/bltinmodule.c
==============================================================================
--- python/trunk/Python/bltinmodule.c	(original)
+++ python/trunk/Python/bltinmodule.c	Mon Feb  2 22:50:13 2009
@@ -268,6 +268,8 @@
 
 	/* Guess a result list size. */
 	len = _PyObject_LengthHint(seq, 8);
+	if (len == -1)
+		goto Fail_it;
 
 	/* Get a result list. */
 	if (PyList_Check(seq) && seq->ob_refcnt == 1) {


More information about the Python-checkins mailing list