diff --git a/CHANGELOG.md b/CHANGELOG.md index 26584a1..8a9d8ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Fix invalid serialization with a duplicated comma when removing a non-edge element from a parsed inline table. ([#486](https://github.com/python-poetry/tomlkit/pull/486)) - Fix invalid serialization with a duplicated comma when appending or inserting into a comma-first formatted array. ([#499](https://github.com/python-poetry/tomlkit/pull/499)) +- Fix `ParseError` when a sub-table extends the last element of an array of tables after an unrelated table. ([#261](https://github.com/python-poetry/tomlkit/issues/261)) - Fix unparseable serialization when adding a key to a dotted-key table inside an inline table. ([#500](https://github.com/python-poetry/tomlkit/pull/500)) - Fix a table replaced by a plain value being serialized inside the preceding table's body when other tables follow; the value now moves before the first table like other root-level values. ([#504](https://github.com/python-poetry/tomlkit/issues/504)) - Restore `dumps()` rendering mapping-like wrappers around a parsed document (e.g. `dotty_dict`'s `Dotty`) through their delegated `as_string`, preserving the original table order and layout instead of re-encoding through a plain dict — a 0.15.0 regression. ([#482](https://github.com/python-poetry/tomlkit/issues/482)) diff --git a/README.md b/README.md index 0baf4f8..4a88fd4 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,15 @@ Part of the implementation has been adapted, improved and fixed from [Molten](ht See the [documentation](https://tomlkit.readthedocs.io/) for more information. +## Limitations + +`tomlkit` preserves the original layout of a document, with one exception: a +sub-table that extends an array of tables out of order — for example a +`[fruit.apple.texture]` header that appears after `[[fruit]]` with an unrelated +table in between — is normalized into the array's last element when the document +is re-serialized, rather than kept at its original position. The data is +preserved; only the physical placement of that sub-table changes. + ## Installation If you are using [uv](https://docs.astral.sh/uv), you can diff --git a/tests/test_toml_document.py b/tests/test_toml_document.py index 1cdfb9c..1b0508b 100644 --- a/tests/test_toml_document.py +++ b/tests/test_toml_document.py @@ -239,6 +239,25 @@ def test_document_with_aot_after_sub_tables() -> None: assert doc["foo"]["bar"]["tests"][0]["name"] == "Test 1" +def test_subtable_of_aot_element_after_other_table() -> None: + # A sub-table header that extends the last element of an array of tables + # is valid even when an unrelated table appears in between (issue #261). + content = """[[fruit]] +apple.color = "red" + +[potato] + +[fruit.apple.texture] +smooth = true +""" + + doc = parse(content) + + assert doc["fruit"][0]["apple"]["color"] == "red" + assert doc["fruit"][0]["apple"]["texture"]["smooth"] is True + assert doc["potato"] == {} + + def test_document_with_new_sub_table_after_other_table() -> None: content = """[foo] name = "Bar" diff --git a/tomlkit/container.py b/tomlkit/container.py index 75e0901..0a96089 100644 --- a/tomlkit/container.py +++ b/tomlkit/container.py @@ -260,6 +260,16 @@ def append( return self elif isinstance(current, AoT): if not item.is_aot_element(): + if item.is_super_table() and len(current.body): + # A sub-table header such as `[fruit.apple.texture]` + # appearing after the array `[[fruit]]` (possibly with + # unrelated tables in between) extends the last element + # of the array, per the TOML spec. + last = current[-1] + for k, v in item.value.body: + last.value.append(k, v) + + return self # Tried to define a table after an AoT with the same name. raise KeyAlreadyPresent(key)