From 49c3955593934a5c93971bd65a490d3f03e09794 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Wed, 2 Jul 2025 08:49:50 -0400 Subject: [PATCH 1/6] Remove breaking check from _make_async --- src/zarr/storage/_fsspec.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/zarr/storage/_fsspec.py b/src/zarr/storage/_fsspec.py index 4f6929456e..59c00655d8 100644 --- a/src/zarr/storage/_fsspec.py +++ b/src/zarr/storage/_fsspec.py @@ -56,11 +56,6 @@ def _make_async(fs: AbstractFileSystem) -> AsyncFileSystem: fs_dict["asynchronous"] = True return fsspec.AbstractFileSystem.from_json(json.dumps(fs_dict)) - # Wrap sync filesystems with the async wrapper - if type(fs) is fsspec.implementations.local.LocalFileSystem and not fs.auto_mkdir: - raise ValueError( - f"LocalFilesystem {fs} was created with auto_mkdir=False but Zarr requires the filesystem to automatically create directories" - ) if fsspec_version < parse_version("2024.12.0"): raise ImportError( f"The filesystem '{fs}' is synchronous, and the required " From 18532314857333baae47b9041070ae5c9319916d Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Wed, 2 Jul 2025 09:03:56 -0400 Subject: [PATCH 2/6] Update expected error --- tests/test_store/test_fsspec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_store/test_fsspec.py b/tests/test_store/test_fsspec.py index 1a989525e3..305dfbcbc9 100644 --- a/tests/test_store/test_fsspec.py +++ b/tests/test_store/test_fsspec.py @@ -365,7 +365,7 @@ def test_open_fsmap_file_raises(tmp_path: pathlib.Path) -> None: fsspec = pytest.importorskip("fsspec.implementations.local") fs = fsspec.LocalFileSystem(auto_mkdir=False) mapper = fs.get_mapper(tmp_path) - with pytest.raises(ValueError, match="LocalFilesystem .*"): + with pytest.raises(FileNotFoundError, match="No such file or directory: .*"): array_roundtrip(mapper) From 1772c894e36b6e4ae22e6f5c813225ae78c14062 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Wed, 2 Jul 2025 10:37:02 -0400 Subject: [PATCH 3/6] Change import structure to protect against AttributeError --- src/zarr/storage/_fsspec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/zarr/storage/_fsspec.py b/src/zarr/storage/_fsspec.py index 59c00655d8..a1b05a7630 100644 --- a/src/zarr/storage/_fsspec.py +++ b/src/zarr/storage/_fsspec.py @@ -62,8 +62,9 @@ def _make_async(fs: AbstractFileSystem) -> AsyncFileSystem: "AsyncFileSystemWrapper is not available. Upgrade fsspec to version " "2024.12.0 or later to enable this functionality." ) + from fsspec.implementations.asyn_wrapper import AsyncFileSystemWrapper - return fsspec.implementations.asyn_wrapper.AsyncFileSystemWrapper(fs, asynchronous=True) + return AsyncFileSystemWrapper(fs, asynchronous=True) class FsspecStore(Store): From cec89ffa15e3704e8d728aa6324c8fdaf5d8c1de Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Thu, 3 Jul 2025 11:25:21 +0200 Subject: [PATCH 4/6] changelog --- changes/3193.bugfix.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changes/3193.bugfix.rst diff --git a/changes/3193.bugfix.rst b/changes/3193.bugfix.rst new file mode 100644 index 0000000000..a6e387c10c --- /dev/null +++ b/changes/3193.bugfix.rst @@ -0,0 +1,2 @@ +Removed an unnecessary check from ``_fsspec._make_async`` that would raise an exception when +creating a read-only store backed by a local file system with ``auto_mkdir`` set to ``False``. \ No newline at end of file From 13238ff76401d32369b785aadc88b037a7d7055f Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Thu, 3 Jul 2025 11:42:32 +0200 Subject: [PATCH 5/6] add test to ensure that we can create a read-only copy of the store with auto_mkdir=False --- tests/test_store/test_fsspec.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_store/test_fsspec.py b/tests/test_store/test_fsspec.py index 305dfbcbc9..51ea16afca 100644 --- a/tests/test_store/test_fsspec.py +++ b/tests/test_store/test_fsspec.py @@ -426,3 +426,13 @@ async def test_delete_dir_wrapped_filesystem(tmp_path: Path) -> None: assert await store.exists("foo-bar/zarr.json") assert not await store.exists("foo/zarr.json") assert not await store.exists("foo/c/0") + + +async def test_with_read_only_auto_mkdir(tmp_path: Path) -> None: + """ + Test that creating a read-only copy of a store backed by the local file system does not error + if auto_mkdir is False. + """ + + store_w = FsspecStore.from_url(f"file://{tmp_path}", storage_options={"auto_mkdir": False}) + _ = store_w.with_read_only() From 46e4148b71331a53cce4f3ae51387c1654f5d0f5 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Thu, 3 Jul 2025 12:27:03 +0200 Subject: [PATCH 6/6] only test if the async wrapper is available --- tests/test_store/test_fsspec.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_store/test_fsspec.py b/tests/test_store/test_fsspec.py index 51ea16afca..026b25f8fc 100644 --- a/tests/test_store/test_fsspec.py +++ b/tests/test_store/test_fsspec.py @@ -428,6 +428,10 @@ async def test_delete_dir_wrapped_filesystem(tmp_path: Path) -> None: assert not await store.exists("foo/c/0") +@pytest.mark.skipif( + parse_version(fsspec.__version__) < parse_version("2024.12.0"), + reason="No AsyncFileSystemWrapper", +) async def test_with_read_only_auto_mkdir(tmp_path: Path) -> None: """ Test that creating a read-only copy of a store backed by the local file system does not error