From ee63389c27c46cf7e2270be78a5685f1a96442e3 Mon Sep 17 00:00:00 2001 From: ZHANG YU <182837830+xixihaha-ha@users.noreply.github.com> Date: Tue, 3 Nov 2026 11:34:12 +0800 Subject: [PATCH] Keep caller-owned binary streams open after reading ASCII PLY --- plyfile.py | 6 +++++- test/test_stream_ownership.py | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 test/test_stream_ownership.py diff --git a/plyfile.py b/plyfile.py index 6e4fa52..780dc65 100644 --- a/plyfile.py +++ b/plyfile.py @@ -155,6 +155,7 @@ def read(stream, mmap='c', known_list_len={}): indicates binary encoding. """ (must_close, stream) = _open_stream(stream, 'read') + text_wrapper = None try: data = PlyData._parse_header(stream) if isinstance(stream.read(0), str): @@ -165,13 +166,16 @@ def read(stream, mmap='c', known_list_len={}): "from text stream") else: if data.text: - data_stream = _io.TextIOWrapper(stream, 'ascii') + text_wrapper = _io.TextIOWrapper(stream, 'ascii') + data_stream = text_wrapper else: data_stream = stream for elt in data: elt._read(data_stream, data.text, data.byte_order, mmap, known_list_len=known_list_len.get(elt.name, {})) finally: + if text_wrapper is not None: + text_wrapper.detach() if must_close: stream.close() diff --git a/test/test_stream_ownership.py b/test/test_stream_ownership.py new file mode 100644 index 0000000..ba5da0a --- /dev/null +++ b/test/test_stream_ownership.py @@ -0,0 +1,22 @@ +from io import BytesIO + +import pytest + +from plyfile import PlyData, PlyElementParseError + + +@pytest.mark.parametrize('valid', [True, False]) +def test_read_ascii_leaves_caller_binary_stream_open(valid): + payload = (b'ply\nformat ascii 1.0\nelement vertex 1\n' + b'property float x\nend_header\n') + payload += b'1\n' if valid else b'invalid\n' + stream = BytesIO(payload) + if valid: + data = PlyData.read(stream) + assert data['vertex']['x'][0] == 1 + else: + with pytest.raises(PlyElementParseError): + PlyData.read(stream) + assert not stream.closed + stream.seek(0) + assert stream.read() == payload