You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Remove copy from bytearray to bytes in rawiobase_read() implementation #60107
It works as along as the bytes object cannot leak to Python code, (imagine a custom readinto() method which plays with gc.get_referrers, then calls hash(b)...)
This is OK with this patch.
sbt commented on Sep 17, 2012
sbtmannequin
MannequinAuthor
More actions
New patch which checks the refcount of the memoryview and bytes object after calling readinto().
If either refcount is larger than the expected value of 1, then the data is copied rather than resized.
If either refcount is larger than the expected value of 1, then the
data is copied rather than resized.
I think that's a useless precaution. The bytes object cannot "leak" since you are using PyMemoryView_FromMemory(), which doesn't know about the original object.
Out of curiousity, have you done any benchmarks?
sbt commented on Sep 17, 2012
sbtmannequin
MannequinAuthor
More actions
I think that's a useless precaution. The bytes object cannot "leak"
since you are using PyMemoryView_FromMemory(), which doesn't know about
the original object.
The bytes object cannot "leak" so, as you say, checking that refcount is pointless. But the view might "leak", and since it does not own a reference to the base object we have a problem: we can't deallocate the bytes object for fear of breaking the view.
It looks like objects returned by PyMemoryView_FromMemory() must never be allowed to "leak", so I am not sure there are many circumstances in which PyMemoryView_FromMemory() is safe to use.
Perhaps using PyBuffer_FillInfo() and PyMemory_FromBuffer() would keep alive the bytes object while the view is alive, without letting the bytes object "leak".
The bytes object cannot "leak" so, as you say, checking that refcount
is pointless. But the view might "leak", and since it does not own a
reference to the base object we have a problem: we can't deallocate the
bytes object for fear of breaking the view.
Indeed, that's a problem (but your patch does deallocate the bytes object).
It's quite fishy, I'm not sure how to solve the issue cleanly. Stefan, do you have an idea?
skrah commented on Sep 18, 2012
skrahmannequin
Mannequin
More actions
So the problem is that readinto(view) might result in several references
to view? I don't think that can be solved on the memoryview side.
Then the view owns a reference to the bytes object. But that does not
solve the problem that writable memoryviews based on a readonly object
might be hanging around.
sbt commented on Sep 18, 2012
sbtmannequin
MannequinAuthor
More actions
Then the view owns a reference to the bytes object. But that does not
solve the problem that writable memoryviews based on a readonly object
might be hanging around.
Would attempts to access a "leaked" reference to view now result in ValueError("operation forbidden on released memoryview object")? If so then I think this would be safe.
sbt commented on Sep 18, 2012
sbtmannequin
MannequinAuthor
More actions
The current non-test uses of PyMemoryView_FromBuffer() are in _io.BufferedReader.read(), _io.BufferedWriter.write(), PyUnicode_Decode().
It looks like they can each be made to leak a memoryview that references a deallocated buffer. (Maybe the answer is Don't Do That.)
I am rather confused about the ownership semantics when one uses PyMemoryView_FromBuffer().
It looks as though PyMemoryView_FromBuffer() "steals" ownership of the buffer since, when the associated _PyManagedBufferObject is garbage collected, PyBuffer_Release() is called on its copy of the buffer info. However, the _PyManagedBufferObject does not own a reference of the base object, so one still needs to decref the base object (at some time when it is safe to do so).
So am I right in thinking that
PyObject_GetBuffer(obj, &buf, ...);
view=PyMemoryView_FromBuffer(&buf); // view->master owns the buffer, but view->master->obj == NULL
...
Py_DECREF(view); // releases buffer (assuming no other exports)Py_XDECREF(buf.obj);
has balanced refcounting and is more or less equivalent to
Would attempts to access a "leaked" reference to view now result in ValueError("operation forbidden on released memoryview object")? If so then I think this would be safe.
You would need to call memory_release(). Perhaps we can just expose it on the
C-API level as PyMemoryView_Release().
IMO the use of PyObject_GetBuffer() should be discouraged. The semantics
aren't clear (see bpo-15821). I'd suggest using:
A buffer provider is involved (the case here):
PyMemoryView_From Object()
A piece of memory needs to be wrapped temporarily and no references
to the memoryview are "leaked" on the Python level:
PyMemoryView_FromMemory()
A piece of memory needs to be packaged as a memoryview with automatic
cleanup in mbuf_dealloc():
PyMemoryView_FromBufferWithCleanup() (proposed in msg169613)
So I think the combination of PyMemoryView_FromObject() with a call to
PyMemoryView_Release() should indeed work here.
Another approach here is to make it so bytearray can be converted to bytes without a copy. Looking to see if can use _io.BytesIO in this case / performance is reasonable with that which already exposes the "mutable buffer" -> bytes without copying.
changed the title [-]Make rawiobase_read() read directly to bytes object[/-][+]Remove copy from `bytearray` to `bytes` in rawiobase_read() implementation[/+]on Nov 13, 2025
Activity
sbt commentedon Sep 10, 2012
Currently rawiobase_read() reads to a bytearray object and then copies the data to a bytes object.
There is a TODO comment saying that the bytes object should be created directly. The attached patch does that.
amauryfa commentedon Sep 10, 2012
It works as along as the bytes object cannot leak to Python code, (imagine a custom readinto() method which plays with gc.get_referrers, then calls hash(b)...)
This is OK with this patch.
sbt commentedon Sep 17, 2012
New patch which checks the refcount of the memoryview and bytes object after calling readinto().
If either refcount is larger than the expected value of 1, then the data is copied rather than resized.
pitrou commentedon Sep 17, 2012
I think that's a useless precaution. The bytes object cannot "leak" since you are using PyMemoryView_FromMemory(), which doesn't know about the original object.
Out of curiousity, have you done any benchmarks?
sbt commentedon Sep 17, 2012
The bytes object cannot "leak" so, as you say, checking that refcount is pointless. But the view might "leak", and since it does not own a reference to the base object we have a problem: we can't deallocate the bytes object for fear of breaking the view.
It looks like objects returned by PyMemoryView_FromMemory() must never be allowed to "leak", so I am not sure there are many circumstances in which PyMemoryView_FromMemory() is safe to use.
Perhaps using PyBuffer_FillInfo() and PyMemory_FromBuffer() would keep alive the bytes object while the view is alive, without letting the bytes object "leak".
No.
pitrou commentedon Sep 17, 2012
Indeed, that's a problem (but your patch does deallocate the bytes object).
It's quite fishy, I'm not sure how to solve the issue cleanly. Stefan, do you have an idea?
skrah commentedon Sep 18, 2012
So the problem is that readinto(view) might result in several references
to view? I don't think that can be solved on the memoryview side.
One could do:
[...]
Then the view owns a reference to the bytes object. But that does not
solve the problem that writable memoryviews based on a readonly object
might be hanging around.
sbt commentedon Sep 18, 2012
How about doing
Would attempts to access a "leaked" reference to view now result in ValueError("operation forbidden on released memoryview object")? If so then I think this would be safe.
sbt commentedon Sep 18, 2012
The current non-test uses of PyMemoryView_FromBuffer() are in _io.BufferedReader.read(), _io.BufferedWriter.write(), PyUnicode_Decode().
It looks like they can each be made to leak a memoryview that references a deallocated buffer. (Maybe the answer is Don't Do That.)
sbt commentedon Sep 18, 2012
I am rather confused about the ownership semantics when one uses PyMemoryView_FromBuffer().
It looks as though PyMemoryView_FromBuffer() "steals" ownership of the buffer since, when the associated _PyManagedBufferObject is garbage collected, PyBuffer_Release() is called on its copy of the buffer info. However, the _PyManagedBufferObject does not own a reference of the base object, so one still needs to decref the base object (at some time when it is safe to do so).
So am I right in thinking that
has balanced refcounting and is more or less equivalent to
The documentation is not very helpful. It just says that calls to PyObject_GetBuffer() must be matched with calls to PyBuffer_Release().
skrah commentedon Sep 18, 2012
Richard Oudkerk <report@bugs.python.org> wrote:
You would need to call memory_release(). Perhaps we can just expose it on the
C-API level as PyMemoryView_Release().
IMO the use of PyObject_GetBuffer() should be discouraged. The semantics
aren't clear (see bpo-15821). I'd suggest using:
A buffer provider is involved (the case here):
PyMemoryView_From Object()
A piece of memory needs to be wrapped temporarily and no references
to the memoryview are "leaked" on the Python level:
PyMemoryView_FromMemory()A piece of memory needs to be packaged as a memoryview with automatic
cleanup in mbuf_dealloc():
PyMemoryView_FromBufferWithCleanup() (proposed in msg169613)
So I think the combination of PyMemoryView_FromObject() with a call to
PyMemoryView_Release() should indeed work here.
skrah commentedon Sep 18, 2012
Richard Oudkerk <report@bugs.python.org> wrote:
Yes, we need to sort that out, see bpo-15821.
7 remaining items
BreamoreBoy commentedon Jul 16, 2014
Please note this is also referred to from bpo-15994.
cmaloney commentedon May 19, 2025
Another approach here is to make it so
bytearraycan be converted to bytes without a copy. Looking to see if can use_io.BytesIOin this case / performance is reasonable with that which already exposes the "mutable buffer" ->byteswithout copying.pythongh-60107: pythongh-138616: Minor code changes
pythongh-60107: Change blurb_it
pythongh-60107: Tests for readinto
pythongh-60107: Remove a copy from RawIOBase.read
[-]Make rawiobase_read() read directly to bytes object[/-][+]Remove copy from `bytearray` to `bytes` in rawiobase_read() implementation[/+]gh-60107: Remove a copy from RawIOBase.read (#141532)
pythongh-60107: Remove a copy from RawIOBase.read (python#141532)
pythongh-60107: Remove a copy from RawIOBase.read (python#141532)