Skip to content

feat(zarr-metadata): with_problems groups a read's problems by field, and canonical_of spells a field read already - #377

Open
d-v-b wants to merge 1 commit into
feat/zarr-metadata-constraintsfrom
feat/zarr-metadata-field-problems
Open

d-v-b wants to merge 1 commit into
feat/zarr-metadata-constraintsfrom
feat/zarr-metadata-field-problems

Conversation

@d-v-b

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

Copy link
Copy Markdown
Owner

🤖 AI text below 🤖

Stacked on #376. It adds the third item on the pydantic/zod list: problems grouped by field, and the canonical form of a field already read.

1. with_problems(fields, problems) gives each field of one read with its problems. Grouping is a function over the problems, as zod's flattenError is over the issues; the readings don't change shape. It takes a reading's fields() and problems, or fields_of a field and the problems resolve gave with it.

  • What a field's problems are. Every problem located in the field, including those in the fields it holds, so a shard lists its inner codec's problem too. They cover what it was read with and what the document found with it where it stands: its place in the pipeline, the chunk it is handed, the array's shape. A field with none is valid there.
  • A problem in no field (the fill value, the shape) is in none's.
  • Each field comes before the fields it holds, so the last field that lists a problem is the innermost field holding it.

2. canonical_of(field, problems) gives a field already read in its simplest spelling, without reading it again.

  • It returns None for a field with a problem, as canonicalize does. So it never erases an undeclared key, a stray envelope member or a must_understand, and never spells what doesn't hold.
  • canonicalize is now canonical_of(*resolve(...)). A test checks that the two agree on every example field.
reading = read_array_metadata_v3(raw)
simplest = {
    loc: canonical_of(field, problems)
    for loc, field, problems in with_problems(reading.fields(), reading.problems)
}

Nothing breaks: fields() is unchanged, and canonicalize gives what it gave.

Scenario re-runs. All four replay unchanged.

  • Metadata cleaner. Its field_at helper is gone: the innermost field holding a problem is the last one with_problems lists it under. Its notes pass calls canonical_of instead of re-reading each field through canonicalize, and it reports unknown extensions straight from the fields. It shrank from 1,501 to 1,486 lines, and it writes the same repaired files.
  • OME-Zarr library, zarr-python swap, new codecs. No changes.

Reviews

  • roborev found two things, both fixed:
    • canonical_of without the problems could quietly spell a field canonicalize refuses. It now takes them.
    • A group grouped its documents' problems twice. Grouping is now a single function call.
  • A design review changed three things:
    • fields() stays as it is, and grouping is with_problems, as zod has it. The first version changed fields() to yield triples; that was a breaking change, and it gave a resolve user no way to group.
    • The docs no longer claim a field's problems are only what resolve gives. They include what the document finds with it, so "none" means valid in this document.
    • It asked for a test of a problem at a field's own place. It's added: a codec out of the pipeline's order.

Choices worth a look

  1. A field lists the problems of the fields it holds. That makes "none" mean valid as a whole, where zod's treeifyError puts an issue only at its exact node. Innermost attribution is still one step away, as above.
  2. canonical_of takes the problems as an argument. Passing () gets a spelling regardless; that's explicit at the call.

🤖 Generated with Claude Code

… and canonical_of spells a field read already

`with_problems(fields, problems)` gives each field of one read -- a
reading's `fields()` and `problems`, or `fields_of` a field and the
problems `resolve` gave with it -- with the problems located in it, in
the fields it holds too: those it was read with, and those the document
found with it where it stands, its place in the pipeline, the chunk it
is handed, the array's shape. A field with none is valid there. A
function of the problems, as zod's `flattenError` is of the issues,
grouping them at every depth; a problem in no field is in none's.

`canonical_of(resolved, problems)` spells a field a scope has read
already, given its problems, in its simplest equivalent spelling,
without reading it again: None for a field with a problem, as
`canonicalize` gives, so a key its TypedDict does not declare is never
erased. `canonicalize` is `canonical_of(*resolve(...))`.

Assisted-by: ClaudeCode:claude-opus-5-5

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant