https://github.com/python/cpython/commit/1f3814f68771ed464cfe66938774d2df51748847
commit: 1f3814f68771ed464cfe66938774d2df51748847
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: ZeroIntensity <[email protected]>
date: 2026-07-18T14:11:27Z
summary:

[3.14] gh-71896: Add a docs warning about trailing newlines ignored by 
`difflib.HtmlDiff` (GH-153930) (GH-153966)

(cherry picked from commit 755d97167f6f5d4361c790f8f18df76cae8f2ad2)

Co-authored-by: Lenormand Julien <[email protected]>

files:
M Doc/library/difflib.rst
M Lib/difflib.py
M Lib/test/test_difflib.py

diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst
index 85357008b6e14f..e53a47289bf5d4 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 ac1ba4a6e4ee94..aef2b6f0fd8a1b 100644
--- a/Lib/difflib.py
+++ b/Lib/difflib.py
@@ -1977,6 +1977,8 @@ def 
make_table(self,fromlines,tolines,fromdesc='',todesc='',context=False,
 
         # 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 36be5c99c3f01b..518b8c5278e4c4 100644
--- a/Lib/test/test_difflib.py
+++ b/Lib/test/test_difflib.py
@@ -282,6 +282,29 @@ def 
test_make_file_usascii_charset_with_nonascii_input(self):
         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)
+
 class TestDiffer(unittest.TestCase):
     def test_close_matches_aligned(self):
         # Of the 4 closely matching pairs, we want 1 to match with 3,

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to