]> granicus.if.org Git - python/commitdiff
Issue 1242657: list(obj) can swallow KeyboardInterrupt
authorRaymond Hettinger <python@rcn.com>
Mon, 2 Feb 2009 22:44:06 +0000 (22:44 +0000)
committerRaymond Hettinger <python@rcn.com>
Mon, 2 Feb 2009 22:44:06 +0000 (22:44 +0000)
Include/abstract.h
Lib/test/test_iterlen.py
Misc/NEWS
Objects/abstract.c
Objects/bytearrayobject.c
Objects/listobject.c
Python/bltinmodule.c

index f3bc8bcd0fe9f5de553a0f144589a2c85cb95be8..c8446ebc2b18a487fea15dbaa81a558e0db6ed9a 100644 (file)
@@ -438,7 +438,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
        /*
          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);
index 2a32512e8f9c1155308a8e5e77520d432b1aaa8c..91873c2af4a07cf515fa0f1f18579c8bcc4d6bed 100644 (file)
@@ -195,6 +195,36 @@ class TestListReversed(TestInvariantWithoutMutations):
         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 @@ def test_main():
         TestSet,
         TestList,
         TestListReversed,
+        TestLengthHintExceptions,
     ]
     test_support.run_unittest(*unittests)
 
index 8f4e485f61cfcb9b9c030461d1d07e8bad4dbbf2..ebff3a152cd86b458905ad2bd7be98f4218b4ae6 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -15,6 +15,10 @@ Core and Builtins
 - Issue #5013: Fixed a bug in FileHandler which occurred when the delay
   parameter was set.
 
+- Issue 1242657: the __len__() and __length_hint__() calls in several tools
+  were suppressing all exceptions.  These include list(), filter(), map(),
+  zip(), and bytearray().
+
 - Issue #4935: The overflow checking code in the expandtabs() method common
   to str, bytes and bytearray could be optimized away by the compiler, letting
   the interpreter segfault instead of raising an error.
index 956c4f4996933928cf5e52d57ab78ab02a466af3..c10f4ca4f95eac83ea77bcd0ba183a6b74edf7c2 100644 (file)
@@ -85,8 +85,8 @@ PyObject_Length(PyObject *o)
 
 /* 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 @@ _PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue)
        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 *
@@ -2114,7 +2117,7 @@ PySequence_Tuple(PyObject *v)
 {
        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)
@@ -2139,6 +2142,8 @@ PySequence_Tuple(PyObject *v)
 
        /* 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;
index 56071a0cce18317eac712f617eda6ce03cc5e776..34d9137c46930e1f631aa6537ca19e70e8a51d6e 100644 (file)
@@ -2679,6 +2679,10 @@ bytes_extend(PyByteArrayObject *self, PyObject *arg)
 
     /* 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)
index 7b4bf35a3fa23e254589c6643f3faa42be6070e7..98d7e473549e0a2c557a0754fc0ea64ac38fbf3a 100644 (file)
@@ -838,6 +838,10 @@ listextend(PyListObject *self, PyObject *b)
 
        /* 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) {
index 4e6f901ab34a8b20ef8df6c347fe42e14d6387dc..4d7dec1c3f13abd609e35fc1acbac7ee0ccb5bcb 100644 (file)
@@ -268,6 +268,8 @@ builtin_filter(PyObject *self, PyObject *args)
 
        /* 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) {