Skip to content

Commit

Permalink
Test structured fill_value parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
moradology committed Feb 26, 2025
1 parent e3c7659 commit 29faec3
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/zarr/core/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -3458,7 +3458,7 @@ def _build_metadata_v3(zarr_json: dict[str, JSON]) -> ArrayV3Metadata | GroupMet


def _build_metadata_v2(
zarr_json: dict[str, object], attrs_json: dict[str, JSON]
zarr_json: dict[str, JSON], attrs_json: dict[str, JSON]
) -> ArrayV2Metadata | GroupMetadata:
"""
Convert a dict representation of Zarr V2 metadata into the corresponding metadata class.
Expand Down
4 changes: 3 additions & 1 deletion src/zarr/core/metadata/v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,9 @@ def parse_metadata(data: ArrayV2Metadata) -> ArrayV2Metadata:
def parse_structured_fill_value(fill_value: Any, dtype: np.dtype[Any]) -> Any:
"""Handle structured dtype/fill value pairs"""
try:
if isinstance(fill_value, (tuple, list)):
if isinstance(fill_value, list):
fill_value = tuple(fill_value)
if isinstance(fill_value, tuple):
fill_value = np.array([fill_value], dtype=dtype)[0]
elif isinstance(fill_value, bytes):
fill_value = np.frombuffer(fill_value, dtype=dtype)[0]
Expand Down
2 changes: 1 addition & 1 deletion src/zarr/testing/strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import hypothesis.extra.numpy as npst
import hypothesis.strategies as st
import numpy as np
from hypothesis import event, given, settings # noqa: F401
from hypothesis import event
from hypothesis.strategies import SearchStrategy

import zarr
Expand Down
1 change: 1 addition & 0 deletions tests/test_properties.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import dataclasses
import json
import numbers

import numpy as np
import pytest
from numpy.testing import assert_array_equal
Expand Down
84 changes: 84 additions & 0 deletions tests/test_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from zarr import config
from zarr.abc.store import Store
from zarr.core.buffer.core import default_buffer_prototype
from zarr.core.metadata.v2 import parse_structured_fill_value
from zarr.core.sync import sync
from zarr.storage import MemoryStore, StorePath

Expand Down Expand Up @@ -315,6 +316,89 @@ def test_structured_dtype_roundtrip(fill_value, tmp_path) -> None:
assert (a == za[:]).all()


@pytest.mark.parametrize(
(
"fill_value",
"dtype",
"expected_result",
),
[
(
("Alice", 30),
np.dtype([("name", "U10"), ("age", "i4")]),
np.array([("Alice", 30)], dtype=[("name", "U10"), ("age", "i4")])[0],
),
(
["Bob", 25],
np.dtype([("name", "U10"), ("age", "i4")]),
np.array([("Bob", 25)], dtype=[("name", "U10"), ("age", "i4")])[0],
),
(
b"\x01\x00\x00\x00\x02\x00\x00\x00",
np.dtype([("x", "i4"), ("y", "i4")]),
np.array([(1, 2)], dtype=[("x", "i4"), ("y", "i4")])[0],
),
(
"BQAAAA==",
np.dtype([("val", "i4")]),
np.array([(5,)], dtype=[("val", "i4")])[0],
),
(
{"x": 1, "y": 2},
np.dtype([("location", "O")]),
np.array([({"x": 1, "y": 2},)], dtype=[("location", "O")])[0],
),
(
{"x": 1, "y": 2, "z": 3},
np.dtype([("location", "O")]),
np.array([({"x": 1, "y": 2, "z": 3},)], dtype=[("location", "O")])[0],
),
],
ids=[
"tuple_input",
"list_input",
"bytes_input",
"string_input",
"dictionary_input",
"dictionary_input_extra_fields",
],
)
def test_parse_structured_fill_value_valid(
fill_value: Any, dtype: np.dtype[Any], expected_result: Any
) -> None:
result = parse_structured_fill_value(fill_value, dtype)
assert result.dtype == expected_result.dtype
assert result == expected_result
if isinstance(expected_result, np.void):
for name in expected_result.dtype.names or []:
assert result[name] == expected_result[name]


@pytest.mark.parametrize(
(
"fill_value",
"dtype",
),
[
(("Alice", 30), np.dtype([("name", "U10"), ("age", "i4"), ("city", "U20")])),
(b"\x01\x00\x00\x00", np.dtype([("x", "i4"), ("y", "i4")])),
("this_is_not_base64", np.dtype([("val", "i4")])),
("hello", np.dtype([("age", "i4")])),
({"x": 1, "y": 2}, np.dtype([("location", "i4")])),
],
ids=[
"tuple_list_wrong_length",
"bytes_wrong_length",
"invalid_base64",
"wrong_data_type",
"wrong_dictionary",
],
)
def test_parse_structured_fill_value_invalid(fill_value: Any, dtype: np.dtype[Any]) -> None:
with pytest.raises(ValueError):
parse_structured_fill_value(fill_value, dtype)


@pytest.mark.parametrize("fill_value", [None, b"x"], ids=["no_fill", "fill"])
def test_other_dtype_roundtrip(fill_value, tmp_path) -> None:
a = np.array([b"a\0\0", b"bb", b"ccc"], dtype="V7")
Expand Down

0 comments on commit 29faec3

Please sign in to comment.