@@ -54,20 +54,31 @@ class ScalarMap(TypedDict, total=False):
5454
5555
5656# see https://github.com/zarr-developers/zarr-extensions/tree/main/codecs/cast_value
57- PERMITTED_DATA_TYPE_NAMES : Final [set [str ]] = {
57+ CAST_VALUE_INT_DTYPES : Final [set [str ]] = {
58+ # signed
5859 "int2" ,
5960 "int4" ,
6061 "int8" ,
6162 "int16" ,
6263 "int32" ,
6364 "int64" ,
64- "int64uint2" ,
65+ # unsigned
66+ "uint2" ,
6567 "uint4" ,
6668 "uint8" ,
6769 "uint16" ,
6870 "uint32" ,
6971 "uint64" ,
70- "uint64float4_e2m1fn" ,
72+ }
73+ """Integer dtype identifiers permitted as the source or target of `cast_value`.
74+
75+ Membership in this set drives the `out_of_range="wrap"` rule, which the
76+ spec restricts to integral targets that use two's-complement representation
77+ for modular arithmetic.
78+ """
79+
80+ CAST_VALUE_FLOAT_DTYPES : Final [set [str ]] = {
81+ "float4_e2m1fn" ,
7182 "float6_e2m3fn" ,
7283 "float6_e3m2fn" ,
7384 "float8_e3m4" ,
@@ -82,6 +93,10 @@ class ScalarMap(TypedDict, total=False):
8293 "float32" ,
8394 "float64" ,
8495}
96+ """Floating-point dtype identifiers permitted as the source or target of `cast_value`."""
97+
98+ PERMITTED_DATA_TYPE_NAMES : Final [set [str ]] = CAST_VALUE_INT_DTYPES | CAST_VALUE_FLOAT_DTYPES
99+ """All dtype identifiers the `cast_value` codec is defined for."""
85100
86101
87102def parse_scalar_map (obj : ScalarMapJSON | ScalarMap ) -> ScalarMap :
@@ -240,15 +255,22 @@ def validate(
240255 dtype : ZDType [TBaseDType , TBaseScalar ],
241256 chunk_grid : ChunkGridMetadata ,
242257 ) -> None :
243- target_name = dtype .to_json (zarr_format = 3 )
244- if target_name not in PERMITTED_DATA_TYPE_NAMES :
258+ # `dtype` is the source (the array's dtype); `self.dtype` is the
259+ # cast target. The spec requires both to be permitted, and rules
260+ # like `out_of_range="wrap"` apply to the target.
261+ source_name = dtype .to_json (zarr_format = 3 )
262+ target_name = self .dtype .to_json (zarr_format = 3 )
263+ for role , name in (("source" , source_name ), ("target" , target_name )):
264+ if name not in PERMITTED_DATA_TYPE_NAMES :
265+ raise ValueError (
266+ f"The cast_value codec only supports integer and floating-point data types. "
267+ f"Got { role } dtype { name } ."
268+ )
269+ if self .out_of_range == "wrap" and target_name not in CAST_VALUE_INT_DTYPES :
245270 raise ValueError (
246- f"The cast_value codec only supports integer and floating-point data types. "
247- f"Got dtype { target_name } ."
271+ f"out_of_range='wrap' is only valid for integer target types. "
272+ f"Got target dtype { target_name } ."
248273 )
249- target_native = dtype .to_native_dtype ()
250- if self .out_of_range == "wrap" and not np .issubdtype (target_native , np .integer ):
251- raise ValueError ("out_of_range='wrap' is only valid for integer target types." )
252274
253275 if self .scalar_map is not None :
254276 self ._validate_scalar_map (dtype , self .dtype )
0 commit comments