From 32eacf4d6575a97fa6317b0dd2fb1c9ff7a07808 Mon Sep 17 00:00:00 2001 From: erichanwang Date: Sat, 8 Aug 2026 19:47:47 -0500 Subject: [PATCH 1/2] ARROW-50831 avoid abort on read-only output buffer --- python/pyarrow/io.pxi | 5 ++++- python/pyarrow/tests/test_io.py | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/python/pyarrow/io.pxi b/python/pyarrow/io.pxi index b648fbf66980..b34849e4ffc7 100644 --- a/python/pyarrow/io.pxi +++ b/python/pyarrow/io.pxi @@ -2934,7 +2934,10 @@ def output_stream(source, compression='detect', buffer_size=None): elif source_path is not None: stream = OSFile(source_path, 'w') elif isinstance(source, (Buffer, memoryview)): - stream = FixedSizeBufferWriter(as_buffer(source)) + source = as_buffer(source) + if not source.is_mutable: + raise ValueError("pa.output_stream() requires a mutable buffer") + stream = FixedSizeBufferWriter(source) elif (hasattr(source, 'write') and hasattr(source, 'close') and hasattr(source, 'closed')): diff --git a/python/pyarrow/tests/test_io.py b/python/pyarrow/tests/test_io.py index 3d4ba997b3ee..ac59fed6288f 100644 --- a/python/pyarrow/tests/test_io.py +++ b/python/pyarrow/tests/test_io.py @@ -2225,6 +2225,15 @@ def test_output_stream_errors(tmpdir): with pytest.raises(ValueError): pa.output_stream(buf, compression="foo") + for arg in [memoryview(b"x"), pa.py_buffer(b"x"), + memoryview(b""), pa.py_buffer(b"")]: + with pytest.raises(ValueError, match="requires a mutable buffer"): + pa.output_stream(arg) + + for arg in [memoryview(bytearray(1)), pa.py_buffer(bytearray(1))]: + with pa.output_stream(arg) as stream: + stream.write(b"x") + for arg in [bytearray(), StringIO()]: with pytest.raises(TypeError): pa.output_stream(arg) From 6023209975b5e43fb0470383acf482ef667462b5 Mon Sep 17 00:00:00 2001 From: erichanwang Date: Sun, 9 Aug 2026 16:34:43 -0500 Subject: [PATCH 2/2] GH-50831: [Python] Guard FixedSizeBufferWriter against read-only buffers --- python/pyarrow/io.pxi | 7 +++---- python/pyarrow/tests/test_io.py | 4 ++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/python/pyarrow/io.pxi b/python/pyarrow/io.pxi index b34849e4ffc7..b709c1391de1 100644 --- a/python/pyarrow/io.pxi +++ b/python/pyarrow/io.pxi @@ -1336,6 +1336,8 @@ cdef class FixedSizeBufferWriter(NativeFile): """ def __cinit__(self, Buffer buffer): + if not buffer.is_mutable: + raise ValueError("pa.FixedSizeBufferWriter() requires a mutable buffer") self.output_stream.reset(new CFixedSizeBufferWriter(buffer.buffer)) self.is_writable = True @@ -2934,10 +2936,7 @@ def output_stream(source, compression='detect', buffer_size=None): elif source_path is not None: stream = OSFile(source_path, 'w') elif isinstance(source, (Buffer, memoryview)): - source = as_buffer(source) - if not source.is_mutable: - raise ValueError("pa.output_stream() requires a mutable buffer") - stream = FixedSizeBufferWriter(source) + stream = FixedSizeBufferWriter(as_buffer(source)) elif (hasattr(source, 'write') and hasattr(source, 'close') and hasattr(source, 'closed')): diff --git a/python/pyarrow/tests/test_io.py b/python/pyarrow/tests/test_io.py index ac59fed6288f..84726d4bc011 100644 --- a/python/pyarrow/tests/test_io.py +++ b/python/pyarrow/tests/test_io.py @@ -2230,6 +2230,10 @@ def test_output_stream_errors(tmpdir): with pytest.raises(ValueError, match="requires a mutable buffer"): pa.output_stream(arg) + for arg in [pa.py_buffer(b"x"), pa.py_buffer(b"")]: + with pytest.raises(ValueError, match="requires a mutable buffer"): + pa.FixedSizeBufferWriter(arg) + for arg in [memoryview(bytearray(1)), pa.py_buffer(bytearray(1))]: with pa.output_stream(arg) as stream: stream.write(b"x")