2929import  math 
3030from  dataclasses  import  dataclass , replace 
3131from  functools  import  cached_property 
32+ from  importlib .metadata  import  version 
3233from  typing  import  Any , Self 
3334from  warnings  import  warn 
3435
3536import  numpy  as  np 
37+ from  packaging .version  import  Version 
3638
3739import  numcodecs 
3840
3941try :
40-     import  zarr 
42+     import  zarr    # noqa: F401 
4143
42-     if  zarr . __version__  <  "3.0.0" :  # pragma: no cover 
44+     if  Version ( version ( ' zarr' ))  <  Version ( "3.0.0" ) :  # pragma: no cover 
4345        raise  ImportError ("zarr 3.0.0 or later is required to use the numcodecs zarr integration." )
4446except  ImportError  as  e :  # pragma: no cover 
4547    raise  ImportError (
5658CODEC_PREFIX  =  "numcodecs." 
5759
5860
61+ def  _from_zarr_dtype (dtype : Any ) ->  np .dtype :
62+     """ 
63+     Get a numpy data type from an array spec, depending on the zarr version. 
64+     """ 
65+     if  Version (version ('zarr' )) >=  Version ("3.1.0" ):
66+         return  dtype .to_native_dtype ()
67+     return  dtype   # pragma: no cover 
68+ 
69+ 
70+ def  _to_zarr_dtype (dtype : np .dtype ) ->  Any :
71+     if  Version (version ('zarr' )) >=  Version ("3.1.0" ):
72+         from  zarr .dtype  import  parse_data_type 
73+ 
74+         return  parse_data_type (dtype , zarr_format = 3 )
75+     return  dtype   # pragma: no cover 
76+ 
77+ 
5978def  _expect_name_prefix (codec_name : str ) ->  str :
6079    if  not  codec_name .startswith (CODEC_PREFIX ):
6180        raise  ValueError (
@@ -224,15 +243,17 @@ class LZMA(_NumcodecsBytesBytesCodec, codec_name="lzma"):
224243class  Shuffle (_NumcodecsBytesBytesCodec , codec_name = "shuffle" ):
225244    def  evolve_from_array_spec (self , array_spec : ArraySpec ) ->  Shuffle :
226245        if  self .codec_config .get ("elementsize" ) is  None :
227-             return  Shuffle (** {** self .codec_config , "elementsize" : array_spec .dtype .itemsize })
246+             dtype  =  _from_zarr_dtype (array_spec .dtype )
247+             return  Shuffle (** {** self .codec_config , "elementsize" : dtype .itemsize })
228248        return  self   # pragma: no cover 
229249
230250
231251# array-to-array codecs ("filters") 
232252class  Delta (_NumcodecsArrayArrayCodec , codec_name = "delta" ):
233253    def  resolve_metadata (self , chunk_spec : ArraySpec ) ->  ArraySpec :
234254        if  astype  :=  self .codec_config .get ("astype" ):
235-             return  replace (chunk_spec , dtype = np .dtype (astype ))  # type: ignore[call-overload] 
255+             dtype  =  _to_zarr_dtype (np .dtype (astype ))  # type: ignore[call-overload] 
256+             return  replace (chunk_spec , dtype = dtype )
236257        return  chunk_spec 
237258
238259
@@ -243,12 +264,14 @@ class BitRound(_NumcodecsArrayArrayCodec, codec_name="bitround"):
243264class  FixedScaleOffset (_NumcodecsArrayArrayCodec , codec_name = "fixedscaleoffset" ):
244265    def  resolve_metadata (self , chunk_spec : ArraySpec ) ->  ArraySpec :
245266        if  astype  :=  self .codec_config .get ("astype" ):
246-             return  replace (chunk_spec , dtype = np .dtype (astype ))  # type: ignore[call-overload] 
267+             dtype  =  _to_zarr_dtype (np .dtype (astype ))  # type: ignore[call-overload] 
268+             return  replace (chunk_spec , dtype = dtype )
247269        return  chunk_spec 
248270
249271    def  evolve_from_array_spec (self , array_spec : ArraySpec ) ->  FixedScaleOffset :
250272        if  self .codec_config .get ("dtype" ) is  None :
251-             return  FixedScaleOffset (** {** self .codec_config , "dtype" : str (array_spec .dtype )})
273+             dtype  =  _from_zarr_dtype (array_spec .dtype )
274+             return  FixedScaleOffset (** {** self .codec_config , "dtype" : str (dtype )})
252275        return  self 
253276
254277
@@ -258,7 +281,8 @@ def __init__(self, **codec_config: JSON) -> None:
258281
259282    def  evolve_from_array_spec (self , array_spec : ArraySpec ) ->  Quantize :
260283        if  self .codec_config .get ("dtype" ) is  None :
261-             return  Quantize (** {** self .codec_config , "dtype" : str (array_spec .dtype )})
284+             dtype  =  _from_zarr_dtype (array_spec .dtype )
285+             return  Quantize (** {** self .codec_config , "dtype" : str (dtype )})
262286        return  self 
263287
264288
@@ -267,21 +291,27 @@ def resolve_metadata(self, chunk_spec: ArraySpec) -> ArraySpec:
267291        return  replace (
268292            chunk_spec ,
269293            shape = (1  +  math .ceil (product (chunk_spec .shape ) /  8 ),),
270-             dtype = np .dtype ("uint8" ),
294+             dtype = _to_zarr_dtype ( np .dtype ("uint8" ) ),
271295        )
272296
273-     def  validate (self , * , dtype : np .dtype [Any ], ** _kwargs ) ->  None :
274-         if  dtype  !=  np .dtype ("bool" ):
297+     # todo: remove this type: ignore when this class can be defined w.r.t. 
298+     # a single zarr dtype API 
299+     def  validate (self , * , dtype : np .dtype [Any ], ** _kwargs ) ->  None :  # type: ignore[override] 
300+         _dtype  =  _from_zarr_dtype (dtype )
301+         if  _dtype  !=  np .dtype ("bool" ):
275302            raise  ValueError (f"Packbits filter requires bool dtype. Got { dtype }  )
276303
277304
278305class  AsType (_NumcodecsArrayArrayCodec , codec_name = "astype" ):
279306    def  resolve_metadata (self , chunk_spec : ArraySpec ) ->  ArraySpec :
280-         return  replace (chunk_spec , dtype = np .dtype (self .codec_config ["encode_dtype" ]))  # type: ignore[arg-type] 
307+         dtype  =  _to_zarr_dtype (np .dtype (self .codec_config ["encode_dtype" ]))  # type: ignore[arg-type] 
308+         return  replace (chunk_spec , dtype = dtype )
281309
282310    def  evolve_from_array_spec (self , array_spec : ArraySpec ) ->  AsType :
283311        if  self .codec_config .get ("decode_dtype" ) is  None :
284-             return  AsType (** {** self .codec_config , "decode_dtype" : str (array_spec .dtype )})
312+             # TODO: remove these coverage exemptions the correct way, i.e. with tests 
313+             dtype  =  _from_zarr_dtype (array_spec .dtype )  # pragma: no cover 
314+             return  AsType (** {** self .codec_config , "decode_dtype" : str (dtype )})  # pragma: no cover 
285315        return  self 
286316
287317
0 commit comments