Index: Python/bltinmodule.c =================================================================== --- Python/bltinmodule.c (revision 68609) +++ Python/bltinmodule.c (working copy) @@ -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) { @@ -977,6 +979,8 @@ /* Update len. */ curlen = _PyObject_LengthHint(curseq, 8); + if (curlen == -1) + goto Fail_2; if (curlen > len) len = curlen; } @@ -2381,10 +2385,12 @@ /* Guess at result length: the shortest of the input lengths. If some argument refuses to say, we refuse to guess too, lest an argument like xrange(sys.maxint) lead us astray.*/ - len = -1; /* unknown */ + len = -2; /* unknown */ for (i = 0; i < itemsize; ++i) { PyObject *item = PyTuple_GET_ITEM(args, i); - Py_ssize_t thislen = _PyObject_LengthHint(item, -1); + Py_ssize_t thislen = _PyObject_LengthHint(item, -2); + if (thislen == -1) + return NULL; if (thislen < 0) { len = -1; break; Index: Include/abstract.h =================================================================== --- Include/abstract.h (revision 68609) +++ Include/abstract.h (working copy) @@ -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); Index: Objects/abstract.c =================================================================== --- Objects/abstract.c (revision 68609) +++ Objects/abstract.c (working copy) @@ -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,31 @@ 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 && PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_TypeError) && + !PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_Clear(); + } 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 +2116,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) @@ -2139,6 +2141,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; Index: Objects/listobject.c =================================================================== --- Objects/listobject.c (revision 68609) +++ Objects/listobject.c (working copy) @@ -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) { Index: Objects/bytearrayobject.c =================================================================== --- Objects/bytearrayobject.c (revision 68609) +++ Objects/bytearrayobject.c (working copy) @@ -2687,6 +2687,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) Index: Lib/test/test_iterlen.py =================================================================== --- Lib/test/test_iterlen.py (revision 68609) +++ Lib/test/test_iterlen.py (working copy) @@ -195,6 +195,33 @@ 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()) + def test_main(): unittests = [ TestRepeat, @@ -209,6 +236,7 @@ TestSet, TestList, TestListReversed, + TestLengthHintExceptions, ] test_support.run_unittest(*unittests)