From: Peter Bierma Date: Sat, 18 Jul 2026 14:10:12 +0000 (-0400) Subject: [3.13] gh-71896: Add a docs warning about trailing newlines ignored by `difflib.HtmlD... X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=75b6789946076c65f0b5a4194f293ed019f81ff9;p=thirdparty%2FPython%2Fcpython.git [3.13] gh-71896: Add a docs warning about trailing newlines ignored by `difflib.HtmlDiff` (GH-153930) (GH-153968) (cherry picked from commit 755d97167f6f5d4361c790f8f18df76cae8f2ad2) Co-authored-by: Lenormand Julien --- diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst index 85357008b6e1..e53a47289bf5 100644 --- a/Doc/library/difflib.rst +++ b/Doc/library/difflib.rst @@ -89,6 +89,11 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. with inter-line and intra-line change highlights. The table can be generated in either full or contextual difference mode. + .. warning:: + + The trailing newlines get stripped before the diff, so the result can be + incomplete. See :gh:`71896` for details. + The constructor for this class is: diff --git a/Lib/difflib.py b/Lib/difflib.py index db550ceff2e0..cc8f2064a19d 100644 --- a/Lib/difflib.py +++ b/Lib/difflib.py @@ -1961,6 +1961,8 @@ class HtmlDiff(object): # change tabs to spaces before it gets more difficult after we insert # markup + # it also removes trailing newlines, causing some diffs to be missed + # see: gh-71896 fromlines,tolines = self._tab_newline_replace(fromlines,tolines) # create diffs iterator which generates side by side from/to data diff --git a/Lib/test/test_difflib.py b/Lib/test/test_difflib.py index 943d7a659b1e..6c7d6c519990 100644 --- a/Lib/test/test_difflib.py +++ b/Lib/test/test_difflib.py @@ -282,6 +282,28 @@ class TestSFpatches(unittest.TestCase): self.assertIn('content="text/html; charset=us-ascii"', output) self.assertIn('ımplıcıt', output) + def test_strip_trailing_newlines_before_diff(self): + # characterization test for the current buggy behavior + # see: gh-71896 + html_diff = difflib.HtmlDiff() + from_lines = [ + "Line 1: no newline after", + "Line 2: one newline after\n", + "Line 3: several newlines after\n\n\n\n\n", + ] + to_lines = [ + "Line 1: no newline after", + "Line 2: one newline after", # actually no \n + "Line 3: several newlines after", # actually no \n + ] + output = html_diff.make_table(from_lines, to_lines) + # we (currently) expect no line change, so all equal + self.assertNotIn('class="diff_add"', output) + self.assertNotIn('class="diff_chg"', output) + self.assertNotIn('class="diff_sub"', output) + self.assertEqual(output.count('>Line 1: no newline after<'), 2) + self.assertEqual(output.count('>Line 2: one newline after<'), 2) + self.assertEqual(output.count('>Line 3: several newlines after<'), 2) def test_one_insert(self): m = difflib.Differ().compare('b' * 2, 'a' + 'b' * 2)