summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2009-02-02 22:44:06 +0000
committerRaymond Hettinger <python@rcn.com>2009-02-02 22:44:06 +0000
commit24e2872f0d04c0d7c1745f677aef1a335433b556 (patch)
treed11c883d601cda87a46839fbc6ba321089c6cb61
parentb2dbd4309aa3d2a8a823d251447b8fd8b4b2d7e3 (diff)
downloadcpython-git-24e2872f0d04c0d7c1745f677aef1a335433b556.tar.gz
Issue 1242657: list(obj) can swallow KeyboardInterrupt
-rw-r--r--Include/abstract.h2
-rw-r--r--Lib/test/test_iterlen.py31
-rw-r--r--Misc/NEWS4
-rw-r--r--Objects/abstract.c33
-rw-r--r--Objects/bytearrayobject.c4
-rw-r--r--Objects/listobject.c4
-rw-r--r--Python/bltinmodule.c2
7 files changed, 65 insertions, 15 deletions
diff --git a/Include/abstract.h b/Include/abstract.h
index f3bc8bcd0f..c8446ebc2b 100644
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -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);
diff --git a/Lib/test/test_iterlen.py b/Lib/test/test_iterlen.py
index 2a32512e8f..91873c2af4 100644
--- a/Lib/test/test_iterlen.py
+++ b/Lib/test/test_iterlen.py
@@ -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)
diff --git a/Misc/NEWS b/Misc/NEWS
index 8f4e485f61..ebff3a152c 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -15,6 +15,10 @@ Core and Builtins
- Issue #5013: Fixed a bug in FileHandler which occurred when the delay
parameter was set.
+- Issue 1242657: the __len__() and __length_hint__() calls in several tools
+ were suppressing all exceptions. These include list(), filter(), map(),
+ zip(), and bytearray().
+
- Issue #4935: The overflow checking code in the expandtabs() method common
to str, bytes and bytearray could be optimized away by the compiler, letting
the interpreter segfault instead of raising an error.
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 956c4f4996..c10f4ca4f9 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -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 *
@@ -2114,7 +2117,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)
@@ -2139,6 +2142,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;
diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c
index 56071a0cce..34d9137c46 100644
--- a/Objects/bytearrayobject.c
+++ b/Objects/bytearrayobject.c
@@ -2679,6 +2679,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)
diff --git a/Objects/listobject.c b/Objects/listobject.c
index 7b4bf35a3f..98d7e47354 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -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) {
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 4e6f901ab3..4d7dec1c3f 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -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) {