diff --git a/changes/4495.bugfix.md b/changes/4495.bugfix.md new file mode 100644 index 0000000000..e95e5badda --- /dev/null +++ b/changes/4495.bugfix.md @@ -0,0 +1 @@ +Structured data types now default byte-string (`S`) and raw-bytes (`V`) fields to zero bytes. Before, an array with a raw-bytes field failed to create without an explicit fill value, and byte-string fields defaulted to `b"0"`. diff --git a/src/zarr/core/dtype/npy/structured.py b/src/zarr/core/dtype/npy/structured.py index 7a0d54e1a4..f421835309 100644 --- a/src/zarr/core/dtype/npy/structured.py +++ b/src/zarr/core/dtype/npy/structured.py @@ -579,7 +579,7 @@ def default_scalar(self) -> np.void: # zero count is representable by viewing the integer storage. value = np.zeros(1, dtype=dtype.byteorder + "i8").view(dtype)[0] else: - value = np.array([0], dtype=dtype)[0] + value = np.zeros(1, dtype=dtype)[0] values.append(value) return self._cast_scalar_unchecked(tuple(values)) diff --git a/tests/test_dtype/test_npy/test_structured.py b/tests/test_dtype/test_npy/test_structured.py index a8dc94db96..e1920012b2 100644 --- a/tests/test_dtype/test_npy/test_structured.py +++ b/tests/test_dtype/test_npy/test_structured.py @@ -392,3 +392,12 @@ def test_subarray_structured_dtype_raises() -> None: dtype = np.dtype([("f0", "i4", (2,))]) with pytest.raises(ValueError, match="field 'f0' is a subarray"): Struct.from_native_dtype(dtype) + + +@pytest.mark.parametrize("field_dtype", ["S4", "V4"]) +def test_default_scalar_bytes_field_is_zero(field_dtype: str) -> None: + """ + The default scalar of a bytes field is zero bytes, as for the same dtype outside a struct. + """ + dtype = np.dtype([("a", field_dtype), ("b", "