https://github.com/python/cpython/commit/ece712197d2d23bcc80937d122e7b9f07338350e
commit: ece712197d2d23bcc80937d122e7b9f07338350e
branch: main
author: Stefan Zetzsche <[email protected]>
committer: encukou <[email protected]>
date: 2026-03-11T13:21:22+01:00
summary:
gh-145546: unittest.util: fix `sorted_list_difference` tail deduplication
(GH-145547)
* fix(unittest.util): Deduplicate tail elements in sorted_list_difference
sorted_list_difference failed to deduplicate remaining elements when one
list was exhausted, causing duplicate values in the result.
Deduplicate before extending.
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
files:
A Misc/NEWS.d/next/Library/2026-03-05-14-13-10.gh-issue-145546.3tnlxx.rst
M Lib/test/test_unittest/test_util.py
M Lib/unittest/util.py
diff --git a/Lib/test/test_unittest/test_util.py
b/Lib/test/test_unittest/test_util.py
index d590a333930278..09ce09b91b7ac2 100644
--- a/Lib/test/test_unittest/test_util.py
+++ b/Lib/test/test_unittest/test_util.py
@@ -26,6 +26,39 @@ def test_sorted_list_difference(self):
self.assertEqual(sorted_list_difference([2], [1, 1]), ([2], [1]))
self.assertEqual(sorted_list_difference([1, 2], [1, 1]), ([2], []))
+ def test_sorted_list_difference_tail_deduplication(self):
+ # Tail deduplication when one list is exhausted before the other.
+ # These exercise the except-IndexError path in sorted_list_difference.
+ self.assertEqual(sorted_list_difference([], [0, 0]), ([], [0]))
+ self.assertEqual(sorted_list_difference([0, 0], []), ([0], []))
+ self.assertEqual(sorted_list_difference([], [1, 1, 2, 2]), ([], [1,
2]))
+ self.assertEqual(sorted_list_difference([1, 1, 2, 2], []), ([1, 2],
[]))
+ # One list exhausts mid-way, leaving duplicated tail in the other.
+ self.assertEqual(sorted_list_difference([1], [1, 2, 2, 3, 3]), ([],
[2, 3]))
+ self.assertEqual(sorted_list_difference([1, 2, 2, 3, 3], [1]), ([2,
3], []))
+
+ def test_sorted_list_difference_strings(self):
+ self.assertEqual(
+ sorted_list_difference(['a', 'b'], ['b', 'c']),
+ (['a'], ['c']))
+ self.assertEqual(
+ sorted_list_difference([], ['a', 'a', 'b']),
+ ([], ['a', 'b']))
+ self.assertEqual(
+ sorted_list_difference(['a', 'a', 'b'], []),
+ (['a', 'b'], []))
+
+ def test_sorted_list_difference_unhashable(self):
+ self.assertEqual(
+ sorted_list_difference([[1], [2]], [[2], [3]]),
+ ([[1]], [[3]]))
+ self.assertEqual(
+ sorted_list_difference([], [[0], [0]]),
+ ([], [[0]]))
+ self.assertEqual(
+ sorted_list_difference([[0], [0]], []),
+ ([[0]], []))
+
def test_unorderable_list_difference(self):
self.assertEqual(unorderable_list_difference([], []), ([], []))
self.assertEqual(unorderable_list_difference([1, 2], []), ([2, 1], []))
diff --git a/Lib/unittest/util.py b/Lib/unittest/util.py
index c7e6b941978cd5..0681163c979587 100644
--- a/Lib/unittest/util.py
+++ b/Lib/unittest/util.py
@@ -63,6 +63,14 @@ def safe_repr(obj, short=False):
def strclass(cls):
return "%s.%s" % (cls.__module__, cls.__qualname__)
+def _dedupe_sorted(lst):
+ """Remove consecutive duplicate elements from a sorted list."""
+ result = []
+ for item in lst:
+ if not result or result[-1] != item:
+ result.append(item)
+ return result
+
def sorted_list_difference(expected, actual):
"""Finds elements in only one or the other of two, sorted input lists.
@@ -98,8 +106,8 @@ def sorted_list_difference(expected, actual):
while actual[j] == a:
j += 1
except IndexError:
- missing.extend(expected[i:])
- unexpected.extend(actual[j:])
+ missing.extend(_dedupe_sorted(expected[i:]))
+ unexpected.extend(_dedupe_sorted(actual[j:]))
break
return missing, unexpected
diff --git
a/Misc/NEWS.d/next/Library/2026-03-05-14-13-10.gh-issue-145546.3tnlxx.rst
b/Misc/NEWS.d/next/Library/2026-03-05-14-13-10.gh-issue-145546.3tnlxx.rst
new file mode 100644
index 00000000000000..e9401bb08c6774
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-03-05-14-13-10.gh-issue-145546.3tnlxx.rst
@@ -0,0 +1,2 @@
+Fix ``unittest.util.sorted_list_difference()`` to deduplicate remaining
+elements when one input list is exhausted before the other.
_______________________________________________
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]