Repository navigation
Reject keys that are invalid for an explicit bare or literal key type - #630
Open
choudhryfrompak wants to merge 1 commit into
Open
choudhryfrompak wants to merge 1 commit into
choudhryfrompak wants to merge 1 commit into
Conversation
SingleKey only escapes basic keys. When a caller passes t=KeyType.Bare or
t=KeyType.Literal, the key text was written between the delimiters as-is,
so a key containing a newline, "=", a quote or a dot changed the structure
of the dumped document instead of being one key:
doc.add(SingleKey('user = "x"\nadmin', KeyType.Bare), True)
dumps(doc) # 'user = "x"\nadmin = true\n'
Raise ValueError when the key cannot be written in the requested form:
bare keys must be non-empty and use only A-Za-z0-9_-, and literal keys
must not contain a single quote or a control character other than tab
(the same rule as single-line literal strings). Keys built by the parser
pass their original text and are not affected, nor are keys whose type
is chosen automatically.
Wkhanex
reviewed
Oct 13, 2026
Wkhanex
left a comment
There was a problem hiding this comment.
I checked explicit Bare and Literal keys across every ASCII character, three Unicode samples and empty input: 264 standalone cases passed. Bare keys accepted only nonempty ASCII letters/digits/_/-, while literal keys allowed tabs and ordinary Unicode but rejected quotes and forbidden controls. Every accepted key serialized and decoded through stdlib tomllib as exactly one unchanged key/value pair.
This focuses on the new explicit-type validation. I excluded Basic-key escaping and the parser's original-spelling path from this check.
I used Python 3.12.14 against commit 037f09d. These were focused checks, not a full-suite run.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SingleKeyonly escapes basic keys. If you passt=KeyType.Bareort=KeyType.Literalyourself, the key text goes between the delimiters as is. A newline,=, a quote or a dot in the key then changes the structure of the dumped document:That parses back as
{'user': 'x', 'admin': True, 'x': 1, 'evil': {'y': 2}}, not as two keys. Same on 0.15.1 from PyPI.With this change
SingleKeyraisesValueErrorwhen an explicit type can't represent the key. A bare key has to be non-empty and use onlyA-Za-z0-9_-. A literal key can't contain'or a control character other than tab, which is the ruleStringType.SLL.invalid_sequencesalready has for literal strings. Basic keys were already escaped and haven't changed. Keys from the parser passoriginaland skip the check, and so do keys whose type is picked automatically (tomlkit.key(),doc["..."],table.add("...")). I moved the bare character check into a small_is_bare_key()helper so the automatic choice and the new check use the same rule.This can break code that used
KeyType.Bareto write a dotted header on purpose, e.g.SingleKey("tool.poetry", t=KeyType.Bare). I found that in one small project on GitHub. It already gave an inconsistent document (doc.unwrap()has a"tool.poetry"key while the output parses as nested tables), andtable(is_super_table=True)gives the same[tool.poetry]header properly. If you'd rather keep dots allowed for bare keys, I can relax it.Tests are in
tests/test_items.py: invalid bare and literal keys raise, and valid keys of each explicit type round-trip throughparse. The 8 rejection cases fail on master. The full suite passes with the toml-test submodule checked out (1080 passed, 1068 before), and ruff and ruff-format are clean on the changed files.