From 584e8aedc3d66721efcdcbd1a43d4c5b7476427b Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 5 May 2016 11:14:06 +0300 Subject: [PATCH] Issue 26915: Add identity checks to the collections ABC __contains__ methods. --- Lib/_collections_abc.py | 7 ++++--- Lib/test/test_collections.py | 22 +++++++++++++++++++++- Misc/NEWS | 5 +++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py index d3375847e28..31583731fc7 100644 --- a/Lib/_collections_abc.py +++ b/Lib/_collections_abc.py @@ -689,7 +689,7 @@ class ItemsView(MappingView, Set): except KeyError: return False else: - return v == value + return v is value or v == value def __iter__(self): for key in self._mapping: @@ -704,7 +704,8 @@ class ValuesView(MappingView): def __contains__(self, value): for key in self._mapping: - if value == self._mapping[key]: + v = self._mapping[key] + if v is value or v == value: return True return False @@ -839,7 +840,7 @@ class Sequence(Sized, Reversible, Container): def __contains__(self, value): for v in self: - if v == value: + if v is value or v == value: return True return False diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 42024628c5e..3824a87365f 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -23,7 +23,7 @@ from collections.abc import Awaitable, Coroutine, AsyncIterator, AsyncIterable from collections.abc import Hashable, Iterable, Iterator, Generator, Reversible from collections.abc import Sized, Container, Callable from collections.abc import Set, MutableSet -from collections.abc import Mapping, MutableMapping, KeysView, ItemsView +from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView from collections.abc import Sequence, MutableSequence from collections.abc import ByteString @@ -1074,6 +1074,26 @@ class TestCollectionABCs(ABCTestCase): self.assertFalse(ncs > cs) self.assertTrue(ncs >= cs) + def test_issue26915(self): + # Container membership test should check identity first + class CustomEqualObject: + def __eq__(self, other): + return False + class CustomSequence(list): + def __contains__(self, value): + return Sequence.__contains__(self, value) + + nan = float('nan') + obj = CustomEqualObject() + containers = [ + CustomSequence([nan, obj]), + ItemsView({1: nan, 2: obj}), + ValuesView({1: nan, 2: obj}) + ] + for container in containers: + for elem in container: + self.assertIn(elem, container) + def assertSameSet(self, s1, s2): # coerce both to a real set then check equality self.assertSetEqual(set(s1), set(s2)) diff --git a/Misc/NEWS b/Misc/NEWS index 4833aa37e29..875c1136021 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -268,6 +268,11 @@ Library - Issue #26873: xmlrpc now raises ResponseError on unsupported type tags instead of silently return incorrect result. +- Issue #26915: The __contains__ methods in the collections ABCs now check + for identity before checking equality. This better matches the behavior + of the concrete classes, allows sensible handling of NaNs, and makes it + easier to reason about container invariants. + - Issue #26711: Fixed the comparison of plistlib.Data with other types. - Issue #24114: Fix an uninitialized variable in `ctypes.util`.