]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-71896: Add a docs warning about trailing newlines ignored by `difflib.HtmlD...
authorPeter Bierma <zintensitydev@gmail.com>
Sat, 18 Jul 2026 14:10:12 +0000 (10:10 -0400)
committerGitHub <noreply@github.com>
Sat, 18 Jul 2026 14:10:12 +0000 (14:10 +0000)
(cherry picked from commit 755d97167f6f5d4361c790f8f18df76cae8f2ad2)

Co-authored-by: Lenormand Julien <lenormand.julien0@gmail.com>
Doc/library/difflib.rst
Lib/difflib.py
Lib/test/test_difflib.py

index 85357008b6e14f491051ec409c4e6b2ea604fde6..e53a47289bf5d4103ed891bcdf47527e20b7f724 100644 (file)
@@ -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:
 
 
index db550ceff2e075494e5a8982d1df4365ae7b9613..cc8f2064a19d8e21ba0205e3bf04857f9ad7a283 100644 (file)
@@ -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
index 943d7a659b1e1b6a8ba444e0bb7ca249557bf632..6c7d6c5199905b0e1ba215257f87c6ce19b8f190 100644 (file)
@@ -282,6 +282,28 @@ class TestSFpatches(unittest.TestCase):
         self.assertIn('content="text/html; 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)
 
     def test_one_insert(self):
         m = difflib.Differ().compare('b' * 2, 'a' + 'b' * 2)