Skip to content

Commit ab5925b

Browse files
moradologyjhammand-v-b
authored
Use removeprefix rather than replace to avoid separator deletion (#2778)
* Use removeprefix rather than replace to avoid separator deletion * Test list behavior with empty paths --------- Co-authored-by: Joe Hamman <[email protected]> Co-authored-by: Davis Bennett <[email protected]>
1 parent bb2c4fe commit ab5925b

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

changes/2778.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Use removeprefix rather than replace when removing filename prefixes in `FsspecStore.list`

src/zarr/storage/_fsspec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ async def set_partial_values(
341341
async def list(self) -> AsyncIterator[str]:
342342
# docstring inherited
343343
allfiles = await self.fs._find(self.path, detail=False, withdirs=False)
344-
for onefile in (a.replace(self.path + "/", "") for a in allfiles):
344+
for onefile in (a.removeprefix(self.path + "/") for a in allfiles):
345345
yield onefile
346346

347347
async def list_dir(self, prefix: str) -> AsyncIterator[str]:

src/zarr/testing/store.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,37 @@ async def test_list_prefix(self, store: S) -> None:
400400
expected = tuple(sorted(expected))
401401
assert observed == expected
402402

403+
async def test_list_empty_path(self, store: S) -> None:
404+
"""
405+
Verify that list and list_prefix work correctly when path is an empty string,
406+
i.e. no unwanted replacement occurs.
407+
"""
408+
data = self.buffer_cls.from_bytes(b"")
409+
store_dict = {
410+
"foo/bar/zarr.json": data,
411+
"foo/bar/c/1": data,
412+
"foo/baz/c/0": data,
413+
}
414+
await store._set_many(store_dict.items())
415+
416+
# Test list()
417+
observed_list = await _collect_aiterator(store.list())
418+
observed_list_sorted = sorted(observed_list)
419+
expected_list_sorted = sorted(store_dict.keys())
420+
assert observed_list_sorted == expected_list_sorted
421+
422+
# Test list_prefix() with an empty prefix
423+
observed_prefix_empty = await _collect_aiterator(store.list_prefix(""))
424+
observed_prefix_empty_sorted = sorted(observed_prefix_empty)
425+
expected_prefix_empty_sorted = sorted(store_dict.keys())
426+
assert observed_prefix_empty_sorted == expected_prefix_empty_sorted
427+
428+
# Test list_prefix() with a non-empty prefix
429+
observed_prefix = await _collect_aiterator(store.list_prefix("foo/bar/"))
430+
observed_prefix_sorted = sorted(observed_prefix)
431+
expected_prefix_sorted = sorted(k for k in store_dict if k.startswith("foo/bar/"))
432+
assert observed_prefix_sorted == expected_prefix_sorted
433+
403434
async def test_list_dir(self, store: S) -> None:
404435
root = "foo"
405436
store_dict = {

0 commit comments

Comments
 (0)