Skip to content

Commit e61e3da

Browse files
Grrandinessita
authored andcommitted
[4.2.x] Fixed #36341 -- Preserved whitespaces in wordwrap template filter.
Regression in 55d89e2. This work improves the django.utils.text.wrap() function to ensure that empty lines and lines with whitespace only are kept instead of being dropped. Thanks Matti Pohjanvirta for the report and fix. Co-authored-by: Natalia <124304+nessita@users.noreply.github.com> Backport of 1e9db35 from main.
1 parent 07edc97 commit e61e3da

File tree

2 files changed

+52
-2
lines changed

2 files changed

+52
-2
lines changed

django/utils/text.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,19 @@ def wrap(text, width):
102102
width=width,
103103
break_long_words=False,
104104
break_on_hyphens=False,
105+
replace_whitespace=False,
105106
)
106107
result = []
107-
for line in text.splitlines(True):
108-
result.extend(wrapper.wrap(line))
108+
for line in text.splitlines():
109+
wrapped = wrapper.wrap(line)
110+
if not wrapped:
111+
# If `line` contains only whitespaces that are dropped, restore it.
112+
result.append(line)
113+
else:
114+
result.extend(wrapped)
115+
if text.endswith("\n"):
116+
# If `text` ends with a newline, preserve it.
117+
result.append("")
109118
return "\n".join(result)
110119

111120

tests/template_tests/filter_tests/test_wordwrap.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,44 @@ def test_wrap_long_text(self):
8989
"I'm afraid",
9090
wordwrap(long_text, 10),
9191
)
92+
93+
def test_wrap_preserve_newlines(self):
94+
cases = [
95+
(
96+
"this is a long paragraph of text that really needs to be wrapped\n\n"
97+
"that is followed by another paragraph separated by an empty line\n",
98+
"this is a long paragraph of\ntext that really needs to be\nwrapped\n\n"
99+
"that is followed by another\nparagraph separated by an\nempty line\n",
100+
30,
101+
),
102+
("\n\n\n", "\n\n\n", 5),
103+
("\n\n\n\n\n\n", "\n\n\n\n\n\n", 5),
104+
]
105+
for text, expected, width in cases:
106+
with self.subTest(text=text):
107+
self.assertEqual(wordwrap(text, width), expected)
108+
109+
def test_wrap_preserve_whitespace(self):
110+
width = 5
111+
width_spaces = " " * width
112+
cases = [
113+
(
114+
f"first line\n{width_spaces}\nsecond line",
115+
f"first\nline\n{width_spaces}\nsecond\nline",
116+
),
117+
(
118+
"first line\n \t\t\t \nsecond line",
119+
"first\nline\n \t\t\t \nsecond\nline",
120+
),
121+
(
122+
f"first line\n{width_spaces}\nsecond line\n\nthird{width_spaces}\n",
123+
f"first\nline\n{width_spaces}\nsecond\nline\n\nthird\n",
124+
),
125+
(
126+
f"first line\n{width_spaces}{width_spaces}\nsecond line",
127+
f"first\nline\n{width_spaces}{width_spaces}\nsecond\nline",
128+
),
129+
]
130+
for text, expected in cases:
131+
with self.subTest(text=text):
132+
self.assertEqual(wordwrap(text, width), expected)

0 commit comments

Comments
 (0)