diff --git a/CHANGELOG.md b/CHANGELOG.md index faad379..fde8c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - 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)) - Out-of-order value-vs-table and dotted-key-vs-table redefinitions are now rejected at parse time instead of being silently accepted or raising only on access. The parser also detects when a non-dotted key is a prefix of an existing dotted key, matching the stdlib `tomllib` behaviour. ([#523](https://github.com/python-poetry/tomlkit/issues/523)) - Reject tables inserted into inline tables instead of serializing invalid TOML. ([#531](https://github.com/python-poetry/tomlkit/issues/531)) +- Fix assigning an array of tables over a dotted key (e.g. `doc["a"] = aot(...)` where `a` came from `a.b = ...`): the new `[[a]]` header kept the dotted key's inline position and swallowed the following dotted sibling on round-trip. The array of tables now renders past the inline entries it would otherwise capture, mirroring the table fix for [#513](https://github.com/python-poetry/tomlkit/issues/513). ([#542](https://github.com/python-poetry/tomlkit/issues/542)) ## [0.15.0] - 2026-05-10 diff --git a/tests/test_toml_document.py b/tests/test_toml_document.py index 0ee5ed8..b4be10f 100644 --- a/tests/test_toml_document.py +++ b/tests/test_toml_document.py @@ -1113,6 +1113,28 @@ def test_replace_dotted_key_with_table_keeps_following_sibling() -> None: assert parse(doc.as_string()) == {"c": {"d": 2}, "a": {"x": 9}} +def test_replace_dotted_key_with_aot_keeps_following_sibling() -> None: + # https://github.com/python-poetry/tomlkit/issues/542 + content = """a.b = 1 +c.d = 2 +""" + doc = parse(content) + arr = tomlkit.aot() + tbl = tomlkit.table() + tbl["x"] = 9 + arr.append(tbl) + doc["a"] = arr + assert ( + doc.as_string() + == """c.d = 2 + +[[a]] +x = 9 +""" + ) + assert parse(doc.as_string()) == {"c": {"d": 2}, "a": [{"x": 9}]} + + def test_replace_value_with_table_keeps_following_dotted_sibling() -> None: # A plain value turning into a table must likewise clear the inline region # (including dotted keys) before emitting its header. diff --git a/tomlkit/container.py b/tomlkit/container.py index 040bde7..32fec66 100644 --- a/tomlkit/container.py +++ b/tomlkit/container.py @@ -860,11 +860,13 @@ def _replace_at( assert k is not None # A dotted key renders its value inline (e.g. ``a.b = 1``), which is only # consistent with a super table. When the replacement value renders with - # its own ``[header]`` instead (a non-super table), keeping the dotted key - # duplicates the prefix onto the header (#524). Drop the dotted key so the - # replacement renders as a plain table. - dotted_to_header = ( - k.is_dotted() and isinstance(value, Table) and not value.is_super_table() + # its own ``[header]`` instead (a non-super table, or an array of tables), + # keeping the dotted key duplicates the prefix onto the header (#524) and + # lets the header swallow following dotted siblings (#542). Drop the dotted + # key so the replacement renders as a plain table/array of tables. + dotted_to_header = k.is_dotted() and ( + isinstance(value, AoT) + or (isinstance(value, Table) and not value.is_super_table()) ) # That new header also captures every sibling that renders inline -- plain # values and dotted keys -- if any still follow it (#513), so it must be