Repository: zarr-developers/zarr-python

Issue:
v3 arrays have no `.chunks` attribute
Similar to: https://github.com/zarr-developers/zarr-python/issues/1924

V2 arrays have a `.chunks` attribute: https://zarr.readthedocs.io/en/stable/_autoapi/zarr.core.Array.html#zarr.core.Array.chunks

V3 arrays don't:

```python
import zarr
arr = zarr.Array.create(store=zarr.store.MemoryStore(), shape=(10,), chunks=(5,), dtype="i4")
arr.chunks
# -> attribute error

# instead it is available at
arr.metadata.chunk_grid.chunk_shape
```

It would be a pretty disruptive API change to remove this. I understand that the ChunkGrid concept makes this more complicated to support. We should reimplement a sane default for this attribute for regular chunk grids. So much code likely relies on this parameter.

Discussion:
rabernat: This is my first PR to the V3 branch.

How are we handling docs? Is there anything to update?
d-v-b: > This is my first PR to the V3 branch.
> 
> How are we handling docs? Is there anything to update?

Docs are a (big) tbd, for the moment code changes alone are fine.

Patch:
diff --git a/src/zarr/array.py b/src/zarr/array.py
index 3e6cf553..698894ba 100644
--- a/src/zarr/array.py
+++ b/src/zarr/array.py
@@ -366,6 +366,15 @@ class AsyncArray:
     def shape(self) -> ChunkCoords:
         return self.metadata.shape
 
+    @property
+    def chunks(self) -> ChunkCoords:
+        if isinstance(self.metadata.chunk_grid, RegularChunkGrid):
+            return self.metadata.chunk_grid.chunk_shape
+        else:
+            raise ValueError(
+                f"chunk attribute is only available for RegularChunkGrid, this array has a {self.metadata.chunk_grid}"
+            )
+
     @property
     def size(self) -> int:
         return np.prod(self.metadata.shape).item()
@@ -641,6 +650,10 @@ class Array:
     def shape(self) -> ChunkCoords:
         return self._async_array.shape
 
+    @property
+    def chunks(self) -> ChunkCoords:
+        return self._async_array.chunks
+
     @property
     def size(self) -> int:
         return self._async_array.size


Test patch:
diff --git a/tests/v3/test_group.py b/tests/v3/test_group.py
index 9ce9b07a..c529e249 100644
--- a/tests/v3/test_group.py
+++ b/tests/v3/test_group.py
@@ -79,7 +79,7 @@ def test_group(store: MemoryStore | LocalStore) -> None:
     assert arr.dtype == data.dtype
 
     # TODO: update this once the array api settles down
-    # assert arr.chunk_shape == (2, 2)
+    assert arr.chunks == (2, 2)
 
     bar2 = foo["bar"]
     assert dict(bar2.attrs) == {"baz": "qux"}