feat!: rewrite toon_format with full TOON 4.1 support - #68
Conversation
Rewrite the package from scratch as a standard uv project: - New `toon` module with a json-style API: dumps, dump, loads, load, ToonDecodeError (with line/source), Delimiter, __toon_spec__. - Full TOON specification 4.1: keyed tabular objects, nested field groups, comment lines, canonical empty arrays, and all strict-mode checks. Every official fixture of spec v4.1.1 passes. - json-style options: default and sort_keys for encoding; parse_float, parse_int, object_hook and object_pairs_hook for decoding. - Host types: dataclasses, enums, UUIDs, sets, lossless Decimal, Pydantic. - CLI: --check, --json-indent, python -m toon; UTF-8 and LF on every platform. - `toon_format` stays as a deprecated wrapper over the new API. - Differential tests against toons and Hypothesis round-trip tests. - MkDocs documentation for Read the Docs (not published yet). - uv_build backend, CI on Python 3.10-3.14, trusted publishing with uv. - Simplified dev container; 88-character lines, explained in CONTRIBUTING.md. - AGENTS.md documents the project rules. Fixes #33, #47, #61, #62, #63, #64, #65. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Raise ValueError when mapping keys collide after conversion to strings
({1: "a", "1": "b"}) instead of silently dropping a value.
- Quote strings starting with U+FEFF so that a root string survives the
byte-order-mark removal of the decoder (§12).
- Pass `strict` from ToonPydanticModel.model_validate_toon to both the TOON
decoder and pydantic; None keeps strict decoding and the model's own
validation settings.
- Make the CLI refuse to write ±inf from non-strict decoding as invalid JSON.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/benchmark.py times encoding and decoding of the working tree, and compares it with any git revision checked out in a temporary worktree (0.9 revisions are driven through toon_format.encode/decode). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `toon --stats` prints token counts of compact JSON, indented JSON, and TOON to standard error. Counting is offline through tiktoken (the tokens extra), so the figures are exact for OpenAI models only; the 0.9 token helpers now share the same code in toon._tokens. - Instances of attrs classes encode as objects of their fields, detected through __attrs_attrs__ without importing attrs. - Document how to encode pandas DataFrames and NumPy values. - Add packaging/toon-python, an alias distribution that only depends on toon-format, and publish it from the Publish workflow, which checks that its version matches. - Replace the per-person copyright lines with "The toon-python authors" and list them, including earlier contributors, in AUTHORS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The repository root is now the toon-python distribution (module `toon`, the `toon` command, the pydantic and tokens extras). - packaging/toon-format builds toon-format, which depends on toon-python at the same version and carries the deprecated toon_format module, its tests, and the `toon` command. Keeping those files in toon-format is what lets `pip install -U toon-format` from 0.9 keep `toon_format` and the command: pip removes the files of the old toon-format after installing toon-python. - CI builds both, smoke-tests both wheels, and runs the toon_format tests from packaging/toon-format. The Publish workflow uploads toon-python and then toon-format, and checks that the versions and pins match. - Documentation, badges, and extras now name toon-python. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add `toon encode`, `toon decode`, `toon check`, and `toon stats`, each with only the options it needs. `stats` prints the token counts of JSON and of TOON with every delimiter, compared with compact JSON; `check` validates several files and reports `file:line: message`. The unreleased `--stats`, `--check`, and `--json-indent` flags are removed. `toon FILE [options]` keeps the 0.9 interface unchanged. Rework the README: absolute links for PyPI, measured token savings, usage examples, and the CLI reduced to `toon stats`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Hi @johannschopplich, friendly ping on this one. Would you have a moment to take a look? Happy to adjust anything. |
|
Hi there! I'm aware. I'll follow up as soon as I can in the next days. Currently busy with a work project. |
|
@johannschopplich Thank you! |
Drop the AUTHORS file and the keywords and classifiers from toon-format, which only carries the deprecated 0.9 API. Its LICENSE no longer points to an AUTHORS file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before each commit, prek runs ruff check --fix, ruff format, mypy, and mkdocs build --strict (only when docs/, src/, or mkdocs.yml change). The manual stage also runs both test suites, so `uv run prek run --all-files --hook-stage manual` covers every CI check. The dev container installs the hooks from a post-create script, kept in a file because Zed joins lifecycle command arguments without quoting them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
johannschopplich
left a comment
There was a problem hiding this comment.
Hey there! Thanks for bringing spec v4.1.1 to this TOON port.
What blocks the merge is the name. #50 and #51 asked for toon-python, but every port publishes as toon-format (npm, crates.io, pub.dev).
To get it in:
- Build
toon-formatwith the moduletoon_formatfrom the repo root, and drop thepackaging/toon-formatshim and the two-step publish. - Keep
encode/decodeas the public names, like the other ports. - Yank
toon-python1.0.0rc1 and leave that name without releases. - Move the license rewrite and the CLI redesign into separate PRs.
Details are inline.
Address the review of #68: - Build toon-format, module toon_format, from the repository root and drop the packaging/toon-format shim. toon-python becomes an alias with no code that depends on toon-format, so that the repository's name stays taken. - Keep dumps/loads/dump/load as the primary API with keyword-only options, and add encode/decode as aliases. The 0.9 options dictionary and its types are gone. - Remove the toon command and the token helpers with the tokens extra; the README points to @toon-format/cli. - Import Self from typing on 3.11+ and declare typing-extensions in the pydantic extra for 3.10. - Write U+FEFF as an escape, document that Decimal values beyond the double range need parse_float=Decimal to decode, and drop "independent" for toons. - Restore the named copyright lines in LICENSE and remove AUTHORS; drop the Read the Docs badge until the site is live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the review. I've pushed 4d55d9f, which addresses it. Here is what changed and where I see things differently. Done
Where I disagreeThe spec defines the format. How a library exposes that format is up to the host language. This is the Python implementation, and its users are Python developers: it has to feel like Python, not like TypeScript translated into Python. Consistency across ports matters for the format, which is identical byte for byte and checked against the official fixtures. It does not matter for calling conventions, which no Python user compares across languages. 1. toon_format.dumps(value, *, delimiter=",", indent_size=2)
toon_format.loads(text, *, strict=True)
toon_format.dump(value, fp, ...)
toon_format.load(fp, ...)
toon_format.encode(value, ...) # alias of dumps
toon_format.decode(text, ...) # alias of loads
Options are keyword-only arguments, not an options dictionary. Passing options as a dict is a JavaScript idiom; no Python library does it. Keyword arguments give type checking, IDE completion, a useful 2. The CLI is removed instead of being made identical to A Python CLI that copies the TypeScript one flag for flag duplicates a tool that already exists and runs anywhere with 3.
Follow-up
|
toon module with full TOON 4.1 supporttoon_format with full TOON 4.1 support
|
Thanks for the quick turn, @alesanfra.
The Two more, since they ship with this PR:
|
|
Thanks for the quick turn on 4d55d9f – the naming and the inline points are all sorted. What's left is subtraction:
|
|
So basically I need to drop most of the innovations, nice 😎 Seriously, I don't agree with most of these points. Here's where I stand:
This is a community project, so I think some compromise on both sides makes sense. I've already changed a lot since my first proposal, and I honestly believe these additions will make the project better in the long run. So here's what I propose: I drop Let me know how you want to proceed. I'll wait for your answer before pushing any changes. |
|
Hey there, fair pushback. Sorry, my last comment moved the goalposts after you'd turned the first round around within a day. I didn't mean to undersell you PR. Your proposal works for me ( One thing before merging: the README links |
johannschopplich
left a comment
There was a problem hiding this comment.
Approving so you can merge whenever you're ready.
Remove packaging/toon-python and its build and publish steps, the toons dev dependency and test_toons_compat.py. Point the documentation links to docs/ until Read the Docs is set up, trim AGENTS.md to rules that do not go stale, and move the release steps to CONTRIBUTING.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rewrites
toon_formatagainst TOON specification 4.1.json-style API:dumps,dump,loads,loadwith keyword-only options;encodeanddecodeare aliases.v4.1.1passes.Decimal, and Pydantic models encode directly; unsupported values raiseTypeErrorinstead of becomingnull.tooncommand is removed in favor ofnpx @toon-format/cli.Breaking changes and the upgrade path from 0.9 are in
docs/migration.md.Before the first release, the
toon-formatproject on PyPI needs a trusted publisher forpublish.ymlwith thepypienvironment.Closes #33, closes #47, closes #61, closes #62, closes #63, closes #64, closes #65.
🤖 Generated with Claude Code