Skip to content

Fix dotted-key child replacement capturing following siblings - #575

Open
shleder wants to merge 2 commits into
python-poetry:masterfrom
shleder:fix/dotted-key-child-siblings
Open

Fix dotted-key child replacement capturing following siblings#575
shleder wants to merge 2 commits into
python-poetry:masterfrom
shleder:fix/dotted-key-child-siblings

Conversation

@shleder

@shleder shleder commented Jul 28, 2026

Copy link
Copy Markdown

Summary

Fixes #556.

When a dotted-key child such as b in a.b = 1 is replaced with a table or array of tables through an out-of-order table proxy, that part begins rendering an [a.b] or [[a.b]] header at its original body position.

Dotted siblings that follow it, such as a.c and a.d, then fall inside that table's scope, corrupting the document when serialized output is parsed again.

This change detects the transition from an inline value to a table or AoT and moves the affected part after its inline same-group siblings, while keeping it before any subsequent same-group header part.

Because moving a body entry shifts other entries as well, the change rebuilds the complete key-to-index map and out-of-order metadata. This keeps unrelated plain keys, dotted groups, and other out-of-order tables correctly addressable and mutable. Proxy-derived state is rebuilt from the unchanged table parts after reordering.

Whitespace and trivia are preserved from the source document; the change does not add or remove blank lines.

Regression coverage includes:

  • replacement with a non-empty table;
  • replacement with an empty table;
  • replacement with an array of tables;
  • exact serialization and semantic round-trip;
  • subsequent edits through the proxy;
  • interleaved plain keys;
  • interleaved dotted groups;
  • empty-table and AoT interleaving.

Validation:

  • focused updating a table captures too many keys #556/interleaving tests — 11 passed;
  • tests/test_toml_document.py — 78 passed;
  • complete suite including the toml-test corpus — 1058 passed;
  • Ruff 0.15.21 — all checks passed;
  • Ruff formatting — passed;
  • mypy 1.19.1 — no errors attributable to the changed files;
  • Python compilation — passed;
  • git diff --check — passed.

Agent Drafting Metadata

  • Agent: Claude Code orchestration using sonnet for architecture, review and test execution, and haiku as the sole code-author role
  • Model: sonnet is locally configured to route to Kimi K3; haiku is locally configured to route to Gemini 3.6 Flash
  • Notes: These identities reflect locally verified routing configuration, not backend attestation. I reviewed the generated changes and validation evidence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Shleder <197753947+shleder@users.noreply.github.com>

@davidpavlovschi davidpavlovschi left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reorder still misses later inline dotted assignments from a different top-level group. A newly rendered table/AoT header captures those assignments when there is no later same-group sibling to push it past.

Minimal reproduction on this head:

from tomlkit import parse

doc = parse("a.b = 1\nq.c = 2\n")
doc["a"]["b"] = {"x": 9}
output = doc.as_string()

assert doc.unwrap() == parse(output).unwrap()

output is currently "\n[a.b]\nx = 9\nq.c = 2\n", so reparsing makes q.c a child of a.b. The same failure occurs with an AoT replacement. The move boundary needs to account for every later root-level inline dotted assignment that a rendered header would capture, not only entries whose k.key matches group_key.key. Please add a regression with only an unrelated group after the replaced entry; the current interleaving test also has a later a.c, which masks this case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shleder

shleder commented Jul 29, 2026

Copy link
Copy Markdown
Author

Fixed the requested unrelated-group boundary case in 6f91002.

The exact a.b = 1 / q.c = 2 reproduction now serializes q.c before the new [a.b] header and preserves:

doc.unwrap() == parse(output).unwrap()

Added exact table, empty-table, and AoT regressions.

Validation:

  • focused document tests: 81 passed;
  • complete suite including initialized toml-test corpus: 1061 passed;
  • focused Ruff check and format check passed;
  • compilation passed;
  • git diff --check passed.

Full-tree Ruff reports only pre-existing style findings in the vendored toml-test submodule.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

updating a table captures too many keys

3 participants