From 9e54553abc64372e6e849a7be1c59ca2c0942655 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Mon, 27 Oct 2025 17:05:34 +0100 Subject: [PATCH 1/3] allow variable chunk sizes for zarr v3 --- xarray/backends/zarr.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xarray/backends/zarr.py b/xarray/backends/zarr.py index fe004c212b6..88c22661404 100644 --- a/xarray/backends/zarr.py +++ b/xarray/backends/zarr.py @@ -279,7 +279,7 @@ async def async_getitem(self, key): ) -def _determine_zarr_chunks(enc_chunks, var_chunks, ndim, name): +def _determine_zarr_chunks(enc_chunks, var_chunks, ndim, name, zarr_format): """ Given encoding chunks (possibly None or []) and variable chunks (possibly None or []). @@ -301,6 +301,9 @@ def _determine_zarr_chunks(enc_chunks, var_chunks, ndim, name): # while dask chunks can be variable sized # https://dask.pydata.org/en/latest/array-design.html#chunks if var_chunks and not enc_chunks: + if zarr_format == 3: + return tuple(var_chunks) + if any(len(set(chunks[:-1])) > 1 for chunks in var_chunks): raise ValueError( "Zarr requires uniform chunk sizes except for final chunk. " @@ -475,6 +478,7 @@ def extract_zarr_variable_encoding( var_chunks=variable.chunks, ndim=variable.ndim, name=name, + zarr_format=zarr_format, ) if _zarr_v3() and chunks is None: chunks = "auto" From ebfe0bee19c6f2aad0fbe818786fe1d108378234 Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Mon, 27 Oct 2025 17:07:39 +0100 Subject: [PATCH 2/3] change the error message to mention zarr v2 --- xarray/backends/zarr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/backends/zarr.py b/xarray/backends/zarr.py index 88c22661404..8201e68e2f1 100644 --- a/xarray/backends/zarr.py +++ b/xarray/backends/zarr.py @@ -306,13 +306,13 @@ def _determine_zarr_chunks(enc_chunks, var_chunks, ndim, name, zarr_format): if any(len(set(chunks[:-1])) > 1 for chunks in var_chunks): raise ValueError( - "Zarr requires uniform chunk sizes except for final chunk. " + "Zarr v2 requires uniform chunk sizes except for final chunk. " f"Variable named {name!r} has incompatible dask chunks: {var_chunks!r}. " "Consider rechunking using `chunk()`." ) if any((chunks[0] < chunks[-1]) for chunks in var_chunks): raise ValueError( - "Final chunk of Zarr array must be the same size or smaller " + "Final chunk of a Zarr v2 array must be the same size or smaller " f"than the first. Variable named {name!r} has incompatible Dask chunks {var_chunks!r}." "Consider either rechunking using `chunk()` or instead deleting " "or modifying `encoding['chunks']`." From e2fd4c3b647b730dc81b0aea539e77ecbc008b9c Mon Sep 17 00:00:00 2001 From: Justus Magin Date: Mon, 27 Oct 2025 17:08:51 +0100 Subject: [PATCH 3/3] infer the chunk shape(s) from the zarr v3 metadata --- xarray/backends/zarr.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/xarray/backends/zarr.py b/xarray/backends/zarr.py index 8201e68e2f1..5d6425b37a0 100644 --- a/xarray/backends/zarr.py +++ b/xarray/backends/zarr.py @@ -857,9 +857,18 @@ def open_store_variable(self, name): ) attributes = dict(attributes) + if hasattr(zarr_array, "metadata"): + chunk_grid = zarr_array.metadata.chunk_grid + chunks = getattr(chunk_grid, "chunk_shapes", None) + # regular chunk grid + if chunks is None: + chunks = chunk_grid.chunk_shape + else: + chunks = zarr_array.chunks + encoding = { - "chunks": zarr_array.chunks, - "preferred_chunks": dict(zip(dimensions, zarr_array.chunks, strict=True)), + "chunks": chunks, + "preferred_chunks": dict(zip(dimensions, chunks, strict=True)), } if _zarr_v3():