5
5
from typing import TYPE_CHECKING , Literal , TypeAlias , TypedDict , cast
6
6
7
7
if TYPE_CHECKING :
8
- from typing import NotRequired
8
+ from typing import NotRequired , Self
9
9
10
10
from zarr .abc .metadata import Metadata
11
11
from zarr .core .common import (
12
12
JSON ,
13
13
parse_named_configuration ,
14
14
)
15
+ from zarr .registry import get_chunk_key_encoding_class , register_chunk_key_encoding
15
16
16
17
SeparatorLiteral = Literal ["." , "/" ]
17
18
@@ -38,31 +39,9 @@ def __init__(self, *, separator: SeparatorLiteral) -> None:
38
39
object .__setattr__ (self , "separator" , separator_parsed )
39
40
40
41
@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 :
53
43
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]
66
45
67
46
def to_dict (self ) -> dict [str , JSON ]:
68
47
return {"name" : self .name , "configuration" : {"separator" : self .separator }}
@@ -76,12 +55,13 @@ def encode_chunk_key(self, chunk_coords: tuple[int, ...]) -> str:
76
55
pass
77
56
78
57
79
- ChunkKeyEncodingLike : TypeAlias = ChunkKeyEncodingParams | ChunkKeyEncoding
58
+ ChunkKeyEncodingLike : TypeAlias = dict [ str , JSON ] | ChunkKeyEncodingParams | ChunkKeyEncoding
80
59
81
60
82
61
@dataclass (frozen = True )
83
62
class DefaultChunkKeyEncoding (ChunkKeyEncoding ):
84
63
name : Literal ["default" ] = "default"
64
+ separator : SeparatorLiteral = "/" # default
85
65
86
66
def decode_chunk_key (self , chunk_key : str ) -> tuple [int , ...]:
87
67
if chunk_key == "c" :
@@ -95,10 +75,38 @@ def encode_chunk_key(self, chunk_coords: tuple[int, ...]) -> str:
95
75
@dataclass (frozen = True )
96
76
class V2ChunkKeyEncoding (ChunkKeyEncoding ):
97
77
name : Literal ["v2" ] = "v2"
78
+ separator : SeparatorLiteral = "." # default
98
79
99
80
def decode_chunk_key (self , chunk_key : str ) -> tuple [int , ...]:
100
81
return tuple (map (int , chunk_key .split (self .separator )))
101
82
102
83
def encode_chunk_key (self , chunk_coords : tuple [int , ...]) -> str :
103
84
chunk_identifier = self .separator .join (map (str , chunk_coords ))
104
85
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