Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions tests/test_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,17 @@ def test_array_add_line() -> None:
)


def test_array_add_line_multiline_comment_is_valid_toml() -> None:
t = api.array()
t.add_line(1, 2, 3, comment="first line\nsecond line")
t.add_line(indent="")
doc = api.document()
doc["a"] = t
rendered = doc.as_string()
assert "\nsecond line" not in rendered
assert parse(rendered).as_string() == rendered


def test_array_add_line_invalid_value() -> None:
t = api.array()
with pytest.raises(ValueError, match="is not allowed"):
Expand Down Expand Up @@ -887,6 +898,22 @@ def test_trim_comments_when_building_inline_table() -> None:
assert table.as_string() == '{foo = "bar", baz = "foobaz"}'


def test_comment_method_multiline_is_valid_toml() -> None:
doc = api.document()
doc["x"] = 1
doc["x"].comment("first line\nsecond line")
rendered = doc.as_string()
assert rendered == "x = 1 # first line\n# second line\n"
assert parse(rendered).as_string() == rendered


def test_comment_method_keeps_existing_hash_prefix() -> None:
doc = api.document()
doc["x"] = 1
doc["x"].comment("# already a comment")
assert doc.as_string() == "x = 1 # already a comment\n"


def test_append_table_to_inline_table_raises() -> None:
table = api.table()
table.append("a", 1)
Expand Down
24 changes: 19 additions & 5 deletions tomlkit/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,21 @@ def item(value: Any, _parent: Item | None = None, _sort_keys: bool = False) -> I
raise ConvertError(f"Unable to convert an object of {type(value)} to a TOML item")


def _format_comment(comment: str) -> str:
"""Prefix every line of ``comment`` with ``#`` so a multi-line comment
stays valid TOML.

Only the first line was prefixed before, so the second line onward of a
comment with embedded newlines rendered without a ``#`` and produced
output that no longer re-parses. A line that already starts with ``#`` is
left untouched and an empty line becomes a bare ``#``.
"""
return "\n".join(
line if line.lstrip().startswith("#") else f"# {line}" if line else "#"
for line in comment.split("\n")
)


class StringType(Enum):
# Single Line Basic
SLB = '"'
Expand Down Expand Up @@ -500,11 +515,8 @@ def unwrap(self) -> Any:

def comment(self, comment: str) -> Item:
"""Attach a comment to this item"""
if not comment.strip().startswith("#"):
comment = "# " + comment

self._trivia.comment_ws = " "
self._trivia.comment = comment
self._trivia.comment = _format_comment(comment)

return self

Expand Down Expand Up @@ -1550,7 +1562,9 @@ def add_line(
if comment:
indent = " " if items else ""
new_values.append(
Comment(Trivia(indent=indent, comment=f"# {comment}", trail=""))
Comment(
Trivia(indent=indent, comment=_format_comment(comment), trail="")
)
)
list.extend(self, data_values)
if len(self._value) > 0:
Expand Down
Loading