Silent data loss: emptying an implicitly-created table (dotted key) makes it vanish from as_string() while unwrap() still contains it
Maintainer antworten meist innerhalb von 1 Tag
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 3/5
- Geschätzter Aufwand
- 1-2 Tage
- Anfängerfreundlichkeit
- 76/100
Rechercherichtung
Start with the dotted-key table creation in items.py around lines 1948-1949, then trace Container._render_table in container.py lines 672-708 and removal logic in lines 476-514. Reproduce the minimal examples and add regression coverage for remove() and clear() on implicit tables. Done means as_string() output reparses to the same unwrap() structure without affecting explicit tables, whole-table deletion, inline tables, or AoT elements.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Summary
After removing keys from a table that was created implicitly (via a dotted key, with no [header] of its own), an empty intermediate table remains in the in-memory model (unwrap() / dict view) but is silently dropped from as_string() / dumps() output. The rendered document no longer round-trips: parse(doc.as_string()).unwrap() != doc.unwrap() — data is lost without any warning.
This is a silent data loss: no exception, no error, the output is perfectly valid TOML — it just no longer contains a table the user can still see in the in-memory model.
Minimal reproduction (copy-paste)
import tomlkit
doc = tomlkit.parse("[a]\nb.c = 1\nd = 2\n")
del doc["a"]["b"]["c"] # empty the implicitly-created table `a.b`
s = doc.as_string()
print(repr(s)) # '[a]\nd = 2\n' <- no trace of `a.b`
print(doc.unwrap()) # {'a': {'b': {}, 'd': 2}} <- `a.b` still here
print(tomlkit.parse(s).unwrap())# {'a': {'d': 2}} <- `a.b` lost
Also works with pure top-level dotted keys, remove(), and clear():
doc = tomlkit.parse("a.b.c = 1\na.d = 2\n")
doc["a"]["b"].remove("c")
# as_string() -> 'a.d = 2\n'
# unwrap() -> {'a': {'b': {}, 'd': 2}}
# reparse -> {'a': {'d': 2}}
doc = tomlkit.parse("[a]\nb.c = 1\nd = 2\n")
doc["a"]["b"].clear()
# same mismatch
What is NOT affected (verified)
- Tables with an explicit
[a.b]header: the empty header is preserved ([a.b]\n), round-trips fine. - Deleting the whole table (
del doc["a"]["b"]): consistent. - Inline tables and AoT elements: consistent.
So the bug is specific to implicit tables — those created by dotted keys (b.c = 1, a.b.c = 1) or by out-of-order [a.b] sections where the parent got no header of its own.
Frequency
A random-mutation fuzz (parse -> 1-4 random delete/add/replace ops -> compare unwrap() vs parse(as_string()).unwrap()) hit this in 274 of 3000 runs. It is the dominant round-trip failure mode.
Root cause
When the parser creates an intermediate table from a dotted key, it sets Table._is_super_table = True (items.py:1948-1949). is_super_table() returns that frozen flag first, so the if not self: return False fallback (which would correctly make an empty table render its own header) is never reached after the table has been emptied.
Container._render_table (container.py:672-708) then treats the table as a super table: no [a.b] header is emitted, and with no children left to render, the table renders as the empty string — while Container.remove / _remove_at (container.py:476-514) never invalidate the frozen _is_super_table flag on the emptied table, and the table stays in the parent's _body and dict view (hence unwrap() still contains it).
Expected
Either the emptied implicit table keeps rendering (e.g. as [a.b], matching the explicit-header behavior), or it is removed from the in-memory model together with the rendered output — but unwrap() and parse(as_string()).unwrap() should never disagree.
Related
- #204 "Deleting a table creates invalid output" (closed) — different symptom (duplicate headers when deleting a whole multi-tier table); this one is about the contents of an implicit table being emptied.
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 5/5 Über eine Woche Anfängerfreundlichkeit 35/100
python-poetry/tomlkit#614 ·
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
Alle Issues in python-poetry/tomlkit
Ähnliche Issues
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
-
bug
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 88/100
qgis/QGIS-Plugins-Website#459 ·
-
bug severity:medium
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 2 Tagen
-
bot-found bug priority: P3
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
madenvel/KalinkaPlayer#179 ·
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100
ls1intum/edutelligence#1098 ·
Maintainer antworten meist innerhalb von 1 Tag