]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-71896: Add a docs warning about trailing newlines ignored by `difflib.HtmlDiff...
authorLenormand Julien <lenormand.julien0@gmail.com>
Sat, 18 Jul 2026 13:40:11 +0000 (15:40 +0200)
committerGitHub <noreply@github.com>
Sat, 18 Jul 2026 13:40:11 +0000 (13:40 +0000)
Doc/library/difflib.rst
Lib/difflib.py
Lib/test/test_difflib.py

index 25edb40e35a630a1e3d866320190f149e2392f19..3ed4768b6a141362c68ecb59fa6184450ef99d05 100644 (file)
@@ -85,6 +85,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:
 
 
index ae8b284b4d36474ed57819a9fbdf34a0374fc2bb..95ba8fd782c6c3c8c8b7dd1738f27ee5bcf9e1af 100644 (file)
@@ -2016,6 +2016,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
index 46c9b2c1d8c9fc4f67abff948a4956cfdb8dcd95..4f99b7c91c654e4282a13d323fcdb1d370740c4e 100644 (file)
@@ -284,6 +284,29 @@ class TestSFpatches(unittest.TestCase):
         self.assertIn('charset="us-ascii"', output)
         self.assertIn('&#305;mpl&#305;c&#305;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&nbsp;1:&nbsp;no&nbsp;newline&nbsp;after<'), 2)
+        self.assertEqual(output.count('>Line&nbsp;2:&nbsp;one&nbsp;newline&nbsp;after<'), 2)
+        self.assertEqual(output.count('>Line&nbsp;3:&nbsp;several&nbsp;newlines&nbsp;after<'), 2)
+
 class TestDiffer(unittest.TestCase):
     def test_close_matches_aligned(self):
         # Of the 4 closely matching pairs, we want 1 to match with 3,