From afd95d9f7c1927b8ce3f93cca7586c1a1ba581b1 Mon Sep 17 00:00:00 2001 From: Claude Subagent Date: Sat, 21 Mar 2026 12:01:49 -0700 Subject: [PATCH] fix #3773: Fix divergent behavior between MemoryStore and LocalStore list_prefix --- changes/3773.bugfix.md | 5 +++++ src/zarr/storage/_local.py | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 changes/3773.bugfix.md diff --git a/changes/3773.bugfix.md b/changes/3773.bugfix.md new file mode 100644 index 0000000000..60d8fa1594 --- /dev/null +++ b/changes/3773.bugfix.md @@ -0,0 +1,5 @@ +Fix divergent behavior between `MemoryStore` and `LocalStore` `list_prefix` methods. + +Both stores now consistently use string prefix matching (checking if keys start with the given prefix string), +rather than `LocalStore` treating the prefix as a filesystem directory path. This ensures consistent +behavior across different store implementations and aligns with the documented behavior of `list_prefix`. diff --git a/src/zarr/storage/_local.py b/src/zarr/storage/_local.py index 80233a112d..f3866c3f44 100644 --- a/src/zarr/storage/_local.py +++ b/src/zarr/storage/_local.py @@ -290,11 +290,14 @@ async def list(self) -> AsyncIterator[str]: async def list_prefix(self, prefix: str) -> AsyncIterator[str]: # docstring inherited + # Use string prefix matching to be consistent with MemoryStore behavior. + # The prefix should match keys as strings, not as filesystem paths. to_strip = self.root.as_posix() + "/" - prefix = prefix.rstrip("/") - for p in (self.root / prefix).rglob("*"): + for p in list(self.root.rglob("*")): if p.is_file(): - yield p.as_posix().replace(to_strip, "") + key = p.as_posix().replace(to_strip, "") + if key.startswith(prefix): + yield key async def list_dir(self, prefix: str) -> AsyncIterator[str]: # docstring inherited