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

index 87afe9a6402d29c0f534aaf7a2513ccfb52346c8..fc9c6b5b454ca1e74346208cc81963bbe7b7007e 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 f399fde57166290a7fd61c79810afe3e0b12d9f3..4954c8b6f5ba8369797ccc14c50f3e394e4acb93 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -14,6 +14,10 @@ Core and Builtins
 
 - 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.
 
index 0d6aa4adb3b476c81d704d572d38615ce3fe6891..1ac4ac95b51c7f24eb1c5cd1a82bedc93a3e59c8 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 *
@@ -2128,7 +2131,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)
@@ -2153,6 +2156,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 4b8d585c8be4a79f557a63189399f5b445083adf..4cfd9f839c46387cd00f3d148d4a11b5076b802b 100644 (file)
@@ -2691,6 +2691,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) {