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