From 505c8bcf190b1925398af7341c0d8b91886bd948 Mon Sep 17 00:00:00 2001 From: herui Date: Wed, 19 Aug 2026 20:49:19 +0800 Subject: [PATCH] fix: raise UnsupportedOperation exception in fsspec.BufferedFile impl - zipfile.ZipFile.__init__ only catch `OSError` - io.UnsupportedOperation inherits `OSError` and `ValueError`, may be helpful - add a test simple test at tests/test_fsspec.py --- obstore/python/obstore/fsspec.py | 11 ++++++----- tests/test_fsspec.py | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/obstore/python/obstore/fsspec.py b/obstore/python/obstore/fsspec.py index d6c76a92..ef22e360 100644 --- a/obstore/python/obstore/fsspec.py +++ b/obstore/python/obstore/fsspec.py @@ -36,6 +36,7 @@ import warnings from collections import defaultdict from functools import cached_property, lru_cache +from io import UnsupportedOperation from pathlib import Path from typing import TYPE_CHECKING, Literal, overload from urllib.parse import urlparse @@ -721,7 +722,7 @@ def read(self, length: int = -1) -> bytes: """ if self.mode != "rb": - raise ValueError("File not in read mode") + raise UnsupportedOperation("File not in read mode") if length < 0: length = self.size - self.tell() if self.closed: @@ -736,7 +737,7 @@ def read(self, length: int = -1) -> bytes: def readline(self) -> bytes: """Read until first occurrence of newline character.""" if self.mode != "rb": - raise ValueError("File not in read mode") + raise UnsupportedOperation("File not in read mode") out = self._reader.readline() return out.to_bytes() @@ -744,7 +745,7 @@ def readline(self) -> bytes: def readlines(self) -> list[bytes]: """Return all data, split by the newline character.""" if self.mode != "rb": - raise ValueError("File not in read mode") + raise UnsupportedOperation("File not in read mode") out = self._reader.readlines() return [b.to_bytes() for b in out] @@ -775,7 +776,7 @@ def seek(self, loc: int, whence: int = 0) -> int: """ if self.mode != "rb": - raise ValueError("Seek only available in read mode.") + raise UnsupportedOperation("Seek only available in read mode.") return self._reader.seek(loc, whence) @@ -787,7 +788,7 @@ def write(self, data: bytes) -> int: """ if not self.writable(): - raise ValueError("File not in write mode") + raise UnsupportedOperation("File not in write mode") if self.closed: raise ValueError("I/O operation on closed file.") if self.forced: diff --git a/tests/test_fsspec.py b/tests/test_fsspec.py index 3da2f5d3..779dfc1f 100644 --- a/tests/test_fsspec.py +++ b/tests/test_fsspec.py @@ -2,6 +2,7 @@ import gc import os +import zipfile from pathlib import Path from tempfile import TemporaryDirectory from typing import TYPE_CHECKING @@ -172,6 +173,24 @@ def test_buffered_file_forwards_size_to_open_reader(): assert len(data) == 500 +def test_zipfile_write_mode(): + """Ensure a write-mode file can fall back to ZipFile non-seekable code path.""" + register("memory") + fs: FsspecStore = fsspec.filesystem("memory") + + with ( + fs._open("archive.zip", mode="wb") as filelike, + zipfile.ZipFile(filelike, "w") as zf, + ): + zf.writestr("hello.txt", "hello world") + + with ( + fs._open("archive.zip") as filelike, + zipfile.ZipFile(filelike) as zf, + ): + assert zf.read("hello.txt") == b"hello world" + + def test_construct_store_cache_diff_bucket_name( minio_bucket: tuple[S3Config, ClientConfig], ):