Skip to content
Merged
1 change: 1 addition & 0 deletions changes/2851.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a bug when setting values of a smaller last chunk.
25 changes: 14 additions & 11 deletions src/zarr/core/codec_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,17 +296,6 @@ def _merge_chunk_array(
is_complete_chunk: bool,
drop_axes: tuple[int, ...],
) -> NDBuffer:
if is_complete_chunk and value.shape == chunk_spec.shape:
return value
if existing_chunk_array is None:
chunk_array = chunk_spec.prototype.nd_buffer.create(
shape=chunk_spec.shape,
dtype=chunk_spec.dtype,
order=chunk_spec.order,
fill_value=fill_value_or_default(chunk_spec),
)
else:
chunk_array = existing_chunk_array.copy() # make a writable copy
if chunk_selection == () or is_scalar(value.as_ndarray_like(), chunk_spec.dtype):
chunk_value = value
else:
Expand All @@ -320,6 +309,20 @@ def _merge_chunk_array(
for idx in range(chunk_spec.ndim)
)
chunk_value = chunk_value[item]
if is_complete_chunk and chunk_value.shape == chunk_spec.shape:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will fail for scalars, need a test for that too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it works for scalars, because chunk_value is now NDBuffer which does have shape. I added a test.

# TODO: For the last chunk, we could have is_complete_chunk=True
# that is smaller than the chunk_spec.shape but this throws
# an error in the _decode_single
return chunk_value
if existing_chunk_array is None:
chunk_array = chunk_spec.prototype.nd_buffer.create(
shape=chunk_spec.shape,
dtype=chunk_spec.dtype,
order=chunk_spec.order,
fill_value=fill_value_or_default(chunk_spec),
)
else:
chunk_array = existing_chunk_array.copy() # make a writable copy
chunk_array[chunk_selection] = chunk_value
return chunk_array

Expand Down
9 changes: 9 additions & 0 deletions tests/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,15 @@ def test_set_orthogonal_selection_1d(store: StorePath) -> None:
_test_set_orthogonal_selection(v, a, z, selection)


def test_set_orthogonal_selection_1d_last_two_chunks():
# regression test for GH2849
g = zarr.open_group("foo.zarr", zarr_format=3, mode="w")
a = g.create_array("bar", shape=(10,), chunks=(3,), dtype=int)
data = np.array([7, 8, 9])
a[slice(7, 10)] = data
np.testing.assert_array_equal(a[slice(7, 10)], data)


def _test_set_orthogonal_selection_2d(
v: npt.NDArray[np.int_],
a: npt.NDArray[np.int_],
Expand Down