Skip to content
20 changes: 20 additions & 0 deletions Lib/test/test_io/test_general.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,26 @@ def test_RawIOBase_readall(self):
rawio = self.MockRawIOWithoutRead((b"abc", b"d", b"efg"))
self.assertEqual(rawio.readall(), b"abcdefg")

# gh-60107: Ensure a "Raw I/O" which keeps a reference to the
# mutable memory doesn't allow making a mutable bytes.
class RawIOKeepsReference(self.MockRawIOWithoutRead):
def __init__(self, *args, **kwargs):
self.buf = None
super().__init__(*args, **kwargs)

def readinto(self, buf):
# buf is the bytearray so keeping a reference to it doesn't keep
# the memory alive; a memoryview does.
self.buf = memoryview(buf)
buf[0:4] = self._read_stack.pop()
return 3

with self.assertRaises(BufferError):
rawio = RawIOKeepsReference([b"1234"])
rawio.read(4)


Comment thread
cmaloney marked this conversation as resolved.
Outdated

def test_BufferedIOBase_readinto(self):
# Exercise the default BufferedIOBase.readinto() and readinto1()
# implementations (which call read() or read1() internally).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Remove a copy from :meth:`io.RawIOBase.read`. If the underlying I/O class
keeps a reference to the mutable memory raise a :exc:`BufferError`.
Comment thread
vstinner marked this conversation as resolved.
Outdated
14 changes: 13 additions & 1 deletion Modules/_io/iobase.c
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,19 @@ _io__RawIOBase_read_impl(PyObject *self, Py_ssize_t n)
return NULL;
}

res = PyBytes_FromStringAndSize(PyByteArray_AsString(b), bytes_filled);
res = PyObject_CallMethod(b, "resize", "i", bytes_filled);
if (res != Py_None) {
Py_DECREF(b);
if (res != NULL) {
PyErr_Format(PyExc_ValueError,
"resize returned unexpected value %R",
res);
Py_DECREF(res);
res = NULL;
}
return res;
}
res = PyObject_CallMethod(b, "take_bytes", NULL);
Copy link
Copy Markdown
Contributor Author

@cmaloney cmaloney Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vstinner : Not sure how common this "resize/discard then take_bytes" is going to be; might make sense to change to take_bytes(n=None, /, *, discard=False)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for now planning to keep that in back pocket until need many ways (ba.resize(n) or del ba[:n] gives the same capability)

Comment thread
cmaloney marked this conversation as resolved.
Outdated
Py_DECREF(b);
return res;
}
Expand Down
Loading