Skip to content

Commit 1f3814f

Browse files
[3.14] gh-71896: Add a docs warning about trailing newlines ignored by difflib.HtmlDiff (GH-153930) (GH-153966)
(cherry picked from commit 755d971) Co-authored-by: Lenormand Julien <lenormand.julien0@gmail.com>
1 parent 388e72f commit 1f3814f

3 files changed

Lines changed: 30 additions & 0 deletions

File tree

Doc/library/difflib.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module.
8989
with inter-line and intra-line change highlights. The table can be generated in
9090
either full or contextual difference mode.
9191

92+
.. warning::
93+
94+
The trailing newlines get stripped before the diff, so the result can be
95+
incomplete. See :gh:`71896` for details.
96+
9297
The constructor for this class is:
9398

9499

Lib/difflib.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1977,6 +1977,8 @@ def make_table(self,fromlines,tolines,fromdesc='',todesc='',context=False,
19771977

19781978
# change tabs to spaces before it gets more difficult after we insert
19791979
# markup
1980+
# it also removes trailing newlines, causing some diffs to be missed
1981+
# see: gh-71896
19801982
fromlines,tolines = self._tab_newline_replace(fromlines,tolines)
19811983

19821984
# create diffs iterator which generates side by side from/to data

Lib/test/test_difflib.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,29 @@ def test_make_file_usascii_charset_with_nonascii_input(self):
282282
self.assertIn('content="text/html; charset=us-ascii"', output)
283283
self.assertIn('&#305;mpl&#305;c&#305;t', output)
284284

285+
def test_strip_trailing_newlines_before_diff(self):
286+
# characterization test for the current buggy behavior
287+
# see: gh-71896
288+
html_diff = difflib.HtmlDiff()
289+
from_lines = [
290+
"Line 1: no newline after",
291+
"Line 2: one newline after\n",
292+
"Line 3: several newlines after\n\n\n\n\n",
293+
]
294+
to_lines = [
295+
"Line 1: no newline after",
296+
"Line 2: one newline after", # actually no \n
297+
"Line 3: several newlines after", # actually no \n
298+
]
299+
output = html_diff.make_table(from_lines, to_lines)
300+
# we (currently) expect no line change, so all equal
301+
self.assertNotIn('class="diff_add"', output)
302+
self.assertNotIn('class="diff_chg"', output)
303+
self.assertNotIn('class="diff_sub"', output)
304+
self.assertEqual(output.count('>Line&nbsp;1:&nbsp;no&nbsp;newline&nbsp;after<'), 2)
305+
self.assertEqual(output.count('>Line&nbsp;2:&nbsp;one&nbsp;newline&nbsp;after<'), 2)
306+
self.assertEqual(output.count('>Line&nbsp;3:&nbsp;several&nbsp;newlines&nbsp;after<'), 2)
307+
285308
class TestDiffer(unittest.TestCase):
286309
def test_close_matches_aligned(self):
287310
# Of the 4 closely matching pairs, we want 1 to match with 3,

0 commit comments

Comments
 (0)