`dumps(sort_keys=True)` only sorts the top level of a parsed document (nested tables / inline tables / AoT keep original order)
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 5/5
- Tempo estimado
- Mais de uma semana
- Facilidade para iniciantes
- 35/100
Direção de pesquisa
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.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
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.
- Linguagem predominante
- Python
- Estrelas
- 850
- Forks
- 163
- Merge médio
- 13min
- PRs com merge (30d)
- 2
Preparar o ambiente
Ainda não verificamos os arquivos de configuração deste projeto. Comece pelo README e veja nosso guia da primeira contribuição para os passos gerais.
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de python-poetry/tomlkit
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
python-poetry/tomlkit#546 · 2 comentários ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 3/5 1-2 dias Facilidade para iniciantes 76/100
python-poetry/tomlkit#615 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 3/5 1-2 dias Facilidade para iniciantes 68/100
python-poetry/tomlkit#603 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 3/5 1-2 dias Facilidade para iniciantes 52/100
python-poetry/tomlkit#580 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 3/5 1-2 dias Facilidade para iniciantes 72/100
python-poetry/tomlkit#577 ·
Mantenedores costumam responder em até 1 dia
Todas as issues de python-poetry/tomlkit
Issues semelhantes
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 86/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-2 dias Facilidade para iniciantes 70/100
-
FingerprintSplitter raises ZeroDivisionError when int(frac_train * len(dataset)) floors to zeroAberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 88/100
Mantenedores costumam responder em até 7 dias
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 70/100
lmstudio-ai/mlx-engine#376 ·
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
pyiron/bagofholding#166 ·