Skip to content

Commit fb552eb

Browse files
committed
test: add end-to-end regression test for structured-dtype fills in sharded arrays, and changelog entry
The new test exercises the sharding codec's chunk-spec caches with an unhashable np.void fill value (zarr-developers#3054), which the ArraySpec test suite only covers at the unit level. Assisted-by: ClaudeCode:claude-fable-5
1 parent a73ef1a commit fb552eb

2 files changed

Lines changed: 26 additions & 0 deletions

File tree

‎changes/4183.bugfix.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed `TypeError: unhashable type: 'writeable void-scalar'` when writing to sharded arrays whose fill value is a `np.void` scalar, e.g. arrays with a structured dtype.
2+
3+
`ArraySpec` equality and hashing now compare the fill value by its byte representation rather than numeric equality. As a result, two specs with a `NaN` (or `NaT`) fill value now compare equal, while fill values of `-0.0` and `0.0` now compare unequal. This also restores the sharding codec's per-chunk spec cache, which had been disabled because of this bug.

‎tests/test_codecs/test_sharding.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,29 @@ async def test_delete_empty_shards(store: Store) -> None:
734734
assert len(chunk_bytes) == 16 * 2 + 8 * 8 * 2 + 4
735735

736736

737+
def test_structured_dtype_fill_value() -> None:
738+
"""Sharded arrays with a structured dtype are writable and readable even though
739+
the fill value is an (unhashable) ``np.void`` scalar: the sharding codec's
740+
chunk-spec caches key on ``ArraySpec``, whose hash must handle void fills
741+
(see https://github.com/zarr-developers/zarr-python/issues/3054)."""
742+
dtype = np.dtype([("a", "i4"), ("b", "f4")])
743+
arr = zarr.create_array(
744+
MemoryStore(),
745+
shape=(8,),
746+
chunks=(2,),
747+
shards=(4,),
748+
dtype=dtype,
749+
fill_value=(1, 2.0),
750+
)
751+
data = np.array([(i, i / 2) for i in range(8)], dtype=dtype)
752+
arr[:4] = data[:4]
753+
754+
expected = np.zeros(8, dtype=dtype)
755+
expected[:4] = data[:4]
756+
expected[4:] = (1, 2.0) # untouched shard reads back as the fill value
757+
assert np.array_equal(arr[:], expected)
758+
759+
737760
def test_pickle() -> None:
738761
"""ShardingCodec round-trips through pickle, including the non-serialized
739762
``subchunk_write_order`` (which ``to_dict`` omits and which must not silently

0 commit comments

Comments
 (0)