Silent data loss: emptying an implicitly-created table (dotted key) makes it vanish from as_string() while unwrap() still contains it
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
调研方向
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.
由索引模型根据 Issue 内容生成。
描述
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.
- 主要语言
- Python
- 星标
- 850
- 派生
- 163
- 平均合并
- 13 分钟
- 30 天内合并 PR
- 2
环境准备
我们还没有检查这个项目的环境配置文件。先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
python-poetry/tomlkit 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 72/100
python-poetry/tomlkit#546 · 2 条评论 ·
维护者通常 1 天内回复
-
难度 5/5 一周以上 新手友好度 35/100
python-poetry/tomlkit#614 ·
维护者通常 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
-
bug status/needs-triage
难度 2/5 1-3 小时 新手友好度 86/100
prowler-cloud/prowler#12887 · 1 条评论 ·
维护者通常 1 天内回复
-
area: desktop platform: macos priority: p3 status: ready type: enhancement
难度 1/5 1 小时以内 新手友好度 92/100
use-agent-os/agent-os#3484 ·
维护者通常 2 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 86/100
open-telemetry/opentelemetry-python-contrib#5113 · 2 条评论 · 2 个 reaction ·
维护者通常 1 天内回复
-
external
难度 2/5 1-3 小时 新手友好度 68/100
langchain-ai/docs#6255 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 1 天内回复