Skip to content

Commit 329487d

Browse files
author
raymond.hettinger
committed
Issue 1242657: list(obj) can swallow KeyboardInterrupt.
git-svn-id: http://svn.python.org/projects/python/trunk@69227 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent d7f3af0 commit 329487d

7 files changed

Lines changed: 65 additions & 15 deletions

File tree

Include/abstract.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
438438
/*
439439
Guess the size of object o using len(o) or o.__length_hint__().
440440
If neither of those return a non-negative value, then return the
441-
default value. This function never fails. All exceptions are cleared.
441+
default value. If one of the calls fails, this function returns -1.
442442
*/
443443

444444
PyAPI_FUNC(PyObject *) PyObject_GetItem(PyObject *o, PyObject *key);

Lib/test/test_iterlen.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,36 @@ def test_mutation(self):
195195
d.extend(xrange(20))
196196
self.assertEqual(len(it), 0)
197197

198+
## -- Check to make sure exceptions are not suppressed by __length_hint__()
199+
200+
201+
class BadLen(object):
202+
def __iter__(self): return iter(range(10))
203+
def __len__(self):
204+
raise RuntimeError('hello')
205+
206+
class BadLengthHint(object):
207+
def __iter__(self): return iter(range(10))
208+
def __length_hint__(self):
209+
raise RuntimeError('hello')
210+
211+
class TestLengthHintExceptions(unittest.TestCase):
212+
213+
def test_issue1242657(self):
214+
self.assertRaises(RuntimeError, list, BadLen())
215+
self.assertRaises(RuntimeError, list, BadLengthHint())
216+
self.assertRaises(RuntimeError, [].extend, BadLen())
217+
self.assertRaises(RuntimeError, [].extend, BadLengthHint())
218+
self.assertRaises(RuntimeError, zip, BadLen())
219+
self.assertRaises(RuntimeError, zip, BadLengthHint())
220+
self.assertRaises(RuntimeError, filter, None, BadLen())
221+
self.assertRaises(RuntimeError, filter, None, BadLengthHint())
222+
self.assertRaises(RuntimeError, map, chr, BadLen())
223+
self.assertRaises(RuntimeError, map, chr, BadLengthHint())
224+
b = bytearray(range(10))
225+
self.assertRaises(RuntimeError, b.extend, BadLen())
226+
self.assertRaises(RuntimeError, b.extend, BadLengthHint())
227+
198228
def test_main():
199229
unittests = [
200230
TestRepeat,
@@ -209,6 +239,7 @@ def test_main():
209239
TestSet,
210240
TestList,
211241
TestListReversed,
242+
TestLengthHintExceptions,
212243
]
213244
test_support.run_unittest(*unittests)
214245

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ Core and Builtins
1414

1515
- Issue #4978: Passing keyword arguments as unicode strings is now allowed.
1616

17+
- Issue 1242657: the __len__() and __length_hint__() calls in several tools
18+
were suppressing all exceptions. These include list(), filter(), map(),
19+
zip(), and bytearray().
20+
1721
- os.ftruncate raises OSErrors instead of IOErrors for consistency with other os
1822
functions.
1923

Objects/abstract.c

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ PyObject_Length(PyObject *o)
8585

8686
/* The length hint function returns a non-negative value from o.__len__()
8787
or o.__length_hint__(). If those methods aren't found or return a negative
88-
value, then the defaultvalue is returned. This function never fails.
89-
Accordingly, it will mask exceptions raised in either method.
88+
value, then the defaultvalue is returned. If one of the calls fails,
89+
this function returns -1.
9090
*/
9191

9292
Py_ssize_t
@@ -100,29 +100,32 @@ _PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue)
100100
rv = PyObject_Size(o);
101101
if (rv >= 0)
102102
return rv;
103-
if (PyErr_Occurred())
103+
if (PyErr_Occurred()) {
104+
if (!PyErr_ExceptionMatches(PyExc_TypeError) &&
105+
!PyErr_ExceptionMatches(PyExc_AttributeError))
106+
return -1;
104107
PyErr_Clear();
108+
}
105109

106110
/* cache a hashed version of the attribute string */
107111
if (hintstrobj == NULL) {
108112
hintstrobj = PyString_InternFromString("__length_hint__");
109113
if (hintstrobj == NULL)
110-
goto defaultcase;
114+
return -1;
111115
}
112116

113117
/* try o.__length_hint__() */
114118
ro = PyObject_CallMethodObjArgs(o, hintstrobj, NULL);
115-
if (ro == NULL)
116-
goto defaultcase;
119+
if (ro == NULL) {
120+
if (!PyErr_ExceptionMatches(PyExc_TypeError) &&
121+
!PyErr_ExceptionMatches(PyExc_AttributeError))
122+
return -1;
123+
PyErr_Clear();
124+
return defaultvalue;
125+
}
117126
rv = PyInt_AsLong(ro);
118127
Py_DECREF(ro);
119-
if (rv >= 0)
120-
return rv;
121-
122-
defaultcase:
123-
if (PyErr_Occurred())
124-
PyErr_Clear();
125-
return defaultvalue;
128+
return rv;
126129
}
127130

128131
PyObject *
@@ -2128,7 +2131,7 @@ PySequence_Tuple(PyObject *v)
21282131
{
21292132
PyObject *it; /* iter(v) */
21302133
Py_ssize_t n; /* guess for result tuple size */
2131-
PyObject *result;
2134+
PyObject *result = NULL;
21322135
Py_ssize_t j;
21332136

21342137
if (v == NULL)
@@ -2153,6 +2156,8 @@ PySequence_Tuple(PyObject *v)
21532156

21542157
/* Guess result size and allocate space. */
21552158
n = _PyObject_LengthHint(v, 10);
2159+
if (n == -1)
2160+
goto Fail;
21562161
result = PyTuple_New(n);
21572162
if (result == NULL)
21582163
goto Fail;

Objects/bytearrayobject.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2691,6 +2691,10 @@ bytes_extend(PyByteArrayObject *self, PyObject *arg)
26912691

26922692
/* Try to determine the length of the argument. 32 is abitrary. */
26932693
buf_size = _PyObject_LengthHint(arg, 32);
2694+
if (buf_size == -1) {
2695+
Py_DECREF(it);
2696+
return NULL;
2697+
}
26942698

26952699
bytes_obj = PyByteArray_FromStringAndSize(NULL, buf_size);
26962700
if (bytes_obj == NULL)

Objects/listobject.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,10 @@ listextend(PyListObject *self, PyObject *b)
838838

839839
/* Guess a result list size. */
840840
n = _PyObject_LengthHint(b, 8);
841+
if (n == -1) {
842+
Py_DECREF(it);
843+
return NULL;
844+
}
841845
m = Py_SIZE(self);
842846
mn = m + n;
843847
if (mn >= m) {

Python/bltinmodule.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ builtin_filter(PyObject *self, PyObject *args)
268268

269269
/* Guess a result list size. */
270270
len = _PyObject_LengthHint(seq, 8);
271+
if (len == -1)
272+
goto Fail_it;
271273

272274
/* Get a result list. */
273275
if (PyList_Check(seq) && seq->ob_refcnt == 1) {

0 commit comments

Comments
 (0)