diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index ed63fda20c1b7a..36841eb3831563 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -281,6 +281,10 @@ def __length_hint__(self): a.extend(CustomIter()) self.assertEqual(a, [1,2,3,4]) + # bpo-28940 + a = self.type2test([]) + a.extend(CustomIter()) + self.assertEqual(a, []) def test_insert(self): a = self.type2test([0, 1, 2]) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 1408fb4fb84b0b..20d9a31561c5ea 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -774,6 +774,19 @@ def test_translate(self): c = b.translate(None, delete=b'e') self.assertEqual(c, b'hllo') + def test_from_iter(self): + # bpo-28940 + class CustomIter: + def __iter__(self): + return self + def __next__(self): + raise StopIteration + def __length_hint__(self): + return sys.maxsize + + a = self.type2test(CustomIter()) + self.assertEqual(a, b'') + class BytesTest(BaseBytesTest, unittest.TestCase): type2test = bytes diff --git a/Lib/test/test_tuple.py b/Lib/test/test_tuple.py index 84c064f19f2ec2..07567f3ee64806 100644 --- a/Lib/test/test_tuple.py +++ b/Lib/test/test_tuple.py @@ -3,6 +3,7 @@ import gc import pickle +import sys class TupleTest(seq_tests.CommonTest): type2test = tuple @@ -223,5 +224,19 @@ def test_lexicographic_ordering(self): self.assertLess(a, b) self.assertLess(b, c) + def test_from_iter(self): + # bpo-28940 + class CustomIter: + def __iter__(self): + return self + def __next__(self): + raise StopIteration + def __length_hint__(self): + return sys.maxsize + + a = self.type2test(CustomIter()) + self.assertEqual(a, ()) + + if __name__ == "__main__": unittest.main() diff --git a/Objects/abstract.c b/Objects/abstract.c index 8d3030a118ae54..5e3117796b255d 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1859,8 +1859,16 @@ PySequence_Tuple(PyObject *v) if (n == -1) goto Fail; result = PyTuple_New(n); - if (result == NULL) - goto Fail; + if (result == NULL) { + /* bpo-28940 - LengthHint could lie and asked for too much, + * try again without preallocation. */ + PyErr_Clear(); + result = PyTuple_New(0); + if (result == NULL) { + return NULL; + } + n = 0; + } /* Fill the tuple. */ for (j = 0; ; ++j) { diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 4e2bb603151b92..ff05ffb36d1eae 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -1639,8 +1639,15 @@ bytearray_extend(PyByteArrayObject *self, PyObject *iterable_of_ints) bytearray_obj = PyByteArray_FromStringAndSize(NULL, buf_size); if (bytearray_obj == NULL) { - Py_DECREF(it); - return NULL; + /* bpo-28940 - LengthHint could lie and asked for too much, + * try again without preallocation. */ + PyErr_Clear(); + bytearray_obj = PyByteArray_FromStringAndSize(NULL, 1); + if (bytearray_obj == NULL) { + Py_DECREF(it); + return NULL; + } + buf_size = 1; } buf = PyByteArray_AS_STRING(bytearray_obj); diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 648b2a591516c1..32e7a3a4358b7d 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -2698,8 +2698,16 @@ _PyBytes_FromIterator(PyObject *it, PyObject *x) _PyBytesWriter_Init(&writer); str = _PyBytesWriter_Alloc(&writer, size); - if (str == NULL) - return NULL; + if (str == NULL) { + /* bpo-28940 - LengthHint could lie and asked for too much, + * try again without preallocation. */ + PyErr_Clear(); + _PyBytesWriter_Init(&writer); + str = _PyBytesWriter_Alloc(&writer, 1); + if (str == NULL) { + return NULL; + } + } writer.overallocate = 1; size = writer.allocated; diff --git a/Objects/listobject.c b/Objects/listobject.c index 4108f502023ad0..200657bd2006fe 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -886,10 +886,15 @@ list_extend(PyListObject *self, PyObject *iterable) else { mn = m + n; /* Make room. */ - if (list_resize(self, mn) < 0) - goto error; - /* Make the list sane again. */ - Py_SIZE(self) = m; + if (list_resize(self, mn) < 0) { + /* bpo-28940 - LengthHint could lie and asked for too much, + * try again without preallocation. */ + PyErr_Clear(); + } + else { + /* Make the list sane again. */ + Py_SIZE(self) = m; + } } /* Run iterator to exhaustion. */