Skip to content

Commit 16219a5

Browse files
Replace dict.fromkeys with equality-based dedup for unhashable types
dict.fromkeys requires hashable elements, which narrows the input contract. Replace with a simple consecutive-duplicate removal that only uses equality comparison, matching the main loop's approach. Add test cases for strings and unhashable types (lists).
1 parent 0d475d2 commit 16219a5

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

Lib/test/test_unittest/test_util.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,28 @@ def test_sorted_list_difference_tail_deduplication(self):
3737
self.assertEqual(sorted_list_difference([1], [1, 2, 2, 3, 3]), ([], [2, 3]))
3838
self.assertEqual(sorted_list_difference([1, 2, 2, 3, 3], [1]), ([2, 3], []))
3939

40+
def test_sorted_list_difference_strings(self):
41+
self.assertEqual(
42+
sorted_list_difference(['a', 'b'], ['b', 'c']),
43+
(['a'], ['c']))
44+
self.assertEqual(
45+
sorted_list_difference([], ['a', 'a', 'b']),
46+
([], ['a', 'b']))
47+
self.assertEqual(
48+
sorted_list_difference(['a', 'a', 'b'], []),
49+
(['a', 'b'], []))
50+
51+
def test_sorted_list_difference_unhashable(self):
52+
self.assertEqual(
53+
sorted_list_difference([[1], [2]], [[2], [3]]),
54+
([[1]], [[3]]))
55+
self.assertEqual(
56+
sorted_list_difference([], [[0], [0]]),
57+
([], [[0]]))
58+
self.assertEqual(
59+
sorted_list_difference([[0], [0]], []),
60+
([[0]], []))
61+
4062
def test_unorderable_list_difference(self):
4163
self.assertEqual(unorderable_list_difference([], []), ([], []))
4264
self.assertEqual(unorderable_list_difference([1, 2], []), ([2, 1], []))

Lib/unittest/util.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,17 @@ def safe_repr(obj, short=False):
6363
def strclass(cls):
6464
return "%s.%s" % (cls.__module__, cls.__qualname__)
6565

66+
def _dedupe_sorted(lst):
67+
"""Remove consecutive duplicate elements from a sorted list.
68+
69+
Only requires that elements support equality comparison,
70+
not hashing."""
71+
result = []
72+
for item in lst:
73+
if not result or result[-1] != item:
74+
result.append(item)
75+
return result
76+
6677
def sorted_list_difference(expected, actual):
6778
"""Finds elements in only one or the other of two, sorted input lists.
6879
@@ -98,8 +109,8 @@ def sorted_list_difference(expected, actual):
98109
while actual[j] == a:
99110
j += 1
100111
except IndexError:
101-
missing.extend(dict.fromkeys(expected[i:]))
102-
unexpected.extend(dict.fromkeys(actual[j:]))
112+
missing.extend(_dedupe_sorted(expected[i:]))
113+
unexpected.extend(_dedupe_sorted(actual[j:]))
103114
break
104115
return missing, unexpected
105116

0 commit comments

Comments
 (0)