Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Avoid :meth:`io.RawIOBase.read` from reading into a temporary :class:`bytearray`
when calling its own :meth:`io.RawIOBase.readinto` internally.
25 changes: 17 additions & 8 deletions Modules/_io/iobase.c
Original file line number Diff line number Diff line change
Expand Up @@ -921,19 +921,25 @@ static PyObject *
_io__RawIOBase_read_impl(PyObject *self, Py_ssize_t n)
/*[clinic end generated code: output=6cdeb731e3c9f13c input=b6d0dcf6417d1374]*/
{
PyObject *b, *res;
PyObject *b, *res, *mv;

if (n < 0) {
return PyObject_CallMethodNoArgs(self, &_Py_ID(readall));
}

/* TODO: allocate a bytes object directly instead and manually construct
a writable memoryview pointing to it. */
b = PyByteArray_FromStringAndSize(NULL, n);
b = PyBytes_FromStringAndSize(NULL, n);
if (b == NULL)
return NULL;

res = PyObject_CallMethodObjArgs(self, &_Py_ID(readinto), b, NULL);
mv = PyMemoryView_FromMemory(PyBytes_AS_STRING(b), n, PyBUF_WRITE);
if (mv == NULL) {
Py_DECREF(b);
return NULL;
}

res = PyObject_CallMethodObjArgs(self, &_Py_ID(readinto), mv, NULL);
Py_DECREF(mv);

if (res == NULL || res == Py_None) {
Py_DECREF(b);
return res;
Expand All @@ -946,9 +952,12 @@ _io__RawIOBase_read_impl(PyObject *self, Py_ssize_t n)
return NULL;
}

res = PyBytes_FromStringAndSize(PyByteArray_AsString(b), n);
Py_DECREF(b);
return res;
if (_PyBytes_Resize(&b, n) < 0) {
Py_DECREF(b);
return NULL;
}

return b;
}


Expand Down
Loading