`dumps(sort_keys=True)` only sorts the top level of a parsed document (nested tables / inline tables / AoT keep original order)
Maintainer antworten meist innerhalb von 1 Tag
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 5/5
- Geschätzter Aufwand
- Über eine Woche
- Anfängerfreundlichkeit
- 35/100
Rechercherichtung
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.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
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.
- Vorherrschende Sprache
- Python
- Sterne
- 850
- Forks
- 163
- Ø Merge
- 13 Min.
- Gemergte PRs (30 T.)
- 2
Entwicklungsumgebung
Die Einrichtungsdateien dieses Projekts haben wir noch nicht geprüft. Beginnen Sie mit der README; die allgemeinen Schritte stehen in unserem Leitfaden für den ersten Beitrag.
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus python-poetry/tomlkit
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
python-poetry/tomlkit#546 · 2 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 3/5 1-2 Tage Anfängerfreundlichkeit 68/100
python-poetry/tomlkit#603 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 3/5 1-2 Tage Anfängerfreundlichkeit 52/100
python-poetry/tomlkit#580 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 3/5 1-2 Tage Anfängerfreundlichkeit 72/100
python-poetry/tomlkit#577 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 5/5 Über eine Woche Anfängerfreundlichkeit 20/100
python-poetry/tomlkit#574 · 5 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in python-poetry/tomlkit
Ähnliche Issues
-
bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 85/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 90/100
Maintainer antworten meist innerhalb von 1 Tag
-
instance instance add
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100
searxng/searx-instances#941 · 1 Kommentar ·
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 92/100
FluidNumerics/fluid-walk-blocker#89 ·
Maintainer antworten meist innerhalb von 1 Tag
-
bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
Maintainer antworten meist innerhalb von 1 Tag