Skip to content

Commit e6f7b90

Browse files
committed
(fix): structured dtype consolidated metadata fill value
1 parent 630897b commit e6f7b90

File tree

2 files changed

+26
-10
lines changed

2 files changed

+26
-10
lines changed

src/zarr/core/group.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
)
5050
from zarr.core.config import config
5151
from zarr.core.metadata import ArrayV2Metadata, ArrayV3Metadata
52-
from zarr.core.metadata.v3 import V3JsonEncoder, _replace_special_floats
52+
from zarr.core.metadata.v3 import V3JsonEncoder, _replace_special_floats_and_serialize_void
5353
from zarr.core.sync import SyncMixin, sync
5454
from zarr.errors import ContainsArrayError, ContainsGroupError, MetadataValidationError
5555
from zarr.storage import StoreLike, StorePath
@@ -334,7 +334,10 @@ def to_buffer_dict(self, prototype: BufferPrototype) -> dict[str, Buffer]:
334334
if self.zarr_format == 3:
335335
return {
336336
ZARR_JSON: prototype.buffer.from_bytes(
337-
json.dumps(_replace_special_floats(self.to_dict()), cls=V3JsonEncoder).encode()
337+
json.dumps(
338+
_replace_special_floats_and_serialize_void(self.to_dict(), is_v3=True),
339+
cls=V3JsonEncoder,
340+
).encode()
338341
)
339342
}
340343
else:
@@ -355,10 +358,14 @@ def to_buffer_dict(self, prototype: BufferPrototype) -> dict[str, Buffer]:
355358
assert isinstance(consolidated_metadata, dict)
356359
for k, v in consolidated_metadata.items():
357360
attrs = v.pop("attributes", None)
358-
d[f"{k}/{ZATTRS_JSON}"] = _replace_special_floats(attrs)
361+
d[f"{k}/{ZATTRS_JSON}"] = _replace_special_floats_and_serialize_void(
362+
attrs, is_v3=False
363+
)
359364
if "shape" in v:
360365
# it's an array
361-
d[f"{k}/{ZARRAY_JSON}"] = _replace_special_floats(v)
366+
d[f"{k}/{ZARRAY_JSON}"] = _replace_special_floats_and_serialize_void(
367+
v, is_v3=False
368+
)
362369
else:
363370
d[f"{k}/{ZGROUP_JSON}"] = {
364371
"zarr_format": self.zarr_format,

src/zarr/core/metadata/v3.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import base64
34
import warnings
45
from typing import TYPE_CHECKING, TypedDict, overload
56

@@ -44,7 +45,7 @@
4445

4546
DEFAULT_DTYPE = "float64"
4647

47-
# Keep in sync with _replace_special_floats
48+
# Keep in sync with _replace_special_floats_and_serialize_void
4849
SPECIAL_FLOATS_ENCODED = {
4950
"Infinity": np.inf,
5051
"-Infinity": -np.inf,
@@ -187,7 +188,9 @@ def default(self, o: object) -> Any:
187188
if isinstance(out, complex):
188189
# python complex types are not JSON serializable, so we use the
189190
# serialization defined in the zarr v3 spec
190-
return _replace_special_floats([out.real, out.imag])
191+
return _replace_special_floats_and_serialize_void(
192+
[out.real, out.imag], is_v3=True
193+
)
191194
elif np.isnan(out):
192195
return "NaN"
193196
elif np.isinf(out):
@@ -204,7 +207,7 @@ def default(self, o: object) -> Any:
204207
return super().default(o)
205208

206209

207-
def _replace_special_floats(obj: object) -> Any:
210+
def _replace_special_floats_and_serialize_void(obj: object, is_v3: bool) -> Any:
208211
"""Helper function to replace NaN/Inf/-Inf values with special strings
209212
210213
Note: this cannot be done in the V3JsonEncoder because Python's `json.dumps` optimistically
@@ -215,12 +218,18 @@ def _replace_special_floats(obj: object) -> Any:
215218
return "NaN"
216219
elif np.isinf(obj):
217220
return "Infinity" if obj > 0 else "-Infinity"
221+
if isinstance(obj, np.void):
222+
if is_v3:
223+
raise ValueError("Cannot convert void with v3 zarr")
224+
return base64.standard_b64encode(cast(bytes, obj)).decode("ascii")
218225
elif isinstance(obj, dict):
219226
# Recursively replace in dictionaries
220-
return {k: _replace_special_floats(v) for k, v in obj.items()}
227+
return {
228+
k: _replace_special_floats_and_serialize_void(v, is_v3=is_v3) for k, v in obj.items()
229+
}
221230
elif isinstance(obj, list):
222231
# Recursively replace in lists
223-
return [_replace_special_floats(item) for item in obj]
232+
return [_replace_special_floats_and_serialize_void(item, is_v3=is_v3) for item in obj]
224233
return obj
225234

226235

@@ -388,7 +397,7 @@ def encode_chunk_key(self, chunk_coords: ChunkCoords) -> str:
388397
return self.chunk_key_encoding.encode_chunk_key(chunk_coords)
389398

390399
def to_buffer_dict(self, prototype: BufferPrototype) -> dict[str, Buffer]:
391-
d = _replace_special_floats(self.to_dict())
400+
d = _replace_special_floats_and_serialize_void(self.to_dict(), is_v3=True)
392401
return {ZARR_JSON: prototype.buffer.from_bytes(json.dumps(d, cls=V3JsonEncoder).encode())}
393402

394403
@classmethod

0 commit comments

Comments
 (0)