Repository: zarr-developers/zarr-python

Issue:
NestedDirectoryStore.listdir returns invalid chunk keys
### Zarr version

2.13.6 and current head 0bf0b3b3444dd57debc8a6b5eacb6eb5c082c668

### Numcodecs version

0.11.0

### Python Version

3.10.6

### Operating System

Mac

### Installation

pip install zarr and built from source

### Description

Calling listdir on NestedDirectoryStore returns keys with a '.' dimension separator even though the store does not support this separator.

I believe the issue is this line:
https://github.com/zarr-developers/zarr-python/blob/0bf0b3b3444dd57debc8a6b5eacb6eb5c082c668/zarr/storage.py#L1207

I will open a PR with a fix and update to the tests.

### Steps to reproduce


```python
import zarr

store = zarr.storage.NestedDirectoryStore('tmp')
z = zarr.zeros((10, 10), chunks=(5, 5), store=store)
z[:] = 1
keys = zarr.storage.listdir(store)
print(f"Store keys: {keys}")
```
Returns
```
Store keys: ['.zarray', '0.0', '0.1', '1.0', '1.1']
```
However attempts to get data for those keys returns None:
```python
print(store.get('0.0'))
```
Returns
```
None
```

### Additional output

_No response_

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/zarr-developers/zarr-python/pull/1335?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers) Report
> Merging [#1335](https://codecov.io/gh/zarr-developers/zarr-python/pull/1335?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers) (5861200) into [main](https://codecov.io/gh/zarr-developers/zarr-python/commit/0bf0b3b3444dd57debc8a6b5eacb6eb5c082c668?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers) (0bf0b3b) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff            @@
##              main     #1335   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           35        35           
  Lines        14404     14406    +2     
=========================================
+ Hits         14404     14406    +2     
```

| [Impacted Files](https://codecov.io/gh/zarr-developers/zarr-python/pull/1335?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers) | Coverage Δ | |
|---|---|---|
| [zarr/storage.py](https://codecov.io/gh/zarr-developers/zarr-python/pull/1335?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers#diff-emFyci9zdG9yYWdlLnB5) | `100.00% <100.00%> (ø)` | |
| [zarr/tests/test\_storage.py](https://codecov.io/gh/zarr-developers/zarr-python/pull/1335?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers#diff-emFyci90ZXN0cy90ZXN0X3N0b3JhZ2UucHk=) | `100.00% <100.00%> (ø)` | |
| [zarr/util.py](https://codecov.io/gh/zarr-developers/zarr-python/pull/1335?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers#diff-emFyci91dGlsLnB5) | `100.00% <0.00%> (ø)` | |
| [zarr/indexing.py](https://codecov.io/gh/zarr-developers/zarr-python/pull/1335?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers#diff-emFyci9pbmRleGluZy5weQ==) | `100.00% <0.00%> (ø)` | |
| [zarr/tests/test\_hierarchy.py](https://codecov.io/gh/zarr-developers/zarr-python/pull/1335?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers#diff-emFyci90ZXN0cy90ZXN0X2hpZXJhcmNoeS5weQ==) | `100.00% <0.00%> (ø)` | |


Patch:
diff --git a/docs/release.rst b/docs/release.rst
index f82c0730..5ebd77c9 100644
--- a/docs/release.rst
+++ b/docs/release.rst
@@ -28,6 +28,8 @@ Bug fixes
 
 * Ensure contiguous data is give to ``FSStore``. Only copying if needed.
   By :user:`Mads R. B. Kristensen <madsbk>` :issue:`1285`.
+* NestedDirectoryStore.listdir now returns chunk keys with the correct '/' dimension_separator.
+  By :user:`Brett Graham <braingram>` :issue:`1334`.
 
 .. _release_2.13.6:
 
diff --git a/zarr/storage.py b/zarr/storage.py
index 5f7b991a..fae95307 100644
--- a/zarr/storage.py
+++ b/zarr/storage.py
@@ -1204,7 +1204,9 @@ class DirectoryStore(Store):
                         for file_name in file_names:
                             file_path = os.path.join(dir_path, file_name)
                             rel_path = file_path.split(root_path + os.path.sep)[1]
-                            new_children.append(rel_path.replace(os.path.sep, '.'))
+                            new_children.append(rel_path.replace(
+                                os.path.sep,
+                                self._dimension_separator or '.'))
                 else:
                     new_children.append(entry)
             return sorted(new_children)


Test patch:
diff --git a/zarr/tests/test_storage.py b/zarr/tests/test_storage.py
index 7c23735f..0b21dfbd 100644
--- a/zarr/tests/test_storage.py
+++ b/zarr/tests/test_storage.py
@@ -1442,6 +1442,13 @@ class TestNestedDirectoryStore(TestDirectoryStore):
         store[self.root + '42'] = b'zzz'
         assert b'zzz' == store[self.root + '42']
 
+    def test_listdir(self):
+        store = self.create_store()
+        z = zarr.zeros((10, 10), chunks=(5, 5), store=store)
+        z[:] = 1  # write to all chunks
+        for k in store.listdir():
+            assert store.get(k) is not None
+
 
 class TestNestedDirectoryStoreNone: