Index: Objects/bytesobject.c =================================================================== --- Objects/bytesobject.c (revision 59259) +++ Objects/bytesobject.c (working copy) @@ -2487,24 +2487,6 @@ return NULL; } -PyDoc_STRVAR(extend__doc__, -"B.extend(iterable int) -> None\n\ -\n\ -Append all the elements from the iterator or sequence to the\n\ -end of B."); -static PyObject * -bytes_extend(PyBytesObject *self, PyObject *arg) -{ - /* XXX(gps): The docstring says any iterable int will do but the - * bytes_setslice code only accepts something supporting PEP 3118. - * A list or tuple of 0 <= int <= 255 is supposed to work. */ - /* bug being tracked on: http://bugs.python.org/issue1283 */ - if (bytes_setslice(self, Py_Size(self), Py_Size(self), arg) == -1) - return NULL; - Py_RETURN_NONE; -} - - PyDoc_STRVAR(reverse__doc__, "B.reverse() -> None\n\ \n\ @@ -2591,6 +2573,69 @@ Py_RETURN_NONE; } +PyDoc_STRVAR(extend__doc__, +"B.extend(iterable int) -> None\n\ +\n\ +Append all the elements from the iterator or sequence to the\n\ +end of B."); +static PyObject * +bytes_extend(PyBytesObject *self, PyObject *arg) +{ + PyObject *it, *item, *tmp, *res; + Py_ssize_t len_hint, buf_size = 0, len = 0; + int value; + char *buf; + + /* bytes_setslice code only accepts something supporting PEP 3118. */ + if (PyObject_CheckBuffer(arg)) { + if (bytes_setslice(self, Py_Size(self), Py_Size(self), arg) == -1) + return NULL; + + Py_RETURN_NONE; + } + + it = PyObject_GetIter(arg); + if (it == NULL) + return NULL; + + len_hint = _PyObject_LengthHint(arg); + if (len_hint != -1) { + buf_size = len_hint; + } + else { + PyErr_Clear(); + buf_size = 32; + } + + buf = (char *)PyMem_Malloc(buf_size * sizeof(char)); + if (buf == NULL) + return PyErr_NoMemory(); + + while ((item = PyIter_Next(it)) != NULL) { + if (! _getbytevalue(item, &value)) { + Py_DECREF(item); + Py_DECREF(it); + return NULL; + } + buf[len++] = value; + Py_DECREF(item); + if (len >= buf_size) { + buf_size = len + (len >> 1) + 1; + buf = (char *)PyMem_Realloc(buf, buf_size * sizeof(char)); + if (buf == NULL) + return PyErr_NoMemory(); + } + } + Py_DECREF(it); + + tmp = PyBytes_FromStringAndSize(buf, len); + res = bytes_extend(self, tmp); + Py_DECREF(tmp); + PyMem_Free(buf); + + return res; +} + PyDoc_STRVAR(pop__doc__, "B.pop([index]) -> int\n\ \n\ Index: Lib/test/test_bytes.py =================================================================== --- Lib/test/test_bytes.py (revision 59259) +++ Lib/test/test_bytes.py (working copy) @@ -529,6 +529,21 @@ a.extend(a) self.assertEqual(a, orig + orig) self.assertEqual(a[5:], orig) + a = bytearray(b'') + a.extend(map(int, orig * 50)) + self.assertEqual(a, orig * 50) + self.assertEqual(a[-5:], orig) + a = bytearray(b'') + a.extend(iter(map(int, orig * 50))) + self.assertEqual(a, orig * 50) + self.assertEqual(a[-5:], orig) + a = bytearray(b'') + a.extend(list(map(int, orig * 50))) + self.assertEqual(a, orig * 50) + self.assertEqual(a[-5:], orig) + a = bytearray(b'') + self.assertRaises(ValueError, a.extend, [0, 1, 2, 3, 256]) + self.assertEqual(len(a), 0) def test_remove(self): b = bytearray(b'hello')