Skip to content

Commit 8f4a332

Browse files
authored
Merge branch 'v3' into docs_v3
2 parents fb938ba + cc50b41 commit 8f4a332

File tree

5 files changed

+77
-36
lines changed

5 files changed

+77
-36
lines changed

src/zarr/abc/metadata.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ def to_dict(self) -> dict[str, JSON]:
2222
are instances of `Metadata`. Sequences of `Metadata` are similarly recursed into, and
2323
the output of that recursion is collected in a list.
2424
"""
25-
...
2625
out_dict = {}
2726
for field in fields(self):
2827
key = field.name

src/zarr/core/group.py

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -768,24 +768,20 @@ async def full(
768768
)
769769

770770
async def empty_like(
771-
self, *, name: str, prototype: async_api.ArrayLike, **kwargs: Any
771+
self, *, name: str, data: async_api.ArrayLike, **kwargs: Any
772772
) -> AsyncArray:
773-
return await async_api.empty_like(a=prototype, store=self.store_path, path=name, **kwargs)
773+
return await async_api.empty_like(a=data, store=self.store_path, path=name, **kwargs)
774774

775775
async def zeros_like(
776-
self, *, name: str, prototype: async_api.ArrayLike, **kwargs: Any
776+
self, *, name: str, data: async_api.ArrayLike, **kwargs: Any
777777
) -> AsyncArray:
778-
return await async_api.zeros_like(a=prototype, store=self.store_path, path=name, **kwargs)
778+
return await async_api.zeros_like(a=data, store=self.store_path, path=name, **kwargs)
779779

780-
async def ones_like(
781-
self, *, name: str, prototype: async_api.ArrayLike, **kwargs: Any
782-
) -> AsyncArray:
783-
return await async_api.ones_like(a=prototype, store=self.store_path, path=name, **kwargs)
780+
async def ones_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> AsyncArray:
781+
return await async_api.ones_like(a=data, store=self.store_path, path=name, **kwargs)
784782

785-
async def full_like(
786-
self, *, name: str, prototype: async_api.ArrayLike, **kwargs: Any
787-
) -> AsyncArray:
788-
return await async_api.full_like(a=prototype, store=self.store_path, path=name, **kwargs)
783+
async def full_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> AsyncArray:
784+
return await async_api.full_like(a=data, store=self.store_path, path=name, **kwargs)
789785

790786
async def move(self, source: str, dest: str) -> None:
791787
raise NotImplementedError
@@ -1171,25 +1167,17 @@ def full(
11711167
)
11721168
)
11731169

1174-
def empty_like(self, *, name: str, prototype: async_api.ArrayLike, **kwargs: Any) -> Array:
1175-
return Array(
1176-
self._sync(self._async_group.empty_like(name=name, prototype=prototype, **kwargs))
1177-
)
1170+
def empty_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> Array:
1171+
return Array(self._sync(self._async_group.empty_like(name=name, data=data, **kwargs)))
11781172

1179-
def zeros_like(self, *, name: str, prototype: async_api.ArrayLike, **kwargs: Any) -> Array:
1180-
return Array(
1181-
self._sync(self._async_group.zeros_like(name=name, prototype=prototype, **kwargs))
1182-
)
1173+
def zeros_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> Array:
1174+
return Array(self._sync(self._async_group.zeros_like(name=name, data=data, **kwargs)))
11831175

1184-
def ones_like(self, *, name: str, prototype: async_api.ArrayLike, **kwargs: Any) -> Array:
1185-
return Array(
1186-
self._sync(self._async_group.ones_like(name=name, prototype=prototype, **kwargs))
1187-
)
1176+
def ones_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> Array:
1177+
return Array(self._sync(self._async_group.ones_like(name=name, data=data, **kwargs)))
11881178

1189-
def full_like(self, *, name: str, prototype: async_api.ArrayLike, **kwargs: Any) -> Array:
1190-
return Array(
1191-
self._sync(self._async_group.full_like(name=name, prototype=prototype, **kwargs))
1192-
)
1179+
def full_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> Array:
1180+
return Array(self._sync(self._async_group.full_like(name=name, data=data, **kwargs)))
11931181

11941182
def move(self, source: str, dest: str) -> None:
11951183
return self._sync(self._async_group.move(source, dest))

src/zarr/core/metadata/v2.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from __future__ import annotations
22

3+
import base64
34
from collections.abc import Iterable
45
from enum import Enum
5-
from typing import TYPE_CHECKING
6+
from typing import TYPE_CHECKING, cast
67

78
if TYPE_CHECKING:
89
from typing import Any, Literal, Self
@@ -31,7 +32,7 @@ class ArrayV2Metadata(ArrayMetadata):
3132
shape: ChunkCoords
3233
chunk_grid: RegularChunkGrid
3334
data_type: np.dtype[Any]
34-
fill_value: None | int | float = 0
35+
fill_value: None | int | float | str | bytes = 0
3536
order: Literal["C", "F"] = "C"
3637
filters: tuple[numcodecs.abc.Codec, ...] | None = None
3738
dimension_separator: Literal[".", "/"] = "."
@@ -140,6 +141,13 @@ def from_dict(cls, data: dict[str, Any]) -> ArrayV2Metadata:
140141
_data = data.copy()
141142
# check that the zarr_format attribute is correct
142143
_ = parse_zarr_format(_data.pop("zarr_format"))
144+
dtype = parse_dtype(_data["dtype"])
145+
146+
if dtype.kind in "SV":
147+
fill_value_encoded = _data.get("fill_value")
148+
if fill_value_encoded is not None:
149+
fill_value = base64.standard_b64decode(fill_value_encoded)
150+
_data["fill_value"] = fill_value
143151

144152
# zarr v2 allowed arbitrary keys here.
145153
# We don't want the ArrayV2Metadata constructor to fail just because someone put an
@@ -155,6 +163,14 @@ def from_dict(cls, data: dict[str, Any]) -> ArrayV2Metadata:
155163

156164
def to_dict(self) -> dict[str, JSON]:
157165
zarray_dict = super().to_dict()
166+
167+
if self.dtype.kind in "SV" and self.fill_value is not None:
168+
# There's a relationship between self.dtype and self.fill_value
169+
# that mypy isn't aware of. The fact that we have S or V dtype here
170+
# means we should have a bytes-type fill_value.
171+
fill_value = base64.standard_b64encode(cast(bytes, self.fill_value)).decode("ascii")
172+
zarray_dict["fill_value"] = fill_value
173+
158174
_ = zarray_dict.pop("chunk_grid")
159175
zarray_dict["chunks"] = self.chunk_grid.chunk_shape
160176

tests/v3/test_group.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ def test_group_array_creation(
447447
assert empty_array.shape == shape
448448
assert empty_array.store_path.store == store
449449

450-
empty_like_array = group.empty_like(name="empty_like", prototype=empty_array)
450+
empty_like_array = group.empty_like(name="empty_like", data=empty_array)
451451
assert isinstance(empty_like_array, Array)
452452
assert empty_like_array.fill_value == 0
453453
assert empty_like_array.shape == shape
@@ -459,7 +459,7 @@ def test_group_array_creation(
459459
assert empty_array_bool.shape == shape
460460
assert empty_array_bool.store_path.store == store
461461

462-
empty_like_array_bool = group.empty_like(name="empty_like_bool", prototype=empty_array_bool)
462+
empty_like_array_bool = group.empty_like(name="empty_like_bool", data=empty_array_bool)
463463
assert isinstance(empty_like_array_bool, Array)
464464
assert not empty_like_array_bool.fill_value
465465
assert empty_like_array_bool.shape == shape
@@ -471,7 +471,7 @@ def test_group_array_creation(
471471
assert zeros_array.shape == shape
472472
assert zeros_array.store_path.store == store
473473

474-
zeros_like_array = group.zeros_like(name="zeros_like", prototype=zeros_array)
474+
zeros_like_array = group.zeros_like(name="zeros_like", data=zeros_array)
475475
assert isinstance(zeros_like_array, Array)
476476
assert zeros_like_array.fill_value == 0
477477
assert zeros_like_array.shape == shape
@@ -483,7 +483,7 @@ def test_group_array_creation(
483483
assert ones_array.shape == shape
484484
assert ones_array.store_path.store == store
485485

486-
ones_like_array = group.ones_like(name="ones_like", prototype=ones_array)
486+
ones_like_array = group.ones_like(name="ones_like", data=ones_array)
487487
assert isinstance(ones_like_array, Array)
488488
assert ones_like_array.fill_value == 1
489489
assert ones_like_array.shape == shape
@@ -495,7 +495,7 @@ def test_group_array_creation(
495495
assert full_array.shape == shape
496496
assert full_array.store_path.store == store
497497

498-
full_like_array = group.full_like(name="full_like", prototype=full_array, fill_value=43)
498+
full_like_array = group.full_like(name="full_like", data=full_array, fill_value=43)
499499
assert isinstance(full_like_array, Array)
500500
assert full_like_array.fill_value == 43
501501
assert full_like_array.shape == shape

tests/v3/test_v2.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
from collections.abc import Iterator
23

34
import numpy as np
@@ -6,6 +7,9 @@
67
from numcodecs.blosc import Blosc
78

89
import zarr
10+
import zarr.core.buffer.cpu
11+
import zarr.core.metadata
12+
import zarr.storage
913
from zarr import Array
1014
from zarr.storage import MemoryStore, StorePath
1115

@@ -46,3 +50,37 @@ def test_codec_pipeline() -> None:
4650
result = array[:]
4751
expected = np.ones(1)
4852
np.testing.assert_array_equal(result, expected)
53+
54+
55+
@pytest.mark.parametrize("dtype", ["|S", "|V"])
56+
async def test_v2_encode_decode(dtype):
57+
store = zarr.storage.MemoryStore(mode="w")
58+
g = zarr.group(store=store, zarr_format=2)
59+
g.create_array(
60+
name="foo",
61+
shape=(3,),
62+
chunks=(3,),
63+
dtype=dtype,
64+
fill_value=b"X",
65+
)
66+
67+
result = await store.get("foo/.zarray", zarr.core.buffer.default_buffer_prototype())
68+
assert result is not None
69+
70+
serialized = json.loads(result.to_bytes())
71+
expected = {
72+
"chunks": [3],
73+
"compressor": None,
74+
"dtype": f"{dtype}0",
75+
"fill_value": "WA==",
76+
"filters": None,
77+
"order": "C",
78+
"shape": [3],
79+
"zarr_format": 2,
80+
"dimension_separator": ".",
81+
}
82+
assert serialized == expected
83+
84+
data = zarr.open_array(store=store, path="foo")[:]
85+
expected = np.full((3,), b"X", dtype=dtype)
86+
np.testing.assert_equal(data, expected)

0 commit comments

Comments
 (0)