This is an automated email from the ASF dual-hosted git repository.
raulcd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new b3a9c444471 GH-51044: [Python] Reject read-only readinto destinations
(#51126)
b3a9c444471 is described below
commit b3a9c4444712046a2608aed174ccb79ca05c51bc
Author: carrerasdarren-cell <[email protected]>
AuthorDate: Wed Sep 9 05:59:59 2026 -0400
GH-51044: [Python] Reject read-only readinto destinations (#51126)
### Rationale for this change
`BufferReader.readinto()` currently segfaults when passed a read-only
destination such as `bytes` or a read-only `memoryview`. `py_buffer()` creates
an immutable Arrow buffer, `mutable_data()` returns a null pointer, and the C++
read path attempts to copy into that pointer. A Python API misuse should raise
a Python exception rather than terminate the interpreter.
### What changes are included in this PR?
- Validate that a `NativeFile.readinto()` destination is mutable before
obtaining its writable pointer.
- Raise `TypeError` for immutable destinations.
- Add regression coverage for `bytes` and read-only `memoryview`
destinations.
### Are these changes tested?
Yes. The focused `readinto` tests pass against a locally compiled patched
`pyarrow.lib`. The stock 25.0.1 wheel exits with status 139 for the issue
reproducer, while the patched build raises the expected `TypeError`; the
writable `bytearray` control continues to read successfully.
### Are there any user-facing changes?
Yes. Passing a read-only destination to `NativeFile.readinto()` now raises
`TypeError` instead of terminating the interpreter. Writable-buffer behavior is
unchanged.
Fixes #51044.
This contribution was developed with assistance from OpenAI Codex. I
reviewed, tested, and take responsibility for the patch and this description.
* GitHub Issue: #51044
Authored-by: Darren Carreras
<[email protected]>
Signed-off-by: Raúl Cumplido <[email protected]>
---
python/pyarrow/io.pxi | 2 ++
python/pyarrow/tests/test_io.py | 7 +++++++
2 files changed, 9 insertions(+)
diff --git a/python/pyarrow/io.pxi b/python/pyarrow/io.pxi
index b648fbf6698..32f7bc26b1f 100644
--- a/python/pyarrow/io.pxi
+++ b/python/pyarrow/io.pxi
@@ -548,6 +548,8 @@ cdef class NativeFile(_Weakrefable):
handle = self.get_input_stream()
py_buf = py_buffer(b)
+ if not py_buf.buffer.get().is_mutable():
+ raise TypeError("readinto() argument must be a writable buffer")
buf_len = py_buf.size
buf = py_buf.buffer.get().mutable_data()
diff --git a/python/pyarrow/tests/test_io.py b/python/pyarrow/tests/test_io.py
index 8494a0ee66b..2cebaabbcbd 100644
--- a/python/pyarrow/tests/test_io.py
+++ b/python/pyarrow/tests/test_io.py
@@ -1024,6 +1024,13 @@ def test_nativefile_write_memoryview():
assert buf.to_pybytes() == data * 3
[email protected]("dst_buf", [b"a", memoryview(b"a")])
+def test_native_file_readinto_rejects_readonly_buffer(dst_buf):
+ with pa.BufferReader(b"x") as f:
+ with pytest.raises(TypeError, match="writable buffer"):
+ f.readinto(dst_buf)
+
+
# ----------------------------------------------------------------------
# Mock output stream