Skip to content

feat(zarr-metadata)!: write store bytes through a JSONWriter - #4610

Merged
d-v-b merged 3 commits into
zarr-developers:mainfrom
d-v-b:feat/zarr-metadata-json-writer-config
Nov 9, 2026
Merged

d-v-b merged 3 commits into
zarr-developers:mainfrom
d-v-b:feat/zarr-metadata-json-writer-config

Conversation

@d-v-b

@d-v-b d-v-b commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

This factors out the details of configuring the JSON output of a metadata document to a dedicated JSONWriter class, so that we don't end up filling up **kwargs of methods that write JSON with implementation details of json.dumps. Users can declare a JSONWriter explicitly, or via a configuration dict.

cc @zarr-developers/python-core-devs, I think this pattern (a single entity, configured and passed to the relevant routines, controls JSON writing) is nicer than what we are currently doing in zarr itself, where we control JSON serialization indirectly with the global config object.

This is a zarr-metadata-specific so ima merge it when green.

🤖 AI text below 🤖

Agent summary

Breaking: to_key_value takes a writer instead of an indent keyword. A JSONWriter owns how a document becomes JSON bytes: it holds the settings (indent, sort_keys, ensure_ascii, trailing_newline), and its encode method does the writing. to_key_value also accepts a plain mapping of the settings, so to_key_value(indent=2) becomes to_key_value({"indent": 2}), with no import.

  • JSONWriter is a final, frozen dataclass in zarr_metadata.model._json_writer, exported from zarr_metadata and zarr_metadata.model. encode holds every choice about the bytes: the separators of Python's json module, non-finite numbers written as bare tokens (as zarr-python writes attributes), and lone surrogates written as escapes when ensure_ascii is off. The private dump_store_json helper is gone, so nothing else writes store JSON.
  • JSONWriterSchema is the closed TypedDict of the settings, every one optional. JSONWriterLike = JSONWriter | JSONWriterSchema is what each model's to_key_value(writer=...) accepts. JSONWriter.from_dict turns either into a writer (a writer passes through), and to_dict goes back.
  • The defaults are those of json.dumps, which the models already used, so a bare to_key_value() writes the same bytes as before.
  • A setting that json.dumps would accept but misread raises when the writer is built: indent=True (read as 1), a negative indent (read as 0), or a string indent that is not JSON whitespace (written verbatim, giving invalid JSON). A mapping key that is not a setting raises TypeError, so a misspelled setting is not ignored.
  • docs/models.md gains a "Writing store bytes" section with a tested example.

tests/model/test_json_writer.py checks, for all five model types and a range of writers given both as JSONWriter and as mappings, that each key's bytes are what json.dumps writes under the same settings, and that the store reads back to an equal model. Each refused value has its own test.

The design moved from an earlier JSONWriterConfig settings object, which a separate private function read, to one object that both holds the settings and encodes, and accepts a mapping so callers need not import it. The agent rebased onto main as one commit and had a separate agent review the redesign. That review found no correctness bugs, and its two actionable points (subclasses passing through from_dict, and gaps in the success cases) are fixed here.

🤖 Generated with Claude Code

d-v-b added 2 commits November 9, 2026 12:50
`to_key_value` takes a writer instead of an `indent` keyword. A
`JSONWriter` owns how a document becomes JSON bytes: it holds the
settings (`indent`, `sort_keys`, `ensure_ascii`, `trailing_newline`)
and its `encode` method does the writing, so the separators, the bare
non-finite tokens and the surrogate escapes are no longer split off in
a private helper. `to_key_value` also accepts a mapping of the settings,
`JSONWriterSchema`, so callers need not import the class:
`to_key_value({"indent": 2})`.

The defaults are those of `json.dumps`, so a bare `to_key_value()` writes
the same bytes as before. A setting `json.dumps` would misread raises,
and so does a mapping key that is not a setting.

Rebased onto main as one commit, replacing the JSONWriterConfig design.

Assisted-by: ClaudeCode:claude-opus-5-5
@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 Nov 9, 2026
@read-the-docs-community

read-the-docs-community Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Documentation build overview

📚 zarr-metadata | 🛠️ Build #35764970 | 📁 Comparing 4a3e5fa against latest (9783489)

  🔍 Preview build  

2 files changed
± models/index.html
± api/model/index.html

@d-v-b
d-v-b marked this pull request as ready for review November 9, 2026 13:34
@d-v-b
d-v-b merged commit ee1b44c into zarr-developers:main Nov 9, 2026
45 checks passed
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