summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/sets.py32
-rw-r--r--Lib/test/test_sets.py35
-rw-r--r--Misc/NEWS17
3 files changed, 61 insertions, 23 deletions
diff --git a/Lib/sets.py b/Lib/sets.py
index 0824fb1..e6a509f 100644
--- a/Lib/sets.py
+++ b/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
diff --git a/Lib/test/test_sets.py b/Lib/test/test_sets.py
index 9223596..d8b7f3f 100644
--- a/Lib/test/test_sets.py
+++ b/Lib/test/test_sets.py
@@ -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:
diff --git a/Misc/NEWS b/Misc/NEWS
index 5405ce5..a6dc7c6 100644
--- a/Misc/NEWS
+++ b/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