mirror of
https://github.com/python/cpython.git
synced 2024-11-25 19:03:49 +08:00
SF bug 693121: Set == non-Set is a TypeError.
Allow mixed-type __eq__ and __ne__ for Set objects. This is messier than I'd like because Set *also* implements __cmp__. I know of one glitch now: cmp(s, t) returns 0 now when s and t are both Sets and s == t, despite that Set.__cmp__ unconditionally raises TypeError (and by intent). The rub is that __eq__ gets tried first, and the x.__eq__(y) True result convinces Python that cmp(x, y) is 0 without even calling Set.__cmp__.
This commit is contained in:
parent
3ba491e6b1
commit
44f14b0399
32
Lib/sets.py
32
Lib/sets.py
@ -102,20 +102,40 @@ class BaseSet(object):
|
||||
"""
|
||||
return self._data.iterkeys()
|
||||
|
||||
# Three-way comparison is not supported
|
||||
# Three-way comparison is not supported. However, because __eq__ is
|
||||
# tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and
|
||||
# then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this
|
||||
# case).
|
||||
|
||||
def __cmp__(self, other):
|
||||
raise TypeError, "can't compare sets using cmp()"
|
||||
|
||||
# Equality comparisons using the underlying dicts
|
||||
# Equality comparisons using the underlying dicts. Mixed-type comparisons
|
||||
# are allowed here, where Set == z for non-Set z always returns False,
|
||||
# and Set != z always True. This allows expressions like "x in y" to
|
||||
# give the expected result when y is a sequence of mixed types, not
|
||||
# raising a pointless TypeError just because y contains a Set, or x is
|
||||
# a Set and y contain's a non-set ("in" invokes only __eq__).
|
||||
# Subtle: it would be nicer if __eq__ and __ne__ could return
|
||||
# NotImplemented instead of True or False. Then the other comparand
|
||||
# would get a chance to determine the result, and if the other comparand
|
||||
# also returned NotImplemented then it would fall back to object address
|
||||
# comparison (which would always return False for __eq__ and always
|
||||
# True for __ne__). However, that doesn't work, because this type
|
||||
# *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented,
|
||||
# Python tries __cmp__ next, and the __cmp__ here then raises TypeError.
|
||||
|
||||
def __eq__(self, other):
|
||||
self._binary_sanity_check(other)
|
||||
return self._data == other._data
|
||||
if isinstance(other, BaseSet):
|
||||
return self._data == other._data
|
||||
else:
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
self._binary_sanity_check(other)
|
||||
return self._data != other._data
|
||||
if isinstance(other, BaseSet):
|
||||
return self._data != other._data
|
||||
else:
|
||||
return True
|
||||
|
||||
# Copying operations
|
||||
|
||||
|
@ -232,7 +232,16 @@ class TestBinaryOps(unittest.TestCase):
|
||||
|
||||
def test_cmp(self):
|
||||
a, b = Set('a'), Set('b')
|
||||
self.assertRaises(TypeError, cmp, (a,b))
|
||||
self.assertRaises(TypeError, cmp, a, b)
|
||||
|
||||
# You can view this as a buglet: cmp(a, a) does not raise TypeError,
|
||||
# because __eq__ is tried before __cmp__, and a.__eq__(a) returns,
|
||||
# which Python thinks is good enough to synthesize a cmp() result
|
||||
# without calling __cmp__.
|
||||
self.assertEqual(cmp(a, a), 0)
|
||||
|
||||
self.assertRaises(TypeError, cmp, a, 12)
|
||||
self.assertRaises(TypeError, cmp, "abc", a)
|
||||
|
||||
#==============================================================================
|
||||
|
||||
@ -476,17 +485,19 @@ class TestSubsetNonOverlap(TestSubsets):
|
||||
|
||||
class TestOnlySetsInBinaryOps(unittest.TestCase):
|
||||
|
||||
def test_cmp(self):
|
||||
try:
|
||||
self.other == self.set
|
||||
self.fail("expected TypeError")
|
||||
except TypeError:
|
||||
pass
|
||||
try:
|
||||
self.set != self.other
|
||||
self.fail("expected TypeError")
|
||||
except TypeError:
|
||||
pass
|
||||
def test_eq_ne(self):
|
||||
# Unlike the others, this is testing that == and != *are* allowed.
|
||||
self.assertEqual(self.other == self.set, False)
|
||||
self.assertEqual(self.set == self.other, False)
|
||||
self.assertEqual(self.other != self.set, True)
|
||||
self.assertEqual(self.set != self.other, True)
|
||||
|
||||
def test_ge_gt_lt_le(self):
|
||||
# Unlike the others, this is testing that == and != *are* allowed.
|
||||
self.assertRaises(TypeError, lambda: self.set < self.other)
|
||||
self.assertRaises(TypeError, lambda: self.set <= self.other)
|
||||
self.assertRaises(TypeError, lambda: self.set > self.other)
|
||||
self.assertRaises(TypeError, lambda: self.set >= self.other)
|
||||
|
||||
def test_union_update(self):
|
||||
try:
|
||||
|
17
Misc/NEWS
17
Misc/NEWS
@ -31,6 +31,13 @@ Extension modules
|
||||
Library
|
||||
-------
|
||||
|
||||
- sets.Set objects now support mixed-type __eq__ and __ne__, instead
|
||||
of raising TypeError. If x is a Set object and y is a non-Set object,
|
||||
x == y is False, and x != y is True. This is akin to the change made
|
||||
for mixed-type comparisons of datetime objects in 2.3a2; more info
|
||||
about the rationale is in the NEWS entry for that. See also SF bug
|
||||
report <http://www.python.org/sf/693121>.
|
||||
|
||||
- os.listdir() now returns Unicode strings on platforms that set
|
||||
Py_FileSystemDefaultEncoding, for file names that are not representable
|
||||
in ASCII. (This currently only affects MacOS X; on Windows versions
|
||||
@ -83,7 +90,7 @@ Mac
|
||||
|
||||
- A new method MacOS.WMAvailable() returns true if it is safe to access
|
||||
the window manager, false otherwise.
|
||||
|
||||
|
||||
- EasyDialogs dialogs are now movable-modal.
|
||||
|
||||
|
||||
@ -343,8 +350,8 @@ Library
|
||||
- the platform dependent path related variables sep, altsep, extsep,
|
||||
pathsep, curdir, pardir and defpath are now defined in the platform
|
||||
dependent path modules (e.g. ntpath.py) rather than os.py, so these
|
||||
variables are now available via os.path. They continue to be
|
||||
available from the os module.
|
||||
variables are now available via os.path. They continue to be
|
||||
available from the os module.
|
||||
(see <http://www.python.org/sf/680789>).
|
||||
|
||||
- array.array was added to the types repr.py knows about (see
|
||||
@ -499,12 +506,12 @@ Mac
|
||||
|
||||
- Type Carbon.File.FSCatalogInfo and supporting methods have been implemented.
|
||||
This also makes macfs.FSSpec.SetDates() work again.
|
||||
|
||||
|
||||
- There is a new module pimp, the package install manager for Python, and
|
||||
accompanying applet PackageManager. These allow you to easily download
|
||||
and install pretested extension packages either in source or binary
|
||||
form. Only in MacPython-OSX.
|
||||
|
||||
|
||||
- Applets are now built with bundlebuilder in MacPython-OSX, which should make
|
||||
them more robust and also provides a path towards BuildApplication. The
|
||||
downside of this change is that applets can no longer be run from the
|
||||
|
Loading…
Reference in New Issue
Block a user