Skip to content

Preserve inline table key order in item() (#546)#552

Open
uttam12331 wants to merge 1 commit into
python-poetry:masterfrom
uttam12331:fix/inline-table-key-order-546
Open

Preserve inline table key order in item() (#546)#552
uttam12331 wants to merge 1 commit into
python-poetry:masterfrom
uttam12331:fix/inline-table-key-order-546

Conversation

@uttam12331

Copy link
Copy Markdown

Summary

Closes #546.

item() reordered the keys of an inline table under the default sort_keys=False:

tomlkit.item([[{"a": {"x": 1}, "b": 2}]]).as_string()
# -> '[[{a = {x = 1}, b = 2}]]'   (before: '[[{b = 2, a = {x = 1}}]]')

Root cause

In the list/tuple branch of item(), the sort key was

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

so isinstance(i[1], dict) stayed in the tuple unconditionally, forcing dict-valued keys last even when not sorting. The sibling dict branch has the correct form ((isinstance(i[1], dict), i[0]) if _sort_keys else 1).

Fix

Keep the dicts-last ordering only for arrays of tables (Table constructor), which render as [[table]] headers where a dict-valued key would otherwise capture the following keys — this is why simply removing the dicts-last ordering (PR #547) was not acceptable. Inline tables cannot capture, so their insertion order is preserved unless sort_keys=True is passed (matching the dict branch).

Verified:

  • inline: [[{a = {x = 1}, b = 2}]] (order preserved)
  • inline with _sort_keys=True: [[{b = 2, a = {x = 1}}]] (still sorted, dicts-last)
  • array of tables: unchanged — b = 2 still emitted before the [a] sub-table header

Tests

Added test_item_inline_table_preserves_key_order covering all three cases. It fails on master and passes with this change. Full suite passes (356 passed); ruff check/ruff format clean; added a CHANGELOG entry.

`item()` sorted dict-valued keys last in the list/tuple branch even under the
default ``sort_keys=False``, because the sort key kept ``isinstance(i[1],
dict)`` in the tuple unconditionally (unlike the dict branch). For an inline
table this silently reordered keys, e.g.
``item([[{"a": {"x": 1}, "b": 2}]])`` produced ``[[{b = 2, a = {x = 1}}]]``.

Only apply the dicts-last ordering to arrays of tables (which render as
``[[table]]`` headers, where a dict-valued key would otherwise capture the
following keys). Inline tables cannot capture, so their insertion order is now
preserved unless sorting is explicitly requested.

Closes python-poetry#546
Comment thread tomlkit/items.py
# Inline tables cannot capture, so preserve insertion order unless
# explicitly sorting (matching the dict branch above). See #546.
def _sort_key(i: tuple[Any, Any]) -> Any:
return (isinstance(i[1], dict), i[0]) if _sort_keys else 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you keeping dictionaries last in this branch?

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

2 participants