Skip to content

Fix KeyAlreadyPresent error when AoT is extended by a dotted-key header after an unrelated table#529

Open
gaoflow wants to merge 3 commits into
python-poetry:masterfrom
gaoflow:fix-261-aot-dotted-key-extension
Open

Fix KeyAlreadyPresent error when AoT is extended by a dotted-key header after an unrelated table#529
gaoflow wants to merge 3 commits into
python-poetry:masterfrom
gaoflow:fix-261-aot-dotted-key-extension

Conversation

@gaoflow

@gaoflow gaoflow commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #261.

When a dotted-key header like [fruit.apple.texture] appears after an unrelated table following an [[fruit]] array-of-tables, the parser creates a super-table chain that Container.append receives with the same root key (fruit) as the existing AoT. Since the new item is a table (not an explicit AoT element), append previously raised KeyAlreadyPresent.

Example that was rejected before this fix:

[[fruit]]
apple.color = "red"

[potato]

[fruit.apple.texture]
smooth = true

The fix walks the super-table produced by the parser and does a deep merge of its children into the last element of the existing AoT — the TOML spec implicitly places such dotted-key extensions under the most recent table/array-table header.

The rendering order of unrelated tables interspersed with AoT extensions is not preserved (this is a pre-existing style-preservation limitation), but the parsed data is semantically correct and round-trips.

…er after an unrelated table

When a dotted-key header like [fruit.apple.texture] appears after an
unrelated table following an [[fruit]] array-of-tables, the parser
creates a super-table chain that is appended to the document body with
the same root key ('fruit') as the existing AoT.  Container.append
saw a table (not an explicit AoT element) after an AoT and raised
KeyAlreadyPresent.

The fix walks the super-table and does a deep merge into the last
element of the existing AoT, which is where the TOML spec implicitly
places such extensions.
@dimbleby

Copy link
Copy Markdown
Contributor

Is this better or worse than #498? Why?

@gaoflow

gaoflow commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for pointing at #498. The semantic direction is the same, but this branch is intended to be a stricter superset: #498 appends the immediate super-table children into the last AoT element, while this branch recursively merges super-table children, so it also keeps existing nested table contents such as apple.texture.rough = false followed later by [fruit.apple.texture].

While checking that comparison, I found this branch was too permissive for invalid redefinitions: apple.texture = "rough" followed by [fruit.apple.texture] could replace the scalar with a table. I pushed 2b5f890 to keep those as parse errors and added tests for both the recursive merge case and the redefinition-error cases.

So compared with #498: better on recursive merge coverage after the latest commit, similar caveat on layout normalization. #498 also adds a README limitations note for that caveat; I can add the same note here if you would prefer this PR to carry the documentation change too.

Validation run locally: pytest -q (1027 passed), plus ruff check and ruff format --check on the changed files.

@dimbleby

Copy link
Copy Markdown
Contributor

I can add the same note here if you would prefer this PR to carry the documentation change too

I am just a passer by, a maintainer will have to decide which (if either) pull request to accept and what the docs should say.

But IMO if code is knowingly breaking tomlkit's promises about preserving element ordering - that wants writing down.

@gaoflow

gaoflow commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

I agree. I added a short README limitation note in 854c2fb documenting that this out-of-order AoT sub-table case preserves the data but normalizes the sub-table's physical position when serialized.

Checked locally:

  • pytest -q tests/test_toml_document.py
  • ruff check tomlkit/container.py tests/test_toml_document.py
  • ruff format --check tomlkit/container.py tests/test_toml_document.py
  • git diff --check

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.

Parser rejects out-of-order array of tables

2 participants