4848)
4949from zarr .core .config import config
5050from zarr .core .metadata import ArrayV2Metadata , ArrayV3Metadata
51+ from zarr .core .metadata .v3 import (
52+ AllowedExtraField ,
53+ check_allowed_extra_field ,
54+ parse_extra_fields ,
55+ )
5156from zarr .core .metadata .io import save_metadata
5257from zarr .core .sync import SyncMixin , sync
5358from zarr .errors import (
@@ -354,6 +359,7 @@ class GroupMetadata(Metadata):
354359 zarr_format : ZarrFormat = 3
355360 consolidated_metadata : ConsolidatedMetadata | None = None
356361 node_type : Literal ["group" ] = field (default = "group" , init = False )
362+ extra_fields : dict [str , AllowedExtraField ] = field (default_factory = dict )
357363
358364 def to_buffer_dict (self , prototype : BufferPrototype ) -> dict [str , Buffer ]:
359365 json_indent = config .get ("json_indent" )
@@ -408,13 +414,16 @@ def __init__(
408414 attributes : dict [str , Any ] | None = None ,
409415 zarr_format : ZarrFormat = 3 ,
410416 consolidated_metadata : ConsolidatedMetadata | None = None ,
417+ extra_fields : Mapping [str , AllowedExtraField ] | None = None ,
411418 ) -> None :
412419 attributes_parsed = parse_attributes (attributes )
413420 zarr_format_parsed = parse_zarr_format (zarr_format )
421+ extra_fields_parsed = parse_extra_fields (extra_fields )
414422
415423 object .__setattr__ (self , "attributes" , attributes_parsed )
416424 object .__setattr__ (self , "zarr_format" , zarr_format_parsed )
417425 object .__setattr__ (self , "consolidated_metadata" , consolidated_metadata )
426+ object .__setattr__ (self , "extra_fields" , extra_fields_parsed )
418427
419428 @classmethod
420429 def from_dict (cls , data : dict [str , Any ]) -> GroupMetadata :
@@ -431,11 +440,34 @@ def from_dict(cls, data: dict[str, Any]) -> GroupMetadata:
431440 # extra key in the metadata.
432441 expected = {x .name for x in fields (cls )}
433442 data = {k : v for k , v in data .items () if k in expected }
443+ else :
444+ # zarr v3: extra fields must have must_understand=False
445+ expected = {x .name for x in fields (cls )}
446+ extra_fields : dict [str , AllowedExtraField ] = {}
447+ invalid_extra_fields : dict [str , Any ] = {}
448+ for key in list (data .keys ()):
449+ if key not in expected :
450+ val = data .pop (key )
451+ if check_allowed_extra_field (val ):
452+ extra_fields [key ] = val
453+ else :
454+ invalid_extra_fields [key ] = val
455+ if len (invalid_extra_fields ) > 0 :
456+ msg = (
457+ "Got a Zarr V3 group metadata document with the following disallowed extra fields:"
458+ f"{ sorted (invalid_extra_fields .keys ())} ."
459+ 'Extra fields are not allowed unless they are a dict with a "must_understand" key'
460+ "which is assigned the value `False`."
461+ )
462+ raise MetadataValidationError (msg )
463+ data ["extra_fields" ] = extra_fields
434464
435465 return cls (** data )
436466
437467 def to_dict (self ) -> dict [str , Any ]:
438468 result = asdict (replace (self , consolidated_metadata = None ))
469+ extra_fields = result .pop ("extra_fields" , {})
470+ result = result | extra_fields
439471 if self .consolidated_metadata is not None :
440472 result ["consolidated_metadata" ] = self .consolidated_metadata .to_dict ()
441473 else :
0 commit comments