fix: honour a falsey key in all_unique
all_unique applied the key with `map(key, iterable) if key else iterable`, so a falsey callable was silently ignored. it now checks `key is not None` like the other key functions, same fix as #1309.
This commit is contained in:
1 parent
d1d5e24692
commit
8780c614af
3 files changed
+15
-1
No files matched your search
@@ -9,6 +9,7 @@ Unreleased
|
||||
----------
|
||||
|
||||
* Changes to existing functions:
|
||||
* :func:`all_unique` now honours a falsey *key* callable (such as an object whose ``__bool__`` returns ``False``) instead of silently ignoring it.
|
||||
* :func:`bucket` now honours a falsey *validator* callable (such as an empty ``set`` subclass that defines ``__call__``) instead of silently replacing it with the default that accepts every key.
|
||||
* :func:`ichunked` now matches :func:`chunked` for ``n = 0`` and negative *n*: it yields nothing for a zero chunk size (without consuming the source) and raises ``ValueError: n must be at least 0`` instead of leaking :func:`itertools.islice`'s error.
|
||||
* :func:`running_min`, :func:`running_max`, :func:`running_mean`, and :func:`running_statistics` now consistently validate *maxlen* as an integer and support the index protocol.
|
||||
|
||||
@@ -4279,7 +4279,7 @@ def all_unique(iterable, key=None):
|
||||
seenset_add = seenset.add
|
||||
seenlist = []
|
||||
seenlist_add = seenlist.append
|
||||
for element in map(key, iterable) if key else iterable:
|
||||
for element in map(key, iterable) if key is not None else iterable:
|
||||
try:
|
||||
if element in seenset:
|
||||
return False
|
||||
|
||||
@@ -5153,6 +5153,19 @@ class AllUniqueTests(TestCase):
|
||||
self.assertEqual(mi.all_unique(iterable, lambda x: x), True)
|
||||
self.assertEqual(mi.all_unique(iterable, str.lower), False)
|
||||
|
||||
def test_falsey_key_is_honoured(self):
|
||||
# A falsey callable is still a valid key and must not be ignored
|
||||
class CaseFold:
|
||||
def __bool__(self):
|
||||
return False
|
||||
|
||||
def __call__(self, x):
|
||||
return x.lower()
|
||||
|
||||
key = CaseFold()
|
||||
self.assertFalse(key) # Sanity: the key itself is falsey
|
||||
self.assertEqual(mi.all_unique(['A', 'B', 'C', 'b'], key), False)
|
||||
|
||||
def test_infinite(self):
|
||||
self.assertEqual(mi.all_unique(mi.prepend(3, count())), False)
|
||||
|
||||
|
||||
Reference in new issue
Block a user