1
0
mirror of https://github.com/django/django.git synced 2025-06-02 18:19:11 +00:00

Fixed #36341 -- Preserved whitespaces in wordwrap template filter.

Regression in 55d89e25f4115c5674cdd9b9bcba2bb2bb6d820b.

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>
This commit is contained in:
Matti Pohjanvirta 2025-04-20 18:22:51 +03:00 committed by nessita
parent 18fa74fc88
commit 1e9db35836
3 changed files with 56 additions and 2 deletions

View File

@ -54,10 +54,19 @@ def wrap(text, width):
width=width,
break_long_words=False,
break_on_hyphens=False,
replace_whitespace=False,
)
result = []
for line in text.splitlines(True):
result.extend(wrapper.wrap(line))
for line in text.splitlines():
wrapped = wrapper.wrap(line)
if not wrapped:
# If `line` contains only whitespaces that are dropped, restore it.
result.append(line)
else:
result.extend(wrapped)
if text.endswith("\n"):
# If `text` ends with a newline, preserve it.
result.append("")
return "\n".join(result)

View File

@ -44,3 +44,7 @@ Bugfixes
* Fixed a regression in Django 5.2 that caused the ``object-tools`` block to be
rendered twice when using custom admin templates with overridden blocks due
to changes in the base admin page block structure (:ticket:`36331`).
* Fixed a regression in Django 5.2, introduced when fixing :cve:`2025-26699`,
where the :tfilter:`wordwrap` template filter did not preserve empty lines
between paragraphs after wrapping text (:ticket:`36341`).

View File

@ -89,3 +89,44 @@ class FunctionTests(SimpleTestCase):
"I'm afraid",
wordwrap(long_text, 10),
)
def test_wrap_preserve_newlines(self):
cases = [
(
"this is a long paragraph of text that really needs to be wrapped\n\n"
"that is followed by another paragraph separated by an empty line\n",
"this is a long paragraph of\ntext that really needs to be\nwrapped\n\n"
"that is followed by another\nparagraph separated by an\nempty line\n",
30,
),
("\n\n\n", "\n\n\n", 5),
("\n\n\n\n\n\n", "\n\n\n\n\n\n", 5),
]
for text, expected, width in cases:
with self.subTest(text=text):
self.assertEqual(wordwrap(text, width), expected)
def test_wrap_preserve_whitespace(self):
width = 5
width_spaces = " " * width
cases = [
(
f"first line\n{width_spaces}\nsecond line",
f"first\nline\n{width_spaces}\nsecond\nline",
),
(
"first line\n \t\t\t \nsecond line",
"first\nline\n \t\t\t \nsecond\nline",
),
(
f"first line\n{width_spaces}\nsecond line\n\nthird{width_spaces}\n",
f"first\nline\n{width_spaces}\nsecond\nline\n\nthird\n",
),
(
f"first line\n{width_spaces}{width_spaces}\nsecond line",
f"first\nline\n{width_spaces}{width_spaces}\nsecond\nline",
),
]
for text, expected in cases:
with self.subTest(text=text):
self.assertEqual(wordwrap(text, width), expected)