Skip to content

Commit 8ba942f

Browse files
committed
Fix item() reordering dict keys inside inline tables
The list-of-dicts branch of item() builds each dict's sort key as (isinstance(i[1], dict), i[0] if _sort_keys else 1). The closing parenthesis only wraps i[0], so the dict-valued check stays active no matter what _sort_keys is, quietly moving dict-valued keys to the end even when the caller asked to keep their original order. For a concrete [table] or array of tables this went unnoticed because Container.append already repositions scalar keys ahead of any table header on insertion, independent of the order item() hands it keys in. Inline tables get no such correction, so the reordering is directly visible there, e.g. item([1, {"a": {"x": 1}, "b": 2}]) rendered {b = 2, a = {x = 1}} instead of preserving the original a, b order. Move the parenthesis to match the sibling top-level dict branch a few lines above, which already guards the whole tuple correctly. Verified against the full existing test suite (all 378 tests still pass) plus a new regression test covering both the default order-preserving case and sort_keys=True still forcing dict-valued keys last. Resolves #546.
1 parent 8c959b5 commit 8ba942f

3 files changed

Lines changed: 19 additions & 1 deletion

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
- Preserve trailing whitespace when replacing a super table, including assigning it onto itself. ([#534](https://github.com/python-poetry/tomlkit/pull/534))
4444
- Fix `str()` and `repr()` of out-of-order table proxies to show their merged values. ([#536](https://github.com/python-poetry/tomlkit/pull/536))
4545
- 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))
46+
- 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))
4647

4748
## [0.15.0] - 2026-05-10
4849

‎tests/test_items.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,23 @@ def test_item_array_of_dicts_converted_to_aot() -> None:
654654
)
655655

656656

657+
def test_item_list_of_dicts_as_inline_table_keeps_key_order() -> None:
658+
# A dict inside a list that also holds non-dict values is rendered as an
659+
# InlineTable, not a [table]/AoT. Unlike a real table header, an inline
660+
# table has no ordering constraint that would justify moving dict-valued
661+
# keys to the end, so with the default sort_keys=False the original key
662+
# order must survive even though "a"'s value is itself a dict.
663+
a = item([1, {"a": {"x": 1}, "b": 2}])
664+
665+
assert a.as_string() == "[1, {a = {x = 1}, b = 2}]"
666+
667+
# sort_keys=True should still sort, dict-valued keys last, even though
668+
# "a" comes first in the input.
669+
sorted_a = item([1, {"a": {"x": 1}, "b": 2}], _sort_keys=True)
670+
671+
assert sorted_a.as_string() == "[1, {b = 2, a = {x = 1}}]"
672+
673+
657674
def test_add_float_to_int() -> None:
658675
content = "[table]\nmy_int = 2043"
659676
doc = parse(content)

‎tomlkit/items.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def item(value: Any, _parent: Item | None = None, _sort_keys: bool = False) -> I
167167

168168
for k, _v in sorted(
169169
v.items(),
170-
key=lambda i: (isinstance(i[1], dict), i[0] if _sort_keys else 1),
170+
key=lambda i: (isinstance(i[1], dict), i[0]) if _sort_keys else 1,
171171
):
172172
i = item(_v, _parent=table, _sort_keys=_sort_keys)
173173
if isinstance(table, InlineTable):

0 commit comments

Comments
 (0)