From: Benjamin Peterson Date: Fri, 21 May 2010 20:51:45 +0000 (+0000) Subject: return NotImplemented from Mapping when comparing to a non-mapping #8729 X-Git-Tag: v2.7rc1~121 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=eb318d3b160a1631e462d1434e2ae9e1b5cfe158;p=python return NotImplemented from Mapping when comparing to a non-mapping #8729 --- diff --git a/Lib/_abcoll.py b/Lib/_abcoll.py index 326c1f9b50..ad5a9c0758 100644 --- a/Lib/_abcoll.py +++ b/Lib/_abcoll.py @@ -369,8 +369,9 @@ class Mapping(Sized, Iterable, Container): __hash__ = None def __eq__(self, other): - return isinstance(other, Mapping) and \ - dict(self.items()) == dict(other.items()) + if not isinstance(other, Mapping): + return NotImplemented + return dict(self.items()) == dict(other.items()) def __ne__(self, other): return not (self == other) diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 43305836b5..363511f62b 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -1,5 +1,5 @@ -import unittest, doctest +import unittest, doctest, operator import inspect from test import test_support from collections import namedtuple, Counter, OrderedDict @@ -253,6 +253,37 @@ class ABCTestCase(unittest.TestCase): self.assertNotIsInstance(C(), abc) self.assertFalse(issubclass(C, abc)) + def validate_comparison(self, instance): + ops = ['lt', 'gt', 'le', 'ge', 'ne', 'or', 'and', 'xor', 'sub'] + operators = {} + for op in ops: + name = '__' + op + '__' + operators[name] = getattr(operator, name) + + class Other: + def __init__(self): + self.right_side = False + def __eq__(self, other): + self.right_side = True + return True + __lt__ = __eq__ + __gt__ = __eq__ + __le__ = __eq__ + __ge__ = __eq__ + __ne__ = __eq__ + __ror__ = __eq__ + __rand__ = __eq__ + __rxor__ = __eq__ + __rsub__ = __eq__ + + for name, op in operators.items(): + if not hasattr(instance, name): + continue + other = Other() + op(instance, other) + self.assertTrue(other.right_side,'Right side not called for %s.%s' + % (type(instance), name)) + class TestOneTrickPonyABCs(ABCTestCase): def test_Hashable(self): @@ -430,6 +461,14 @@ class TestCollectionABCs(ABCTestCase): self.assertIsInstance(sample(), Set) self.assertTrue(issubclass(sample, Set)) self.validate_abstract_methods(Set, '__contains__', '__iter__', '__len__') + class MySet(Set): + def __contains__(self, x): + return False + def __len__(self): + return 0 + def __iter__(self): + return iter([]) + self.validate_comparison(MySet()) def test_hash_Set(self): class OneTwoThreeSet(Set): @@ -493,6 +532,14 @@ class TestCollectionABCs(ABCTestCase): self.assertTrue(issubclass(sample, Mapping)) self.validate_abstract_methods(Mapping, '__contains__', '__iter__', '__len__', '__getitem__') + class MyMapping(collections.Mapping): + def __len__(self): + return 0 + def __getitem__(self, i): + raise IndexError + def __iter__(self): + return iter(()) + self.validate_comparison(MyMapping()) def test_MutableMapping(self): for sample in [dict]: diff --git a/Misc/NEWS b/Misc/NEWS index 237ed19abb..23b079c93f 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -29,6 +29,9 @@ C-API Library ------- +- Issue #8729: Return NotImplemented from collections.Mapping.__eq__ when + comparing to a non-mapping. + - Issue #8759: Fixed user paths in sysconfig for posix and os2 schemes. - Issue #1285086: Speed up urllib.quote and urllib.unquote for simple cases.