diff --git a/CHANGELOG.md b/CHANGELOG.md index 3737b80e..d38ef112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ - Preserve trailing whitespace when replacing a super table, including assigning it onto itself. ([#534](https://github.com/python-poetry/tomlkit/pull/534)) - Fix `str()` and `repr()` of out-of-order table proxies to show their merged values. ([#536](https://github.com/python-poetry/tomlkit/pull/536)) - Reject decimal integer literals that exceed Python's integer-string conversion limit instead of coercing them to infinity. ([#538](https://github.com/python-poetry/tomlkit/pull/538)) +- Fix `item()` silently reordering a dict's keys (dict-valued keys forced last) when converting a list of dicts into an inline table, even with the default `sort_keys=False`. A misplaced parenthesis in the sort key left the reordering active regardless of `sort_keys`; concrete `[table]`/array-of-tables entries were unaffected since `Container.append` already repositions scalars ahead of table headers on its own. ([#546](https://github.com/python-poetry/tomlkit/issues/546)) ## [0.15.0] - 2026-05-10 diff --git a/tests/test_items.py b/tests/test_items.py index d7c06fbf..d0194d3b 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -654,6 +654,23 @@ def test_item_array_of_dicts_converted_to_aot() -> None: ) +def test_item_list_of_dicts_as_inline_table_keeps_key_order() -> None: + # A dict inside a list that also holds non-dict values is rendered as an + # InlineTable, not a [table]/AoT. Unlike a real table header, an inline + # table has no ordering constraint that would justify moving dict-valued + # keys to the end, so with the default sort_keys=False the original key + # order must survive even though "a"'s value is itself a dict. + a = item([1, {"a": {"x": 1}, "b": 2}]) + + assert a.as_string() == "[1, {a = {x = 1}, b = 2}]" + + # sort_keys=True should still sort, dict-valued keys last, even though + # "a" comes first in the input. + sorted_a = item([1, {"a": {"x": 1}, "b": 2}], _sort_keys=True) + + assert sorted_a.as_string() == "[1, {b = 2, a = {x = 1}}]" + + def test_add_float_to_int() -> None: content = "[table]\nmy_int = 2043" doc = parse(content) diff --git a/tomlkit/items.py b/tomlkit/items.py index c950e5d0..83ff0df5 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -167,7 +167,7 @@ def item(value: Any, _parent: Item | None = None, _sort_keys: bool = False) -> I for k, _v in sorted( v.items(), - key=lambda i: (isinstance(i[1], dict), i[0] if _sort_keys else 1), + key=lambda i: (isinstance(i[1], dict), i[0]) if _sort_keys else 1, ): i = item(_v, _parent=table, _sort_keys=_sort_keys) if isinstance(table, InlineTable):