Issue 1242657: list(obj) can swallow KeyboardInterrupt.

This commit is contained in:
Raymond Hettinger 2009-02-02 21:50:13 +00:00
parent d7bb4d484f
commit b516370bcb
7 changed files with 65 additions and 15 deletions

View File

@ -438,7 +438,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
/* /*
Guess the size of object o using len(o) or o.__length_hint__(). 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 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); PyAPI_FUNC(PyObject *) PyObject_GetItem(PyObject *o, PyObject *key);

View File

@ -195,6 +195,36 @@ class TestListReversed(TestInvariantWithoutMutations):
d.extend(xrange(20)) d.extend(xrange(20))
self.assertEqual(len(it), 0) 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(): def test_main():
unittests = [ unittests = [
TestRepeat, TestRepeat,
@ -209,6 +239,7 @@ def test_main():
TestSet, TestSet,
TestList, TestList,
TestListReversed, TestListReversed,
TestLengthHintExceptions,
] ]
test_support.run_unittest(*unittests) test_support.run_unittest(*unittests)

View File

@ -14,6 +14,10 @@ Core and Builtins
- Issue #4978: Passing keyword arguments as unicode strings is now allowed. - 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 - os.ftruncate raises OSErrors instead of IOErrors for consistency with other os
functions. functions.

View File

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

View File

@ -2691,6 +2691,10 @@ bytes_extend(PyByteArrayObject *self, PyObject *arg)
/* Try to determine the length of the argument. 32 is abitrary. */ /* Try to determine the length of the argument. 32 is abitrary. */
buf_size = _PyObject_LengthHint(arg, 32); buf_size = _PyObject_LengthHint(arg, 32);
if (buf_size == -1) {
Py_DECREF(it);
return NULL;
}
bytes_obj = PyByteArray_FromStringAndSize(NULL, buf_size); bytes_obj = PyByteArray_FromStringAndSize(NULL, buf_size);
if (bytes_obj == NULL) if (bytes_obj == NULL)

View File

@ -838,6 +838,10 @@ listextend(PyListObject *self, PyObject *b)
/* Guess a result list size. */ /* Guess a result list size. */
n = _PyObject_LengthHint(b, 8); n = _PyObject_LengthHint(b, 8);
if (n == -1) {
Py_DECREF(it);
return NULL;
}
m = Py_SIZE(self); m = Py_SIZE(self);
mn = m + n; mn = m + n;
if (mn >= m) { if (mn >= m) {

View File

@ -268,6 +268,8 @@ builtin_filter(PyObject *self, PyObject *args)
/* Guess a result list size. */ /* Guess a result list size. */
len = _PyObject_LengthHint(seq, 8); len = _PyObject_LengthHint(seq, 8);
if (len == -1)
goto Fail_it;
/* Get a result list. */ /* Get a result list. */
if (PyList_Check(seq) && seq->ob_refcnt == 1) { if (PyList_Check(seq) && seq->ob_refcnt == 1) {