Skip to content

Commit 67d3e86

Browse files
HarperZ9claudefrostming
authored
Fix array of tables replacing a dotted key swallowing the next sibling (#542) (#549)
Assigning an AoT over a dotted key (e.g. `doc["a"] = aot(...)` where `a` came from `a.b = ...`) left the new `[[a]]` header in the dotted key's inline slot, so a following dotted sibling such as `c.d = 2` was rendered inside the array of tables and captured on round-trip. `_replace_at`'s `dotted_to_header` guard only recognised non-super Tables, not AoTs, so the reposition-before-inline-siblings path that already fixes the table case (#513/#524) never ran for arrays of tables. Extend the guard to AoT. Adds a regression test mirroring the table case and a changelog entry. Co-authored-by: Zain Dana Harper <zain@aurora-framework.dev> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Frost Ming <me@frostming.com>
1 parent e23a254 commit 67d3e86

3 files changed

Lines changed: 30 additions & 5 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
- 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))
3535
- 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))
3636
- Reject tables inserted into inline tables instead of serializing invalid TOML. ([#531](https://github.com/python-poetry/tomlkit/issues/531))
37+
- 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))
3738
- Fix invalid serialization with a duplicated `[table]` header when adding a key to an out-of-order table whose concrete header is declared after its sub-tables; the new key now lands in the existing concrete part instead of giving the header-less super part a second header. ([#545](https://github.com/python-poetry/tomlkit/pull/545))
3839
- Fix a table's display name (its exact header spelling, including whitespace and quoting) being normalised when the table is assigned onto itself, e.g. `doc[k] = doc[k]` rewriting `[keys .'a'.'c']` to `[keys.a.'c']`. ([#291](https://github.com/python-poetry/tomlkit/issues/291))
3940
- Fix missing newlines when appending a key after a dotted inline table, including when the original document has no trailing newline. ([#533](https://github.com/python-poetry/tomlkit/pull/533))

‎tests/test_toml_document.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,28 @@ def test_replace_dotted_key_with_table_keeps_following_sibling() -> None:
11701170
assert parse(doc.as_string()) == {"c": {"d": 2}, "a": {"x": 9}}
11711171

11721172

1173+
def test_replace_dotted_key_with_aot_keeps_following_sibling() -> None:
1174+
# https://github.com/python-poetry/tomlkit/issues/542
1175+
content = """a.b = 1
1176+
c.d = 2
1177+
"""
1178+
doc = parse(content)
1179+
arr = tomlkit.aot()
1180+
tbl = tomlkit.table()
1181+
tbl["x"] = 9
1182+
arr.append(tbl)
1183+
doc["a"] = arr
1184+
assert (
1185+
doc.as_string()
1186+
== """c.d = 2
1187+
1188+
[[a]]
1189+
x = 9
1190+
"""
1191+
)
1192+
assert parse(doc.as_string()) == {"c": {"d": 2}, "a": [{"x": 9}]}
1193+
1194+
11731195
def test_replace_value_with_table_keeps_following_dotted_sibling() -> None:
11741196
# A plain value turning into a table must likewise clear the inline region
11751197
# (including dotted keys) before emitting its header.

‎tomlkit/container.py‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -860,11 +860,13 @@ def _replace_at(
860860
assert k is not None
861861
# A dotted key renders its value inline (e.g. ``a.b = 1``), which is only
862862
# consistent with a super table. When the replacement value renders with
863-
# its own ``[header]`` instead (a non-super table), keeping the dotted key
864-
# duplicates the prefix onto the header (#524). Drop the dotted key so the
865-
# replacement renders as a plain table.
866-
dotted_to_header = (
867-
k.is_dotted() and isinstance(value, Table) and not value.is_super_table()
863+
# its own ``[header]`` instead (a non-super table, or an array of tables),
864+
# keeping the dotted key duplicates the prefix onto the header (#524) and
865+
# lets the header swallow following dotted siblings (#542). Drop the dotted
866+
# key so the replacement renders as a plain table/array of tables.
867+
dotted_to_header = k.is_dotted() and (
868+
isinstance(value, AoT)
869+
or (isinstance(value, Table) and not value.is_super_table())
868870
)
869871
# That new header also captures every sibling that renders inline -- plain
870872
# values and dotted keys -- if any still follow it (#513), so it must be

0 commit comments

Comments
 (0)