Skip to content

feat(zarr-metadata): composition rules layer, shape-exact entity validators, create_* factories - #4379

Draft
d-v-b wants to merge 9 commits into
zarr-developers:mainfrom
d-v-b:zarr-metadata/rules-layer
Draft

d-v-b wants to merge 9 commits into
zarr-developers:mainfrom
d-v-b:zarr-metadata/rules-layer

Conversation

@d-v-b

@d-v-b d-v-b commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

🤖 AI text below 🤖

Part 2 of a split of d-v-b#296 (reviewed on the fork as d-v-b#318).

Depends on #4378. Cross-fork PRs can't be stacked, so this targets main and its diff includes #4378's commits; review only the commits after chore(zarr-metadata): number changelog fragments for #4378, or the diff against #4378's branch: d-v-b/zarr-python@zarr-metadata/types-and-tuples...zarr-metadata/rules-layer

What this adds

  • zarr_metadata.rules: composition judgments over whole documents. Fill value vs data type for every dtype family; codec pipeline ordering (AA* AB BB*, exactly one AB, inconclusive with unknown codecs); known-name configuration shapes; dimension_names count; chunk-grid values and geometry (regular rank; rectilinear rank and per-dimension sums incl. RLE pairs); transpose orders; sharding (inner pipelines judged recursively, inner chunks positive, rank-matched, and dividing the enclosing chunk); struct field rules. Codec chains are judged against the array each codec receives, so a shard behind a transpose must divide the transposed chunk and a bytes codec behind a cast_value needs an endianness for the target type. Rules are registered where they are defined; rules about one codec, grid, or data type live with that entity under rules._entities and are dispatched by name.
  • Read-side front door: validate_* / is_* / parse_* / check_* for array and group documents in both formats, structure and composition reported together. check_* returns Valid[T] | Invalid.
  • v3._shape: one exact validator per known entity, key sets derived from the TypedDicts' __annotations__ / __required_keys__. Unknown configuration members report as their own unknown_key kind and never suppress other findings.
  • create_* factories in zarr_metadata.builder, one per public document TypedDict, **kwargs: Unpack[<TypedDict>], one combined raise. The strict on-disk factories are create_zarr_v2_zarray_json / create_zarr_v2_zgroup_json.

Boundary changes

  • v3 dimension_names vs shape and v2 chunks vs shape move from the structural validator into rules. model parsers and dataclasses now accept those documents.
  • Pydantic field types run the rules layer before normalizing, so they are strictly stronger than before rather than losing the two moved checks. Reversible if you'd rather keep them structural-only.
  • A bare bytes inside sharding index_codecs is rejected: the shard index is uint64, so the endianness is required there too.

Relative to d-v-b#296

Dropped: the incremental builder, the extension-point provenance table, and TypeIs codec guards (held back for a later PR); fill-value propagation through codec chains (no rule read it, so ArraySpec is two fields and None replaces the UNKNOWN sentinel); registry introspection helpers with no non-test consumer; a duplicated v2 consolidated envelope check. README and docs no longer claim the model layer checks dimension counts.

Entity rules recursively validate nested data types in struct fields and cast targets. A Hypothesis regression caught invalid datetime/timedelta scale factors inside those positions. Remaining follow-ups: sharding under a rectilinear grid is unjudged; v2 fill-value/dtype consistency has no rule.

Rebased onto main. Main now bans runtime assert (#4363), so the six create_* factories share a _parsed_or_raise helper and the rule-registration import uses importlib.import_module.

Verified after rebase: ruff check/format (package and repo root), pyright 1.1.404 (0 errors), 756 package tests.

🤖 Generated with Claude Code

d-v-b and others added 3 commits September 19, 2026 18:52
… validators return tuples

`ZarrV3NamedConfigJSON.name` and `.configuration` are `ReadOnly` and the
envelope is `closed`, so the concrete codec / chunk-grid / chunk-key-encoding
/ data-type TypedDicts are assignable to the fields they describe. Every
concrete `*Object` / `*Configuration` is `closed` and object forms declare
`must_understand: NotRequired[bool]`.

Every `validate_*` in `zarr_metadata.model` returns
`tuple[ValidationProblem, ...]`, `MetadataValidationError.problems` is a
tuple, and `load_store_json` returns `object` rather than `Any`. `ANN401`
is enforced package-wide. `ZarrV2ConsolidatedMetadataJSON.zarr_consolidated_format`
is `Literal[1]`.

Split from #296 (part 1 of 3).

Assisted-by: ClaudeCode:claude-fable-5-1
Assisted-by: ClaudeCode:claude-fable-5-1
…arr-developers#4365's tests

Assisted-by: ClaudeCode:claude-opus-5
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added needs release notes Automatically applied to PRs which haven't added release notes zarr-metadata Specific to the zarr-metadata sub-package labels Sep 19, 2026
d-v-b and others added 6 commits September 19, 2026 18:55
…4378

Assisted-by: ClaudeCode:claude-opus-5
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…dators, create_* factories

Adds `zarr_metadata.rules`: cross-field judgments over whole documents
(fill value vs data type, codec pipeline ordering, chunk-grid geometry,
sharding and transpose against the array each codec actually receives,
struct field rules, dimension-name counts), registered where they are
defined and dispatched per entity; `validate_*` / `is_*` / `parse_*` /
`check_*` front doors for readers; `v3._shape` validators derived from the
entity TypedDicts; and one `create_*` factory per document TypedDict.

The two rank checks (v3 `dimension_names` vs `shape`, v2 `chunks` vs
`shape`) move from the structural validator into the rules layer. The
pydantic field types now run the rules layer before normalizing, so they
are strictly stronger than before rather than weaker.

Relative to #296 this drops the incremental builder, the
extension-point provenance table, `TypeIs` codec guards, fill-value
propagation through codec chains (no rule read it), registry
introspection helpers, and a duplicated v2 consolidated envelope check;
renames the `.zarray`/`.zgroup` factories to `zarray`/`zgroup`; and
requires an endianness on the `bytes` codec inside `index_codecs`.

Split from #296 (part 2 of 3).

Assisted-by: ClaudeCode:claude-fable-5-1
Assisted-by: ClaudeCode:claude-fable-5-1
…eate_* factories

Upstream enabled ruff S101 for runtime code (zarr-developers#4363). The six
parse-then-raise factories share a _parsed_or_raise helper that narrows
the parsed document, and the rule-registration import uses
importlib.import_module instead of an assert to keep it referenced.

Assisted-by: ClaudeCode:claude-opus-5
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…4379

Assisted-by: ClaudeCode:claude-opus-5
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@d-v-b
d-v-b force-pushed the zarr-metadata/rules-layer branch from 7f19f4e to feffe5c Compare September 19, 2026 16:55
@read-the-docs-community

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs release notes Automatically applied to PRs which haven't added release notes zarr-metadata Specific to the zarr-metadata sub-package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant