|
30 | 30 | IncEx, |
31 | 31 | StrictBase, |
32 | 32 | ensure_key_no_path, |
| 33 | + ensure_multiple, |
33 | 34 | maybe_node, |
34 | 35 | model_like, |
35 | 36 | tuplify_json, |
@@ -94,6 +95,10 @@ class AnyNamedConfig(NamedConfig[str, Mapping[str, object]]): |
94 | 95 | """ |
95 | 96 |
|
96 | 97 |
|
| 98 | +CodecLike = str | AnyNamedConfig |
| 99 | +"""A type modelling the permissible declarations for codecs""" |
| 100 | + |
| 101 | + |
97 | 102 | class RegularChunkingConfig(TypedDict): |
98 | 103 | chunk_shape: tuple[int, ...] |
99 | 104 |
|
@@ -160,7 +165,9 @@ def parse_dtype_v3(dtype: npt.DTypeLike | Mapping[str, object]) -> Mapping[str, |
160 | 165 | raise ValueError(f"Unsupported dtype: {dtype}") |
161 | 166 |
|
162 | 167 |
|
163 | | -DtypeStr = Annotated[str, BeforeValidator(parse_dtype_v3)] |
| 168 | +DTypeStr = Annotated[str, BeforeValidator(parse_dtype_v3)] |
| 169 | +DTypeLike = DTypeStr | AnyNamedConfig |
| 170 | +CodecTuple = Annotated[tuple[CodecLike, ...], BeforeValidator(ensure_multiple)] |
164 | 171 |
|
165 | 172 |
|
166 | 173 | class ArraySpec(NodeSpec, Generic[TAttr]): |
@@ -196,11 +203,11 @@ class ArraySpec(NodeSpec, Generic[TAttr]): |
196 | 203 | node_type: Literal["array"] = "array" |
197 | 204 | attributes: TAttr = cast(TAttr, {}) |
198 | 205 | shape: tuple[int, ...] |
199 | | - data_type: DtypeStr | AnyNamedConfig |
| 206 | + data_type: DTypeLike |
200 | 207 | chunk_grid: RegularChunking # todo: validate this against shape |
201 | 208 | chunk_key_encoding: DefaultChunkKeyEncoding # todo: validate this against shape |
202 | 209 | fill_value: FillValue # todo: validate this against the data type |
203 | | - codecs: tuple[AnyNamedConfig, ...] |
| 210 | + codecs: CodecTuple |
204 | 211 | storage_transformers: tuple[AnyNamedConfig, ...] = () |
205 | 212 | dimension_names: tuple[str | None, ...] | None = None # todo: validate this against shape |
206 | 213 |
|
@@ -252,7 +259,7 @@ def from_array( |
252 | 259 | chunk_grid: Literal["auto"] | AnyNamedConfig = "auto", |
253 | 260 | chunk_key_encoding: Literal["auto"] | AnyNamedConfig = "auto", |
254 | 261 | fill_value: Literal["auto"] | FillValue = "auto", |
255 | | - codecs: Literal["auto"] | Sequence[AnyNamedConfig] = "auto", |
| 262 | + codecs: Literal["auto"] | Sequence[CodecLike] = "auto", |
256 | 263 | storage_transformers: Literal["auto"] | Sequence[AnyNamedConfig] = "auto", |
257 | 264 | dimension_names: Literal["auto"] | Sequence[str | None] = "auto", |
258 | 265 | ) -> Self: |
@@ -293,11 +300,11 @@ def from_array( |
293 | 300 | else: |
294 | 301 | fill_value_actual = fill_value |
295 | 302 |
|
296 | | - codecs_actual: Sequence[AnyNamedConfig] |
| 303 | + codecs_actual: tuple[CodecLike, ...] |
297 | 304 | if codecs == "auto": |
298 | 305 | codecs_actual = auto_codecs(array) |
299 | 306 | else: |
300 | | - codecs_actual = codecs |
| 307 | + codecs_actual = tuple(codecs) |
301 | 308 | storage_transformers_actual: Sequence[AnyNamedConfig] |
302 | 309 | if storage_transformers == "auto": |
303 | 310 | storage_transformers_actual = auto_storage_transformers(array) |
@@ -1017,10 +1024,14 @@ def auto_fill_value(data: object) -> FillValue: |
1017 | 1024 | raise ValueError("Cannot determine default data type for object without shape attribute.") |
1018 | 1025 |
|
1019 | 1026 |
|
1020 | | -def auto_codecs(data: object) -> tuple[AnyNamedConfig, ...]: |
| 1027 | +def auto_codecs(data: object) -> tuple[CodecLike, ...]: |
| 1028 | + """ |
| 1029 | + Automatically create a tuple of codecs from an arbitrary python object. |
| 1030 | + """ |
1021 | 1031 | if hasattr(data, "codecs"): |
| 1032 | + # todo: type check |
1022 | 1033 | return tuple(data.codecs) |
1023 | | - return () |
| 1034 | + return ({"name": "bytes"},) |
1024 | 1035 |
|
1025 | 1036 |
|
1026 | 1037 | def auto_storage_transformers(data: object) -> tuple[AnyNamedConfig, ...]: |
|
0 commit comments