Skip to content

Accept a table extending an out-of-order child of an out-of-order table - #583

Open
dchaudhari7177 wants to merge 1 commit into
python-poetry:masterfrom
dchaudhari7177:fix/out-of-order-child-validation
Open

Accept a table extending an out-of-order child of an out-of-order table#583
dchaudhari7177 wants to merge 1 commit into
python-poetry:masterfrom
dchaudhari7177:fix/out-of-order-child-validation

Conversation

@dchaudhari7177

Copy link
Copy Markdown

Fixes #571.

Problem

[tool.ruff]
[tool.ruff.lint.a]
[tool.ruff.lint]
[[tool.poetry.source]]
[tool.ruff.lint.b]
tomlkit.exceptions.ParseError: Key "lint" already exists. at line 5 col 0

tomllib and tomlkit 0.13.3 / 0.14.0 / 0.15.0 all accept this; 0.15.1 and master reject it.

Cause

tool is out of order at the top level, so the last header extends an existing concrete tool fragment with a super table, which goes through Container._validate_table_candidate. That looks each candidate key up with Container.item():

existing = current.value.item(k)
if isinstance(existing, (Table, AoT)) != isinstance(v, (Table, AoT)):
    raise KeyAlreadyPresent(k)

lint is itself split across two parts of ruff — the implicit one from [tool.ruff.lint.a] and the concrete [tool.ruff.lint]. item() therefore returns an OutOfOrderTableProxy, which is neither a Table nor an AoT, so the guard reads it as "a table is being replaced by a plain value" and raises.

The intervening [[tool.poetry.source]] matters only because without it the parser keeps all the tool headers in one fragment and this validation path is never reached. Any unrelated header reproduces it.

Fix

Give the proxy its own branch, comparing against the merged view its internal container already holds. The duplicate-definition rule is preserved by asking whether any of the proxy's parts is concrete, so a genuine redefinition still raises.

The recursion now threads Containers instead of Tables — every check in the function already went through .value, so this is a rename plus the new branch, and it is what lets the proxy's internal container be passed down.

Tests

  • test_extend_out_of_order_child_of_out_of_order_table — the reported document parses, unwraps to the same shape tomllib produces, and round-trips byte-for-byte.
  • test_reject_duplicate_child_of_out_of_order_table — same shape but the last header repeats [tool.ruff.lint]; still a ParseError.
  • test_extend_out_of_order_child_at_depth — two more levels down, with two intervening headers.

I also diffed tomlkit against tomllib over the surrounding cases (duplicate [a], [a.b] twice across a split, a value/table conflict [a] x=1 … [a.x], dotted-key redefinition [a] b.c=1 … [a.b]): accept/reject now agrees with tomllib on all of them.

Full suite passes (1054 tests).

🤖 Generated with Claude Code

    [tool.ruff]
    [tool.ruff.lint.a]
    [tool.ruff.lint]
    [[tool.poetry.source]]
    [tool.ruff.lint.b]

raised `Key "lint" already exists` since 0.15.1, though tomllib and
0.15.0 accept it.

Extending an existing concrete table with a super table runs
_validate_table_candidate, which looks each candidate key up with
Container.item(). `lint` is itself split across two parts of `ruff`, so
item() returns an OutOfOrderTableProxy, not a Table -- and the
`isinstance(existing, (Table, AoT)) != isinstance(v, (Table, AoT))`
guard read that as a table being replaced by a plain value.

Give the proxy its own branch: compare against the merged view in its
internal container, and keep the duplicate-definition check by asking
whether any of its parts is concrete. The recursion now walks Containers
rather than Tables, which is all the checks ever used.

Fixes python-poetry#571
@dimbleby

dimbleby commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

why is this better or worse than the existing pull request #572?

@dchaudhari7177 you should likely have your bot check for already-open pull requests before creating new ones

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.

Regression in 0.15.1: valid out-of-order child table raises KeyAlreadyPresent

2 participants