Skip to content

reject a table redefined after its parent table header#516

Merged
frostming merged 1 commit into
python-poetry:masterfrom
netliomax25-code:reject-redefined-table
Jun 15, 2026
Merged

reject a table redefined after its parent table header#516
frostming merged 1 commit into
python-poetry:masterfrom
netliomax25-code:reject-redefined-table

Conversation

@netliomax25-code

Copy link
Copy Markdown
Contributor

Summary

  1. Container.append sends a concrete [table] header that lands inside an existing implicit/super table through _validate_table_candidate, which only flagged a child key on a type change or a dotted-key redefinition.
  2. A child table header that was already defined fell through to a plain merge, so a subtable redefined after its parent received its own header ([a.b] / [a] / [a.b], and the deeper [a.b.c] / [a] / [a.b.c]) was merged silently instead of rejected, while tomllib and the toml-test suite treat these as declaring the same table twice.
  3. _validate_table_candidate now raises when the existing child and the candidate child are both concrete (non-super) tables, and recurses through matching super tables so a duplicate nested deeper is caught as well. Valid out-of-order tables (a new sibling subtable, or extending a table with a deeper header) keep parsing, and the existing tests plus the toml-test 1.0/1.1 suites pass.

Agent Drafting Metadata

  • Agent:
  • Model:
  • Notes:

@frostming

Copy link
Copy Markdown
Contributor

2. while tomllib and the toml-test suite treat these as declaring the same table twice.

Hmm, we are using toml-test suite but why it isn't caught?

@netliomax25-code

Copy link
Copy Markdown
Contributor Author

The suite just does not have a case with this shape. Its table-redefinition cases all re-declare the parent/super header, not the child:

  • invalid/table/super-twice is [a.b] / [a] / [a]
  • invalid/table/duplicate-key-09 is [a] / [a.b] / [a]
  • invalid/table/duplicate-key-01 is [a] / [a]

tomlkit already rejects all of those. The gap is when the child header is the repeated one with the parent declared in between, like [a.b] / [a] / [a.b]. That falls through to a plain merge: on master it parses to {"a": {"b": {}}}, and [a.b] / x=1 / [a] / [a.b] / y=2 silently drops x=1. tomllib rejects both.

Since toml-test is a submodule we do not own, I added the regression cases in tests/test_parser.py. Happy to send the input upstream to BurntSushi/toml-test too if you want it covered there.

@frostming
frostming merged commit 7c978a5 into python-poetry:master Jun 15, 2026
25 checks passed
chuenchen309 added a commit to chuenchen309/tomlkit that referenced this pull request Jul 17, 2026
`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>
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.

2 participants