Skip to content

fix: don't reject an out-of-order super-table extended by a dotted key#565

Open
chuenchen309 wants to merge 1 commit into
python-poetry:masterfrom
chuenchen309:fix/out-of-order-supertable-dotted-key
Open

fix: don't reject an out-of-order super-table extended by a dotted key#565
chuenchen309 wants to merge 1 commit into
python-poetry:masterfrom
chuenchen309:fix/out-of-order-supertable-dotted-key

Conversation

@chuenchen309

Copy link
Copy Markdown

Summary

tomlkit rejects valid TOML on the parse() path when an out-of-order table reopens an implicit super-table with a dotted key.

[a.b.c] makes a.b an implicit super-table. Reopening [a] and extending a.b with a dotted key (b.z = 1) only adds a key to that super-table — it is not a redefinition — but Container._validate_table_candidate raises Redefinition of an existing table:

import tomlkit, tomllib
doc = "[a.b.c]\n[a]\nb.z = 1\n"
tomllib.loads(doc)   # -> {'a': {'b': {'c': {}, 'z': 1}}}   (accepted)
tomlkit.parse(doc)   # -> ParseError: Redefinition of an existing table at line 3 col 0

stdlib tomllib — the validity oracle the existing tests already cite (test_reject_out_of_order_dotted_* reference it) — accepts it.

Cause: the if k.is_dotted(): raise guard in _validate_table_candidate fires before the super-table recursion right below it, so a dotted key whose existing side is a super-table never gets to the recursion that would check the subtree for a real conflict. The dotted rejection is only correct when the existing side is a concrete table.

Fix: only take the dotted-key rejection when the existing entry is not a super-table; otherwise fall through to the existing recursion (which raises only on an actual redefinition). Two lines.

The concrete-table case ([a.b] then [a] b.z = 1) stays rejected, matching tomllib and the redefinition rejections added in #516 / #530 — all 27 out-of-order / redefinition tests still pass, and the full suite passes (372, including the new regression test). The target case now parses, round-trips byte-for-byte, and unwrap() matches tomllib.

Regression test added next to the sibling out-of-order tests in tests/test_toml_document.py; it is red before the fix (ParseError) and green after.

Note: I also noticed a separate pre-existing defect where parse() succeeds but .unwrap() raises KeyAlreadyPresent on some valid AoT-based out-of-order docs (e.g. [[y.a]]\n[b.d.x]\n[[y.a.c]]). That's a different code path (AoT merge) and is not touched here — flagging only in case it's of interest.

Agent Drafting Metadata

`Container._validate_table_candidate` rejected valid TOML: when a deep header
like `[a.b.c]` makes `a.b` an implicit super-table, reopening `[a]` and
extending `a.b` via a dotted key (`b.z = 1`) was rejected with "Redefinition of
an existing table". No table is defined twice -- `a.b` only gains a new key --
and stdlib `tomllib` (the validity oracle the tests already use) accepts it,
producing `{'a': {'b': {'c': {}, 'z': 1}}}`.

The `if k.is_dotted(): raise` guard fired before the super-table recursion that
follows it, so it never got the chance to validate the subtree for a real
conflict. Only reject the dotted case when the existing entry is *not* a super
table; otherwise fall through to the existing recursion, which raises only on an
actual redefinition. The concrete-table case (`[a.b]` then `[a]` `b.z = 1`)
stays rejected, matching tomllib and the redefinition rejections from python-poetry#516/python-poetry#530.

Adds a regression test next to the sibling out-of-order tests; it is red before
the fix (ParseError) and green after. Full suite passes (372).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chuenchen309
chuenchen309 force-pushed the fix/out-of-order-supertable-dotted-key branch from 834e3e8 to d2c954e Compare July 17, 2026 05:14
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.

1 participant