You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit daef49b
Browse filesBrowse the repository at this point in the historyBrowse files
feat(zarr-metadata): export base64_fill_value_rules for data type definitions (#4609)
* feat(zarr-metadata): export base64_fill_value_rules for data type definitions
Export the rule that rejects a fill value string that is not
standard-alphabet base64, so a data type defined outside the package can
pass it as `fill_value_rules` or yield from it. `bytes` and
`variable_length_bytes` now share it. The parsing and validation guide
shows a data type that defines `fill_value_rules`, built with `union`.
Rebased onto main as one commit; the earlier history carried the merged
ZARR_PYTHON_CONTEXT branch.
Assisted-by: ClaudeCode:claude-opus-5-5
* docs(zarr-metadata): number the changelog fragment for #4609
Assisted-by: ClaudeCode:claude-opus-5-5
Export `base64_fill_value_rules` from `zarr_metadata.v3.data_type.bytes`, the rule that rejects a fill value string that is not standard-alphabet base64, so a data type defined outside the package can pass it as `fill_value_rules` for a `Base64Bytes` fill value, or yield from it in a rule of its own. The guide to parsing and validation now shows how to define a data type with `fill_value_rules`.
`union` keeps every definition the context has, and raises `ScopeConflictError` if `MY_CODEC` has a name the context already files with another definition. To replace a definition on purpose, use `copy`, which, like pydantic's `model_copy(update=...)`, replaces whole each slot it is given and keeps the others. A slot can be given as a mapping from each definition's name to it, so `CORE_AND_EXTENSIONS_CONTEXT.copy(update={"codecs": {**CORE_AND_EXTENSIONS_CONTEXT.codecs, "gzip": MY_GZIP}})` replaces the gzip definition with your own. Passing `copy(update={"codecs": [MY_CODEC]})` instead would leave `MY_CODEC` as the only codec.
273
273
274
+
A data type's definition also says what its fill value may be. `fill_value` is the fill value's JSON shape, as a type annotation. `fill_value_rules` is a function of the configuration, the nested declarations and the fill value, that yields each problem the shape cannot express. For example, `Base64Bytes` is a string, so an annotation accepts any string, and the rule `base64_fill_value_rules` rejects one that is not base64. A rule of your own can yield from it, then check what depends on the configuration:
for fill_value in ("YWI=", "not base64!", "YWJj"):
310
+
print(fill_value, [problem.message for problem in fill_value_problems(data_type, fill_value)])
311
+
# YWI= []
312
+
# not base64! ['expected standard-alphabet base64, got "not base64!"']
313
+
# YWJj ['expected fewer bytes than the type holds']
314
+
```
315
+
316
+
A reader of a document in this context checks its fill value with these rules, and so does a pydantic field. A JSON Schema written for the context does not contain the rules, so it accepts any string here.
317
+
274
318
[^1]: The one exception is the [`r*` data type](https://zarr-specs.readthedocs.io/en/v3.1.0/v3/core/index.html#data-types), which is identified by a templated string with no `configuration` object. This mixes the identifier with the configuration in the same JSON value, which is not good design. So this library normalizes `r*` into the same form as all the other metadata objects. The declaration `"r16"` keeps its name. The `r*` definition reads it with the configuration `{"bits": 16}`.
0 commit comments