Skip to content

fix(zarr-metadata): problems show the JSON they found, and the boundary says what the package decided - #371

Closed
d-v-b wants to merge 1 commit into
fix/zarr-metadata-object-form-fieldsfrom
fix/zarr-metadata-docs-and-messages
Closed

d-v-b wants to merge 1 commit into
fix/zarr-metadata-object-form-fieldsfrom
fix/zarr-metadata-docs-and-messages

Conversation

@d-v-b

@d-v-b d-v-b commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

🤖 AI text below 🤖

The second of three stacked PRs that act on what four users found when they tried zarr-metadata's new APIs (findings), on top of #370. It covers the findings about messages and docs: findings 3 to 7, and 12.

Problem messages show the JSON the document holds. A message used to show the Python value, and to name shapes in Python's words:

Case Before After
null where a string belongs expected a string, got None expected a string, got null
[0, 0] as a transpose order expected a permutation of 0..1, got (0, 0) expected a permutation of 0..1, got [0, 0]
3 where a union of two TypedDicts belongs expected an object or an object, got 3 expected an object, got 3
0.5 where Literal["0.5"] belongs expected one of ('0.5',), got 0.5 expected "0.5", got 0.5
a string where an array belongs expected a sequence, got 'nuclei' expected an array, got "nuclei"
"x" where an integer or an array of them belongs expected an integer or an array of an integer elements, got 'x' expected an integer or an array of integers, got "x"
null as a float32 fill value expected a number or an integer or one of ('-Infinity', 'Infinity', 'NaN') or a string, got None expected a number or a string, got null

In a union, a broader shape takes in a narrower one: a number an integer, and a string the strings a Literal names. So the float32 message for null says what shape it expected, and the fill value rule's message for a bad string, expected a number, one of ["-Infinity", "Infinity", "NaN"], or a float32 hex string, got "nan", says which strings.

A value of the wrong JSON type is invalid_type. "zarr_format": "3", or "version": 0.5 where a TypedDict says "0.5", used to be invalid_value, the same kind as a right-typed wrong value. Both are now invalid_type, so a caller can tell a wrong type from a wrong value by kind. This holds in the TypedDict checker and in the model validators alike.

Reprs are short. A scope's repr printed every definition, so help(validate_array_metadata_v3) showed a signature of about 14,000 characters. A Context now prints as Context(<33 definitions>), and that signature is 103 characters. A definition prints as CodecDefinition(name='gzip'), where it printed each function it holds at its address; a reading of one sharded array printed 10 KB.

shown is public. zarr_metadata.v3.definition.shown renders a value as these messages do, so an extension's rule messages match the package's. The new-codecs scenario had written json.dumps(name, ensure_ascii=False) by hand to match.

The validation boundary says what the package decides. The README now states three choices that the specs leave open, or settle two ways:

  • attributes may hold NaN, Infinity and -Infinity, and to_key_value writes them back as bare tokens, which a strict JSON parser refuses.
  • must_understand: false is refused at every extension point, codecs included, though the core spec names only the data type, chunk grid and chunk key encoding.
  • A chunk length of 0 is allowed along a dimension of length 0. The core spec allows it, and the regular grid spec does not.

Docstrings. The models' from_json, to_json, from_key_value and to_key_value had none. They now say what each validates and raises. No public docstring names a private function any more, and create_default no longer says a chunk length is at least 1.

Scenario re-run. All four scenarios were re-run on the stack. Their messages changed as the table says and in no other way. The re-run prompted the plural array wording, the union wording, the short definition repr and the public shown.

Choices worth a look

  • Values render as json.dumps writes them, so strings take double quotes, "C". Names and keys in a message's prose keep Python's quotes: 'gzip' requires a configuration.
  • A bare NaN fill value is still invalid_value, though its message says "not JSON". invalid_json is documented as bytes that do not decode, so that was left alone.

Reviews. roborev ran twice. The first pass found that the model validators did not apply the new invalid_type rule; they now do. The second pass found a docstring that read as if optional members were required, now fixed. A third pass, on the re-run's revisions, found nothing.

🤖 Generated with Claude Code

…ry says what the package decided

Four users of the new APIs found the same rough edges. A problem's
message showed the Python value -- `got None`, `got (1,)` -- and named
shapes in Python's words, "a sequence", "a mapping", a one-member
Literal as a one-element tuple, a union of two objects as "an object or
an object". Values now show as the JSON the document holds, shapes in
JSON's words, and a value of another JSON type than a closed set's is
`invalid_type`, so the kind tells a wrong type from a wrong value.

`help` of a validator printed its default scope in full, some 14,000
characters; a scope's repr now says how many definitions it holds.

The README's validation boundary says what the package decides where
the specs leave it open: non-finite numbers in attributes, written back
as bare tokens; `must_understand: false` refused at every extension
point; a chunk length of 0 along a dimension of length 0. The models'
I/O methods have docstrings, and no public docstring names a private
function.

Assisted-by: ClaudeCode:claude-opus-5-5
@d-v-b

d-v-b commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

🤖 AI text below 🤖

The agent folded this PR, with the rest of the stack, into #398, one pull request against main. The commits and fragments are unchanged there.

@d-v-b d-v-b closed this Oct 8, 2026
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