mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-60107: Remove a copy from RawIOBase.read (#141532)
If the underlying I/O class keeps a reference to the memory, raise BufferError. Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
722f4bb8c9
commit
e90061f5f1
7 changed files with 39 additions and 11 deletions
|
|
@ -2070,6 +2070,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(symmetric_difference_update));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(symmetric_difference_update));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(tabsize));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(tabsize));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(tag));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(tag));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(take_bytes));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(target));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(target));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(target_is_directory));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(target_is_directory));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(task));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(task));
|
||||||
|
|
|
||||||
|
|
@ -793,6 +793,7 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(symmetric_difference_update)
|
STRUCT_FOR_ID(symmetric_difference_update)
|
||||||
STRUCT_FOR_ID(tabsize)
|
STRUCT_FOR_ID(tabsize)
|
||||||
STRUCT_FOR_ID(tag)
|
STRUCT_FOR_ID(tag)
|
||||||
|
STRUCT_FOR_ID(take_bytes)
|
||||||
STRUCT_FOR_ID(target)
|
STRUCT_FOR_ID(target)
|
||||||
STRUCT_FOR_ID(target_is_directory)
|
STRUCT_FOR_ID(target_is_directory)
|
||||||
STRUCT_FOR_ID(task)
|
STRUCT_FOR_ID(task)
|
||||||
|
|
|
||||||
1
Include/internal/pycore_runtime_init_generated.h
generated
1
Include/internal/pycore_runtime_init_generated.h
generated
|
|
@ -2068,6 +2068,7 @@ extern "C" {
|
||||||
INIT_ID(symmetric_difference_update), \
|
INIT_ID(symmetric_difference_update), \
|
||||||
INIT_ID(tabsize), \
|
INIT_ID(tabsize), \
|
||||||
INIT_ID(tag), \
|
INIT_ID(tag), \
|
||||||
|
INIT_ID(take_bytes), \
|
||||||
INIT_ID(target), \
|
INIT_ID(target), \
|
||||||
INIT_ID(target_is_directory), \
|
INIT_ID(target_is_directory), \
|
||||||
INIT_ID(task), \
|
INIT_ID(task), \
|
||||||
|
|
|
||||||
|
|
@ -2952,6 +2952,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
_PyUnicode_InternStatic(interp, &string);
|
_PyUnicode_InternStatic(interp, &string);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||||
|
string = &_Py_ID(take_bytes);
|
||||||
|
_PyUnicode_InternStatic(interp, &string);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||||
string = &_Py_ID(target);
|
string = &_Py_ID(target);
|
||||||
_PyUnicode_InternStatic(interp, &string);
|
_PyUnicode_InternStatic(interp, &string);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
|
|
||||||
|
|
@ -609,6 +609,25 @@ def readinto(self, b):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
Misbehaved(bad_size).read()
|
Misbehaved(bad_size).read()
|
||||||
|
|
||||||
|
def test_RawIOBase_read_gh60107(self):
|
||||||
|
# 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)
|
||||||
|
|
||||||
def test_types_have_dict(self):
|
def test_types_have_dict(self):
|
||||||
test = (
|
test = (
|
||||||
self.IOBase(),
|
self.IOBase(),
|
||||||
|
|
|
||||||
|
|
@ -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`.
|
||||||
|
|
@ -927,33 +927,33 @@ _io__RawIOBase_read_impl(PyObject *self, Py_ssize_t n)
|
||||||
return PyObject_CallMethodNoArgs(self, &_Py_ID(readall));
|
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 = PyByteArray_FromStringAndSize(NULL, n);
|
||||||
if (b == NULL)
|
if (b == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
res = PyObject_CallMethodObjArgs(self, &_Py_ID(readinto), b, NULL);
|
res = PyObject_CallMethodObjArgs(self, &_Py_ID(readinto), b, NULL);
|
||||||
if (res == NULL || res == Py_None) {
|
if (res == NULL || res == Py_None) {
|
||||||
Py_DECREF(b);
|
goto cleanup;
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_ssize_t bytes_filled = PyNumber_AsSsize_t(res, PyExc_ValueError);
|
Py_ssize_t bytes_filled = PyNumber_AsSsize_t(res, PyExc_ValueError);
|
||||||
Py_DECREF(res);
|
Py_CLEAR(res);
|
||||||
if (bytes_filled == -1 && PyErr_Occurred()) {
|
if (bytes_filled == -1 && PyErr_Occurred()) {
|
||||||
Py_DECREF(b);
|
goto cleanup;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
if (bytes_filled < 0 || bytes_filled > n) {
|
if (bytes_filled < 0 || bytes_filled > n) {
|
||||||
Py_DECREF(b);
|
|
||||||
PyErr_Format(PyExc_ValueError,
|
PyErr_Format(PyExc_ValueError,
|
||||||
"readinto returned %zd outside buffer size %zd",
|
"readinto returned %zd outside buffer size %zd",
|
||||||
bytes_filled, n);
|
bytes_filled, n);
|
||||||
return NULL;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
if (PyByteArray_Resize(b, bytes_filled) < 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
res = PyObject_CallMethodNoArgs(b, &_Py_ID(take_bytes));
|
||||||
|
|
||||||
res = PyBytes_FromStringAndSize(PyByteArray_AsString(b), bytes_filled);
|
cleanup:
|
||||||
Py_DECREF(b);
|
Py_DECREF(b);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue