From b7ab6bc65a6cb79939d788c77a777b7df9d20575 Mon Sep 17 00:00:00 2001 From: gaoflow Date: Fri, 12 Jun 2026 15:40:02 +0200 Subject: [PATCH] Prefix every line of a multiline comment with # comment() only prepended '# ' to the whole string, so a value with embedded newlines rendered the second line onward without a '#' and produced invalid TOML. Prefix each line individually (bare '#' for empty lines). Fixes #449. --- CHANGELOG.md | 1 + tests/test_api.py | 16 ++++++++++++++++ tomlkit/api.py | 10 ++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52d9a6b..1ef9c45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Fix a table replaced by a plain value being serialized inside the preceding table's body when other tables follow; the value now moves before the first table like other root-level values. ([#504](https://github.com/python-poetry/tomlkit/issues/504)) - Restore `dumps()` rendering mapping-like wrappers around a parsed document (e.g. `dotty_dict`'s `Dotty`) through their delegated `as_string`, preserving the original table order and layout instead of re-encoding through a plain dict — a 0.15.0 regression. ([#482](https://github.com/python-poetry/tomlkit/issues/482)) - Fix uncontrolled recursion when parsing deeply nested documents: crafted input could crash the process with a `RecursionError`. Values nested more than 100 levels deep and keys with more than 100 dotted fragments now raise `ParseError`. ([#459](https://github.com/python-poetry/tomlkit/issues/459)) +- Fix `comment()` producing invalid TOML for a multiline string by prefixing every line with `#`, not just the first. ([#449](https://github.com/python-poetry/tomlkit/issues/449)) ## [0.15.0] - 2026-05-10 diff --git a/tests/test_api.py b/tests/test_api.py index 734a627..fba6a82 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -263,6 +263,22 @@ def test_datetime() -> None: tomlkit.time("1979-05-13") +def test_comment_single_line() -> None: + doc = tomlkit.document() + doc.add(tomlkit.comment("a comment")) + assert doc.as_string() == "# a comment\n" + + +def test_comment_multiline_is_valid_toml() -> None: + """A multiline comment prefixes every line so the output re-parses (#449).""" + doc = tomlkit.document() + doc.add(tomlkit.comment("line one\nline two\n\nline four")) + rendered = doc.as_string() + assert rendered == "# line one\n# line two\n#\n# line four\n" + # The rendered document must round-trip. + assert parse(rendered).as_string() == rendered + + def test_array() -> None: a = tomlkit.array() diff --git a/tomlkit/api.py b/tomlkit/api.py index 902374e..94f658a 100644 --- a/tomlkit/api.py +++ b/tomlkit/api.py @@ -306,8 +306,14 @@ def nl() -> Whitespace: def comment(string: str) -> Comment: - """Create a comment item.""" - return Comment(Trivia(comment_ws=" ", comment="# " + string)) + """Create a comment item. + + A multiline string produces one ``#``-prefixed line per line so that the + result is still valid TOML. + """ + lines = string.split("\n") + rendered = "\n".join(f"# {line}" if line else "#" for line in lines) + return Comment(Trivia(comment_ws=" ", comment=rendered)) def register_encoder(encoder: E) -> E: