Skip to content

Fix item() reordering dict keys inside inline tables - #611

Closed
afonsojanu wants to merge 1 commit into
python-poetry:masterfrom
afonsojanu:fix/item-list-of-dicts-inline-table-key-order
Closed

afonsojanu wants to merge 1 commit into
python-poetry:masterfrom
afonsojanu:fix/item-list-of-dicts-inline-table-key-order

Conversation

@afonsojanu

Copy link
Copy Markdown

Summary

Fixes #546.

item()'s list-of-dicts branch (the one that turns a Python list containing dicts into a TOML inline table) builds a sort key as:

key=lambda i: (isinstance(i[1], dict), i[0] if _sort_keys else 1)

The closing parenthesis only wraps i[0], so isinstance(i[1], dict) gets evaluated unconditionally regardless of _sort_keys. That quietly moves dict-valued keys to the end of the output even when the caller passed the default sort_keys=False and expected the original key order to be preserved.

from tomlkit.items import item
item([1, {"a": {"x": 1}, "b": 2}]).as_string()
# '[1, {b = 2, a = {x = 1}}]'   <- "a" moved after "b" despite being declared first

A prior attempt at this same one-line fix (#547) was closed over a concern that it might break how a scalar key avoids getting swallowed into a following [table] header. I traced the actual rendering path before assuming that concern still applies here: Container.append already repositions scalar keys ahead of any table header at insertion time, independent of the order item() hands it keys in, so a concrete [table]/array-of-tables is unaffected either way:

item([{"a": {"x": 1}, "b": 2}]).as_string()
# 'b = 2\n\n[a]\nx = 1\n' — identical before and after this fix

Inline tables get no such correction from Container.append, since they render in strict insertion order, which is exactly why the bug is visible there and only there.

Fix

Moves the closing parenthesis so the whole tuple is guarded by _sort_keys, matching the sibling branch a few lines above that already does this correctly:

key=lambda i: (isinstance(i[1], dict), i[0]) if _sort_keys else 1

Testing

Added test_item_list_of_dicts_as_inline_table_keeps_key_order in tests/test_items.py, covering both the default order-preserving case and sort_keys=True still forcing dict-valued keys last. Confirmed it fails against the original code and passes with the fix. Full suite passes (pytest tests/ --ignore=tests/test_toml_tests.py, 379/379 — the ignored file needs a git submodule unrelated to this change).

Agent Drafting Metadata

  • Agent: Claude Code
  • Model: Claude (Sonnet 5)
  • Notes: I used Claude Code to investigate the issue, trace the rendering path to check whether the earlier maintainer concern still applied, write the fix, and add the regression test. I reviewed the diff and ran the test suite myself before opening this.

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 python-poetry#546.
@dimbleby

Copy link
Copy Markdown
Contributor

if correct - then why check isinstance(dict) at all?

@frostming

Copy link
Copy Markdown
Contributor

Closing as a duplicate of #552, which fixes #546 as well. Thanks for the contribution!

@frostming frostming closed this Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

item() reorders keys (dict-valued before scalar) even with the default sort_keys=False

3 participants