Skip to content

Render a dotted key's sub-table inside an inline table as an inline table - #616

Open
feiiiiii5 wants to merge 1 commit into
python-poetry:masterfrom
feiiiiii5:fix/dotted-key-sub-table-in-inline-table
Open

feiiiiii5 wants to merge 1 commit into
python-poetry:masterfrom
feiiiiii5:fix/dotted-key-sub-table-in-inline-table

Conversation

@feiiiiii5

Copy link
Copy Markdown

Summary

Adding a sub-table to a dotted key inside an inline table produces TOML that cannot be parsed back:

>>> from tomlkit import parse, dumps
>>> doc = parse("a = {b.c = 1}\n")
>>> doc["a"]["b"]["e"] = {"f": 3}
>>> print(dumps(doc))
a = {b.c = 1, a.a. = q = 1}

a.a. is a key followed by a stray . and then =, which is not valid TOML in any version — the stdlib tomllib answers Invalid initial character for a key part (at line 1, column 20), and so does tomlkit's own parser, so the edit yields a document you cannot read back. After this change the same edit serializes as a = {b.c = 1, b.e = {f = 3}}, which tomllib accepts and which round-trips stably to three levels of nesting.

The cause is in InlineTable._render_dotted (tomlkit/items.py:2176), which rendered every Table child with the prefix.child = body shape. That is the right shape for a child that is itself a dotted key — which is what the parser materializes from b.c = 1 — but a child added afterwards is a real sub-table, and its body is not a value. So the fix recurses only for a child whose own key is dotted, and renders a real sub-table as an inline table, converting nested tables the same way.

The state is only reachable by a programmatic edit, which is why no round-trip test covered it: the parser never produces a non-dotted child under a dotted key inside an inline table. The two existing dotted-key tests in tests/test_items.py pin that adding keys there works and round-trips; this is the same API with a dict instead of a scalar.

Test: pytest tests/test_items.py -k dotted_key_inside_inline_table fails on 8c959b5 with tomkit.exceptions.EmptyKeyError: Empty key at line 1 col 19 when the output is re-parsed, and passes here. The suite is 380 passed with --ignore=tests/test_toml_tests.py — that module needs the tests/toml-test submodule, which is not checked out here, so it errors at collection both before and after this change. ruff check and ruff format --check are clean on both files; mypy reports 2 pre-existing errors in tomlkit/source.py, which this change does not touch.

I have not added a CHANGELOG entry — the file is organised per release and every entry cites a PR number, so tell me if you would like one and I will add it under a new section referencing this PR.

Agent Drafting Metadata

  • Agent: opencode (autonomous OSS contribution agent for this workspace)
  • Model: Space Bunny Free (opencode space-bunny-free)
  • Notes: the agent read InlineTable._render_dotted and the dotted-key rendering path, wrote the fix and the two regression tests, and ran the checks quoted above. I reviewed the code and the local evidence before pushing; a human review is requested before merge.

AI-assisted development. Human review requested before merge.

…able

A dotted key inside an inline table is materialized as a Table whose key is
flagged as dotted, and InlineTable._render_dotted rendered every Table child
with the `prefix.child = body` shape. That is right for a *dotted* child, but
a child added afterwards is a real sub-table, and its body is not a value:

    >>> doc = parse("a = {b.c = 1}\n")
    >>> doc["a"]["b"]["e"] = {"f": 3}
    >>> dumps(doc)
    'a = {b.c = 1, a.a. = q = 1}'      # a.a. is a key, then a stray '.'

`tomllib` rejects that (`Invalid initial character for a key part`), and so
does tomlkit's own parser, so the edit produced a document that could not be
read back. The parser cannot produce this state - it takes a programmatic
edit - which is why no round-trip test covered it.

Only recurse for a child that is itself dotted, and render a real sub-table
as an inline table, converting nested tables the same way.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant