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()
|
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):
|
def __cmp__(self, other):
|
||||||
raise TypeError, "can't compare sets using cmp()"
|
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):
|
def __eq__(self, other):
|
||||||
self._binary_sanity_check(other)
|
if isinstance(other, BaseSet):
|
||||||
return self._data == other._data
|
return self._data == other._data
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
self._binary_sanity_check(other)
|
if isinstance(other, BaseSet):
|
||||||
return self._data != other._data
|
return self._data != other._data
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
# Copying operations
|
# Copying operations
|
||||||
|
|
||||||
|
@ -232,7 +232,16 @@ class TestBinaryOps(unittest.TestCase):
|
|||||||
|
|
||||||
def test_cmp(self):
|
def test_cmp(self):
|
||||||
a, b = Set('a'), Set('b')
|
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):
|
class TestOnlySetsInBinaryOps(unittest.TestCase):
|
||||||
|
|
||||||
def test_cmp(self):
|
def test_eq_ne(self):
|
||||||
try:
|
# Unlike the others, this is testing that == and != *are* allowed.
|
||||||
self.other == self.set
|
self.assertEqual(self.other == self.set, False)
|
||||||
self.fail("expected TypeError")
|
self.assertEqual(self.set == self.other, False)
|
||||||
except TypeError:
|
self.assertEqual(self.other != self.set, True)
|
||||||
pass
|
self.assertEqual(self.set != self.other, True)
|
||||||
try:
|
|
||||||
self.set != self.other
|
def test_ge_gt_lt_le(self):
|
||||||
self.fail("expected TypeError")
|
# Unlike the others, this is testing that == and != *are* allowed.
|
||||||
except TypeError:
|
self.assertRaises(TypeError, lambda: self.set < self.other)
|
||||||
pass
|
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):
|
def test_union_update(self):
|
||||||
try:
|
try:
|
||||||
|
17
Misc/NEWS
17
Misc/NEWS
@ -31,6 +31,13 @@ Extension modules
|
|||||||
Library
|
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
|
- os.listdir() now returns Unicode strings on platforms that set
|
||||||
Py_FileSystemDefaultEncoding, for file names that are not representable
|
Py_FileSystemDefaultEncoding, for file names that are not representable
|
||||||
in ASCII. (This currently only affects MacOS X; on Windows versions
|
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
|
- A new method MacOS.WMAvailable() returns true if it is safe to access
|
||||||
the window manager, false otherwise.
|
the window manager, false otherwise.
|
||||||
|
|
||||||
- EasyDialogs dialogs are now movable-modal.
|
- EasyDialogs dialogs are now movable-modal.
|
||||||
|
|
||||||
|
|
||||||
@ -343,8 +350,8 @@ Library
|
|||||||
- the platform dependent path related variables sep, altsep, extsep,
|
- the platform dependent path related variables sep, altsep, extsep,
|
||||||
pathsep, curdir, pardir and defpath are now defined in the platform
|
pathsep, curdir, pardir and defpath are now defined in the platform
|
||||||
dependent path modules (e.g. ntpath.py) rather than os.py, so these
|
dependent path modules (e.g. ntpath.py) rather than os.py, so these
|
||||||
variables are now available via os.path. They continue to be
|
variables are now available via os.path. They continue to be
|
||||||
available from the os module.
|
available from the os module.
|
||||||
(see <http://www.python.org/sf/680789>).
|
(see <http://www.python.org/sf/680789>).
|
||||||
|
|
||||||
- array.array was added to the types repr.py knows about (see
|
- 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.
|
- Type Carbon.File.FSCatalogInfo and supporting methods have been implemented.
|
||||||
This also makes macfs.FSSpec.SetDates() work again.
|
This also makes macfs.FSSpec.SetDates() work again.
|
||||||
|
|
||||||
- There is a new module pimp, the package install manager for Python, and
|
- There is a new module pimp, the package install manager for Python, and
|
||||||
accompanying applet PackageManager. These allow you to easily download
|
accompanying applet PackageManager. These allow you to easily download
|
||||||
and install pretested extension packages either in source or binary
|
and install pretested extension packages either in source or binary
|
||||||
form. Only in MacPython-OSX.
|
form. Only in MacPython-OSX.
|
||||||
|
|
||||||
- Applets are now built with bundlebuilder in MacPython-OSX, which should make
|
- Applets are now built with bundlebuilder in MacPython-OSX, which should make
|
||||||
them more robust and also provides a path towards BuildApplication. The
|
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
|
downside of this change is that applets can no longer be run from the
|
||||||
|
Loading…
Reference in New Issue
Block a user