Skip to content
Closed
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
23 changes: 23 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,29 @@ def test_create_multiline_string_with_leading_newline_round_trips(
assert str(reparsed) == example


def test_create_multiline_literal_string_with_leading_newline_no_escape() -> None:
"""A multiline literal string created with ``escape=False`` whose value
starts with a newline must render verbatim, without a doubled newline
(#655). PR #551 re-added the trimmed leading newline unconditionally, which
made it impossible to dump such a value as written.
"""
value = tomlkit.string(
"\n aligned\n strings\n",
multiline=True,
literal=True,
escape=False,
)
assert tomlkit.dumps(value) == "'''\n aligned\n strings\n'''"
assert str(value) == "\n aligned\n strings\n"


def test_create_multiline_basic_string_with_leading_newline_no_escape() -> None:
"""Same as above for a multiline basic string built with ``escape=False``."""
value = tomlkit.string("\nfoo\n", multiline=True, escape=False)
assert tomlkit.dumps(value) == '"""\nfoo\n"""'
assert str(value) == "\nfoo\n"


@pytest.mark.parametrize(
"kwargs, example",
[
Expand Down
6 changes: 5 additions & 1 deletion tomlkit/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -2311,11 +2311,15 @@ def from_raw(
if type_ is StringType.MLB and escape:
string_value = _BARE_CR.sub(r"\\r", string_value)

if type_.is_multiline() and string_value[:1] in ("\n", "\r"):
if escape and type_.is_multiline() and string_value[:1] in ("\n", "\r"):
# A newline immediately following the opening delimiter of a
# multiline string is trimmed by the parser, so a value that
# starts with a newline would otherwise lose it on round-trip.
# Emit an extra leading newline (the trimmed one) to preserve it.
#
# This only applies when escaping is enabled: with escape=False the
# caller is responsible for the raw body, so re-adding a newline
# would make it impossible to render a leading newline as written.
string_value = "\n" + string_value

return cls(type_, decode(value), string_value, Trivia())
Expand Down
Loading