From fe46347f8960c65825ed47ec70ddc6ce19331d2f Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Thu, 18 Jun 2026 23:19:30 +0200 Subject: [PATCH] Fix stale dotted-key prefix when replacing a table value with a plain dict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After parsing 'fruit.apple = true', the key 'fruit' in the document body retained _dotted=True from _handle_dotted_key. When the entire table was then replaced via doc['fruit'] = {'a': 1}, _replace_at reused the old key object (same name + same kind), keeping the stale _dotted flag. This caused rendering to both emit a [fruit] header (because the new table is not a super-table) and prefix every child with 'fruit.' (because key.is_dotted() was true), producing the invalid output '[fruit]\nfruit.a = 1' instead of 'a = 1'. Fix: clear _dotted on the reused key in _replace_at. The dotted heritage belongs to the original parse — a full table replacement gets fresh rendering behaviour. Incremental modifications (like doc['fruit']['banana'] = 2) are unaffected because they don't reach the document-level _replace_at path. --- CHANGELOG.md | 1 + tests/test_items.py | 12 ++++++++++++ tomlkit/container.py | 1 + 3 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26584a1c..e8552dc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - Fix `comment()` producing invalid TOML for a multiline string by prefixing every line with `#`, not just the first. ([#449](https://github.com/python-poetry/tomlkit/issues/449)) - Fix the separator comma being swallowed by a trailing comment when appending a key to a multiline inline table, leaving the new key without a separator so the result no longer round-trips. ([#512](https://github.com/python-poetry/tomlkit/issues/512)) - Fix a `KeyAlreadyPresent` error when parsing or accessing an out-of-order table whose array-of-tables elements are split across the table's parts. ([#505](https://github.com/python-poetry/tomlkit/issues/505)) +- Fix a stale dotted-key prefix when replacing a dotted-key table value with a plain dict, where ``fruit.apple = true`` followed by ``doc['fruit'] = {'a': 1}`` would render a ``[fruit]`` header followed by the wrongly dotted ``fruit.a = 1`` instead of ``a = 1``. ([#524](https://github.com/python-poetry/tomlkit/issues/524)) ## [0.15.0] - 2026-05-10 diff --git a/tests/test_items.py b/tests/test_items.py index b4653e72..65304017 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -1236,6 +1236,18 @@ def test_serialize_table_with_dotted_key() -> None: assert parent.as_string() == "[a]\nb.c = 1\n" +def test_replacing_dotted_key_table_clears_prefix() -> None: + """Replacing a dotted-key table with a plain dict should not carry + over the dotted prefix to the new children (#524).""" + doc = parse("fruit.apple = true\n") + doc["fruit"] = {"a": 1} + rendered = doc.as_string() + assert rendered == "[fruit]\na = 1\n" + # Round-trip + doc2 = parse(rendered) + assert doc2["fruit"]["a"] == 1 + + def test_not_showing_parent_header_for_super_table() -> None: doc = api.document() diff --git a/tomlkit/container.py b/tomlkit/container.py index 75e09018..314100db 100644 --- a/tomlkit/container.py +++ b/tomlkit/container.py @@ -829,6 +829,7 @@ def _replace_at( new_key = SingleKey(new_key) else: # Inherit the sep of the old key new_key = k + new_key._dotted = False # Reset dotted heritage on full replacement del self._map[k] self._map[new_key] = idx