Commit e8364233 authored by Raymond Hettinger's avatar Raymond Hettinger

Issue 1242657: list(obj) can swallow KeyboardInterrupt.

parent 9f989264
......@@ -392,7 +392,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);
......
......@@ -195,6 +195,29 @@ class TestListReversed(TestInvariantWithoutMutations):
d.extend(range(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())
b = bytearray(range(10))
self.assertRaises(RuntimeError, b.extend, BadLen())
self.assertRaises(RuntimeError, b.extend, BadLengthHint())
def test_main():
unittests = [
......@@ -210,6 +233,7 @@ def test_main():
TestSet,
TestList,
TestListReversed,
TestLengthHintExceptions,
]
support.run_unittest(*unittests)
......
......@@ -17,6 +17,9 @@ Core and Builtins
the type definition cmpfunc. The tp_compare slot has been renamed
to tp_reserved, and is reserved for future usage.
- Issue 1242657: the __len__() and __length_hint__() calls in several tools
were suppressing all exceptions. These include list() and bytearray().
- Issue #4707: round(x, n) now returns an integer if x is an integer.
Previously it returned a float.
......
......@@ -67,8 +67,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
......@@ -82,29 +82,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 = PyUnicode_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 = PyLong_AsSsize_t(ro);
Py_DECREF(ro);
if (rv >= 0)
return rv;
defaultcase:
if (PyErr_Occurred())
PyErr_Clear();
return defaultvalue;
return rv;
}
PyObject *
......@@ -1742,7 +1745,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)
......@@ -1767,6 +1770,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;
......
......@@ -2616,6 +2616,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)
......
......@@ -802,6 +802,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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment