Skip to content

Commit aad1fc7

Browse files
author
Leo Ji
committed
fix: convert zarr Array to numpy before __setitem__ async dispatch
When the value passed to Array.__setitem__ is itself a zarr Array, the codec pipeline later calls value[...] from inside an already-running async event loop, which raises SyncError. Convert the value to a NumPy array eagerly before entering the async context to avoid this. Closes #3611 Made-with: Cursor
1 parent c9b534a commit aad1fc7

File tree

3 files changed

+25
-0
lines changed

3 files changed

+25
-0
lines changed

changes/3611.bugfix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix `SyncError` raised when assigning a `zarr.Array` as the value in a `__setitem__` call (e.g. `dst[:] = src` where `src` is a zarr array). The source array is now converted to a NumPy array before entering the async codec pipeline.

src/zarr/core/array.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2929,6 +2929,12 @@ def __setitem__(self, selection: Selection, value: npt.ArrayLike) -> None:
29292929
[blocks][zarr.Array.blocks], [__getitem__][zarr.Array.__getitem__]
29302930
29312931
"""
2932+
# Converting a zarr Array to numpy here avoids a SyncError that occurs when
2933+
# value.__getitem__ is called inside the async codec pipeline (which already
2934+
# runs within a running event loop). np.asarray triggers Array.__array__,
2935+
# which reads the data synchronously before we enter the async context.
2936+
if isinstance(value, Array):
2937+
value = np.asarray(value)
29322938
fields, pure_selection = pop_fields(selection)
29332939
if is_pure_fancy_indexing(pure_selection, self.ndim):
29342940
self.vindex[cast("CoordinateSelection | MaskSelection", selection)] = value

tests/test_indexing.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,24 @@ def test_orthogonal_indexing_fallback_on_getitem_2d(
444444

445445

446446
@pytest.mark.skip(reason="fails on ubuntu, windows; numpy=2.2; in CI")
447+
def test_setitem_zarr_array_as_value() -> None:
448+
# Regression test for https://github.com/zarr-developers/zarr-python/issues/3611
449+
# Assigning a zarr Array as the value used to raise
450+
# SyncError("Calling sync() from within a running loop") because the codec
451+
# pipeline tried to index the zarr array inside an already-running async loop.
452+
src = zarr.array(np.arange(10), chunks=(5,))
453+
dst = zarr.zeros(10, chunks=(5,), dtype=src.dtype)
454+
455+
# Full assignment
456+
dst[:] = src
457+
assert_array_equal(dst[:], np.arange(10))
458+
459+
# Slice assignment
460+
dst2 = zarr.zeros(10, chunks=(5,), dtype=src.dtype)
461+
dst2[2:7] = src[2:7]
462+
assert_array_equal(dst2[2:7], np.arange(2, 7))
463+
464+
447465
def test_setitem_repeated_index():
448466
array = zarr.array(data=np.zeros((4,)), chunks=(1,))
449467
indexer = np.array([-1, -1, 0, 0])

0 commit comments

Comments
 (0)