55from typing import TYPE_CHECKING , Literal , TypeAlias , TypedDict , cast
66
77if TYPE_CHECKING :
8- from typing import NotRequired
8+ from typing import NotRequired , Self
99
1010from zarr .abc .metadata import Metadata
1111from zarr .core .common import (
1212 JSON ,
1313 parse_named_configuration ,
1414)
15+ from zarr .registry import get_chunk_key_encoding_class , register_chunk_key_encoding
1516
1617SeparatorLiteral = Literal ["." , "/" ]
1718
@@ -38,31 +39,9 @@ def __init__(self, *, separator: SeparatorLiteral) -> None:
3839 object .__setattr__ (self , "separator" , separator_parsed )
3940
4041 @classmethod
41- def from_dict (cls , data : dict [str , JSON ] | ChunkKeyEncodingLike ) -> ChunkKeyEncoding :
42- if isinstance (data , ChunkKeyEncoding ):
43- return data
44-
45- # handle ChunkKeyEncodingParams
46- if "name" in data and "separator" in data :
47- data = {"name" : data ["name" ], "configuration" : {"separator" : data ["separator" ]}}
48-
49- # TODO: remove this cast when we are statically typing the JSON metadata completely.
50- data = cast ("dict[str, JSON]" , data )
51-
52- # configuration is optional for chunk key encodings
42+ def from_dict (cls , data : dict [str , JSON ]) -> Self :
5343 name_parsed , config_parsed = parse_named_configuration (data , require_configuration = False )
54- if name_parsed == "default" :
55- if config_parsed is None :
56- # for default, normalize missing configuration to use the "/" separator.
57- config_parsed = {"separator" : "/" }
58- return DefaultChunkKeyEncoding (** config_parsed ) # type: ignore[arg-type]
59- if name_parsed == "v2" :
60- if config_parsed is None :
61- # for v2, normalize missing configuration to use the "." separator.
62- config_parsed = {"separator" : "." }
63- return V2ChunkKeyEncoding (** config_parsed ) # type: ignore[arg-type]
64- msg = f"Unknown chunk key encoding. Got { name_parsed } , expected one of ('v2', 'default')."
65- raise ValueError (msg )
44+ return cls (** config_parsed if config_parsed else {}) # type: ignore[arg-type]
6645
6746 def to_dict (self ) -> dict [str , JSON ]:
6847 return {"name" : self .name , "configuration" : {"separator" : self .separator }}
@@ -76,12 +55,13 @@ def encode_chunk_key(self, chunk_coords: tuple[int, ...]) -> str:
7655 pass
7756
7857
79- ChunkKeyEncodingLike : TypeAlias = ChunkKeyEncodingParams | ChunkKeyEncoding
58+ ChunkKeyEncodingLike : TypeAlias = dict [ str , JSON ] | ChunkKeyEncodingParams | ChunkKeyEncoding
8059
8160
8261@dataclass (frozen = True )
8362class DefaultChunkKeyEncoding (ChunkKeyEncoding ):
8463 name : Literal ["default" ] = "default"
64+ separator : SeparatorLiteral = "/" # default
8565
8666 def decode_chunk_key (self , chunk_key : str ) -> tuple [int , ...]:
8767 if chunk_key == "c" :
@@ -95,10 +75,38 @@ def encode_chunk_key(self, chunk_coords: tuple[int, ...]) -> str:
9575@dataclass (frozen = True )
9676class V2ChunkKeyEncoding (ChunkKeyEncoding ):
9777 name : Literal ["v2" ] = "v2"
78+ separator : SeparatorLiteral = "." # default
9879
9980 def decode_chunk_key (self , chunk_key : str ) -> tuple [int , ...]:
10081 return tuple (map (int , chunk_key .split (self .separator )))
10182
10283 def encode_chunk_key (self , chunk_coords : tuple [int , ...]) -> str :
10384 chunk_identifier = self .separator .join (map (str , chunk_coords ))
10485 return "0" if chunk_identifier == "" else chunk_identifier
86+
87+
88+ def parse_chunk_key_encoding (data : ChunkKeyEncodingLike ) -> ChunkKeyEncoding :
89+ """
90+ Take an implicit specification of a chunk key encoding and parse it into a ChunkKeyEncoding object.
91+ """
92+ if isinstance (data , ChunkKeyEncoding ):
93+ return data
94+
95+ # handle ChunkKeyEncodingParams
96+ if "name" in data and "separator" in data :
97+ data = {"name" : data ["name" ], "configuration" : {"separator" : data ["separator" ]}}
98+
99+ # Now must be a named config
100+ data = cast ("dict[str, JSON]" , data )
101+
102+ name_parsed , _ = parse_named_configuration (data , require_configuration = False )
103+ try :
104+ chunk_key_encoding = get_chunk_key_encoding_class (name_parsed ).from_dict (data )
105+ except KeyError as e :
106+ raise ValueError (f"Unknown chunk key encoding: { e .args [0 ]!r} " ) from e
107+
108+ return chunk_key_encoding
109+
110+
111+ register_chunk_key_encoding (DefaultChunkKeyEncoding , qualname = "default" )
112+ register_chunk_key_encoding (V2ChunkKeyEncoding , qualname = "v2" )
0 commit comments