`dumps(sort_keys=True)` only sorts the top level of a parsed document (nested tables / inline tables / AoT keep original order)
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
調査の方向性
Start with the reproduction, then read api.py:60-64 and items.py:114, 130-150 to confirm how parsed and plain-dict inputs differ. The issue still needs a decision between recursive sorting with possible formatting loss and documenting top-level-only behavior; done means implementing the chosen direction and covering nested tables, inline tables, and arrays of tables.
索引モデルが issue の本文から書いたものです。
説明
Summary
For a document produced by tomlkit.parse() / loads(), dumps(doc, sort_keys=True) sorts only the top-level keys. Nested tables, inline tables, and arrays-of-tables keep their original (insertion) order. The same option applied to a plain dict sorts recursively. So the output depends on whether the input was parsed or built from scratch, which is surprising for a documented, explicit option.
Reproduction (copy-paste)
import tomlkit
doc = tomlkit.parse("""
[zeta]
b = 2
a = 1
[alpha]
b = 2
a = 1
top = 0
""")
print(tomlkit.dumps(doc, sort_keys=True))
# [alpha]
# b = 2
# a = 1
#
# top = 0
#
# [zeta]
# b = 2
# a = 1
# -> top level IS sorted (alpha before zeta), but inside each table
# `b` still comes before `a`.
print(tomlkit.dumps({"zeta": {"b": 2, "a": 1}, "alpha": {"b": 2, "a": 1}, "top": 0}, sort_keys=True))
# top = 0
#
# [alpha]
# a = 1
# b = 2
#
# [zeta]
# a = 1
# b = 2
# -> fully sorted, including nested keys.
Same split for inline tables and arrays of tables:
print(tomlkit.dumps(tomlkit.parse("[t]\ninl = { z = 1, a = 2 }\n"), sort_keys=True))
# inl = { z = 1, a = 2 } <- NOT sorted
print(tomlkit.dumps({"t": {"inl": {"z": 1, "a": 2}}}, sort_keys=True))
# inl = { a = 2, z = 1 } <- sorted
print(tomlkit.dumps(tomlkit.parse("[[t]]\nz = 1\na = 2\n"), sort_keys=True))
# z = 1
# a = 2 <- NOT sorted
print(tomlkit.dumps({"t": [{"z": 1, "a": 2}]}, sort_keys=True))
# a = 2
# z = 1 <- sorted
Expected
sort_keys=True sorts recursively, consistent with the plain-dict path and with the documented "sort the keys in alphabetic order".
Actual
Only the top level of a parsed document is sorted; nested structures keep their original order.
Root cause
dumps() (api.py:60-64) routes parsed documents through item(data, _sort_keys=True). In item() (items.py:114), the top-level TOMLDocument/Container is a dict (not an Item), so it is rebuilt in the isinstance(value, dict) branch (items.py:139-150), which sorts. But every nested value in a parsed document is already a Table / InlineTable / AoT, and item() returns it unchanged at the early guard if isinstance(value, Item): return value (items.py:130-131). Those items are never rebuilt, so their keys keep their original order. The plain-dict path has no such guard (nested plain dicts are rebuilt recursively), which is why it sorts fully.
This is the residual of #466 ("sort_keys does not (always?) work"), which fixed the top level; the nested levels were not covered.
Scope note / open question
A fix would need to rebuild nested Table / InlineTable / AoT items when _sort_keys is set. Because tomlkit's whole purpose is lossless round-tripping (preserving comments, blank lines, key order, formatting), rebuilding nested tables would drop that formatting. So this may be a deliberate trade-off rather than a plain bug — filing here to get a decision:
- (a) sort recursively and accept loss of nested formatting, or
- (b) document that
sort_keysapplies only to the top level of parsed documents.
Happy to take either once there's a direction.
Environment
tomlkit master @ 8c959b5 (0.15.1+), Python 3.12.
- 主要言語
- Python
- スター
- 850
- フォーク
- 163
- 平均マージ
- 13分
- マージ済み PR(30日)
- 2
環境構築
このプロジェクトの環境構築ファイルはまだ確認していません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
python-poetry/tomlkit のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
python-poetry/tomlkit#546 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 76/100
python-poetry/tomlkit#615 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 68/100
python-poetry/tomlkit#603 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 52/100
python-poetry/tomlkit#580 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
python-poetry/tomlkit#577 ·
メンテナーはふだん 1 日以内に返信
python-poetry/tomlkit の issue をすべて見る
似ている issue
-
docs pydanty:is-working
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
pydantic/pydantic-ai#8863 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
run-llama/llama_index#23278 ·
メンテナーはふだん 2 日以内に返信
-
documentation from-review-extraction github-actions priority: low severity:nit
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
LearningCircuit/local-deep-research#6946 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
oracle/langchain-oracle#323 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
tenstorrent/tt-metal#58057 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信