diff --git a/Include/object.h b/Include/object.h index ab42f8a39fd..30f0bec279e 100644 --- a/Include/object.h +++ b/Include/object.h @@ -379,6 +379,7 @@ PyAPI_FUNC(PyObject *) PyObject_Unicode(PyObject *); PyAPI_FUNC(int) PyObject_Compare(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyObject_RichCompare(PyObject *, PyObject *, int); PyAPI_FUNC(int) PyObject_RichCompareBool(PyObject *, PyObject *, int); +PyAPI_FUNC(PyObject *) Py_CmpToRich(int op, int cmp); PyAPI_FUNC(PyObject *) PyObject_GetAttrString(PyObject *, const char *); PyAPI_FUNC(int) PyObject_SetAttrString(PyObject *, const char *, PyObject *); PyAPI_FUNC(int) PyObject_HasAttrString(PyObject *, const char *); diff --git a/Lib/StringIO.py b/Lib/StringIO.py index 1e5f25408d7..7d57d80947f 100644 --- a/Lib/StringIO.py +++ b/Lib/StringIO.py @@ -116,7 +116,7 @@ class StringIO: _complain_ifclosed(self.closed) return self.pos - def read(self, n = -1): + def read(self, n=None): """Read at most size bytes from the file (less if the read hits EOF before obtaining size bytes). @@ -128,6 +128,8 @@ class StringIO: if self.buflist: self.buf += ''.join(self.buflist) self.buflist = [] + if n is None: + n = -1 if n < 0: newpos = self.len else: diff --git a/Lib/UserDict.py b/Lib/UserDict.py index b560a11c5b3..1190221b86d 100644 --- a/Lib/UserDict.py +++ b/Lib/UserDict.py @@ -8,11 +8,16 @@ class UserDict: if len(kwargs): self.update(kwargs) def __repr__(self): return repr(self.data) - def __cmp__(self, dict): + def __eq__(self, dict): if isinstance(dict, UserDict): - return cmp(self.data, dict.data) + return self.data == dict.data else: - return cmp(self.data, dict) + return self.data == dict + def __ne__(self, dict): + if isinstance(dict, UserDict): + return self.data != dict.data + else: + return self.data != dict def __len__(self): return len(self.data) def __getitem__(self, key): if key in self.data: @@ -162,11 +167,13 @@ class DictMixin: return default def __repr__(self): return repr(dict(self.iteritems())) - def __cmp__(self, other): - if other is None: - return 1 + def __eq__(self, other): if isinstance(other, DictMixin): other = dict(other.iteritems()) - return cmp(dict(self.iteritems()), other) + return dict(self.iteritems()) == other + def __ne__(self, other): + if isinstance(other, DictMixin): + other = dict(other.iteritems()) + return dict(self.iteritems()) != other def __len__(self): return len(self.keys()) diff --git a/Lib/UserString.py b/Lib/UserString.py index 60dc34bc4b3..271026c19d7 100755 --- a/Lib/UserString.py +++ b/Lib/UserString.py @@ -25,11 +25,37 @@ class UserString: def __complex__(self): return complex(self.data) def __hash__(self): return hash(self.data) - def __cmp__(self, string): + def __eq__(self, string): if isinstance(string, UserString): - return cmp(self.data, string.data) + return self.data == string.data else: - return cmp(self.data, string) + return self.data == string + def __ne__(self, string): + if isinstance(string, UserString): + return self.data != string.data + else: + return self.data != string + def __lt__(self, string): + if isinstance(string, UserString): + return self.data < string.data + else: + return self.data < string + def __le__(self, string): + if isinstance(string, UserString): + return self.data <= string.data + else: + return self.data <= string + def __gt__(self, string): + if isinstance(string, UserString): + return self.data > string.data + else: + return self.data > string + def __ge__(self, string): + if isinstance(string, UserString): + return self.data >= string.data + else: + return self.data >= string + def __contains__(self, char): return char in self.data diff --git a/Lib/ctypes/test/test_simplesubclasses.py b/Lib/ctypes/test/test_simplesubclasses.py index 71551707421..420c0a0c978 100644 --- a/Lib/ctypes/test/test_simplesubclasses.py +++ b/Lib/ctypes/test/test_simplesubclasses.py @@ -2,10 +2,10 @@ import unittest from ctypes import * class MyInt(c_int): - def __cmp__(self, other): + def __eq__(self, other): if type(other) != MyInt: - return -1 - return cmp(self.value, other.value) + return NotImplementedError + return self.value == other.value class Test(unittest.TestCase): diff --git a/Lib/decimal.py b/Lib/decimal.py index 210db523f9b..a8fde82d4e8 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -706,6 +706,26 @@ class Decimal(object): return NotImplemented return self.__cmp__(other) != 0 + def __lt__(self, other): + if not isinstance(other, (Decimal, int, long)): + return NotImplemented + return self.__cmp__(other) < 0 + + def __le__(self, other): + if not isinstance(other, (Decimal, int, long)): + return NotImplemented + return self.__cmp__(other) <= 0 + + def __gt__(self, other): + if not isinstance(other, (Decimal, int, long)): + return NotImplemented + return self.__cmp__(other) > 0 + + def __ge__(self, other): + if not isinstance(other, (Decimal, int, long)): + return NotImplemented + return self.__cmp__(other) >= 0 + def compare(self, other, context=None): """Compares one to another. @@ -1894,6 +1914,7 @@ class Decimal(object): ans = self._check_nans(context=context) if ans: return ans + return self if self._exp >= 0: return self if context is None: diff --git a/Lib/distutils/version.py b/Lib/distutils/version.py index 71a56147195..2cd36365e75 100644 --- a/Lib/distutils/version.py +++ b/Lib/distutils/version.py @@ -32,7 +32,8 @@ from types import StringType class Version: """Abstract base class for version numbering classes. Just provides constructor (__init__) and reproducer (__repr__), because those - seem to be the same for all version numbering classes. + seem to be the same for all version numbering classes; and route + rich comparisons to __cmp__. """ def __init__ (self, vstring=None): @@ -42,6 +43,42 @@ class Version: def __repr__ (self): return "%s ('%s')" % (self.__class__.__name__, str(self)) + def __eq__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c == 0 + + def __ne__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c != 0 + + def __lt__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c < 0 + + def __le__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c <= 0 + + def __gt__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c > 0 + + def __ge__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c >= 0 + # Interface for version-number classes -- must be implemented # by the following classes (the concrete ones -- Version should diff --git a/Lib/doctest.py b/Lib/doctest.py index fe734b3f82a..2774ec16e7b 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -469,11 +469,12 @@ class DocTest: # This lets us sort tests by name: - def __cmp__(self, other): + def __lt__(self, other): if not isinstance(other, DocTest): - return -1 - return cmp((self.name, self.filename, self.lineno, id(self)), - (other.name, other.filename, other.lineno, id(other))) + return NotImplemented + return ((self.name, self.filename, self.lineno, id(self)) + < + (other.name, other.filename, other.lineno, id(other))) ###################################################################### ## 3. DocTestParser diff --git a/Lib/optparse.py b/Lib/optparse.py index 0972f74bcce..f8f643de1cb 100644 --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -838,13 +838,13 @@ class Values: __repr__ = _repr - def __cmp__(self, other): + def __eq__(self, other): if isinstance(other, Values): - return cmp(self.__dict__, other.__dict__) + return self.__dict__ == other.__dict__ elif isinstance(other, types.DictType): - return cmp(self.__dict__, other) + return self.__dict__ == other else: - return -1 + return NotImplemented def _update_careful(self, dict): """ diff --git a/Lib/plat-mac/plistlib.py b/Lib/plat-mac/plistlib.py index 49bd5563c94..f91f1d3fc60 100644 --- a/Lib/plat-mac/plistlib.py +++ b/Lib/plat-mac/plistlib.py @@ -374,13 +374,13 @@ class Data: def asBase64(self, maxlinelength=76): return _encodeBase64(self.data, maxlinelength) - def __cmp__(self, other): + def __eq__(self, other): if isinstance(other, self.__class__): - return cmp(self.data, other.data) + return self.data == other.data elif isinstance(other, str): - return cmp(self.data, other) + return self.data == other else: - return cmp(id(self), id(other)) + return id(self) == id(other) def __repr__(self): return "%s(%s)" % (self.__class__.__name__, repr(self.data)) diff --git a/Lib/pprint.py b/Lib/pprint.py index 19a3dc249ce..97135ebfe54 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -246,7 +246,12 @@ def _safe_repr(object, context, maxlevels, level): append = components.append level += 1 saferepr = _safe_repr - for k, v in sorted(object.items()): + items = object.items() + try: + items = sorted(items) + except TypeError: + items = sorted(items, key=lambda (k, v): (str(type(k)), k, v)) + for k, v in items: krepr, kreadable, krecur = saferepr(k, context, maxlevels, level) vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level) append("%s: %s" % (krepr, vrepr)) diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py index 8da5722d55f..9a11f5c964a 100644 --- a/Lib/sqlite3/test/types.py +++ b/Lib/sqlite3/test/types.py @@ -86,6 +86,12 @@ class DeclTypesTests(unittest.TestCase): else: return 1 + def __eq__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c == 0 + def __conform__(self, protocol): if protocol is sqlite.PrepareProtocol: return self.val diff --git a/Lib/test/mapping_tests.py b/Lib/test/mapping_tests.py index b917540d49a..15f9addcd70 100644 --- a/Lib/test/mapping_tests.py +++ b/Lib/test/mapping_tests.py @@ -60,10 +60,10 @@ class BasicTestMappingProtocol(unittest.TestCase): for k in self.other: self.failIf(k in d) #cmp - self.assertEqual(cmp(p,p), 0) - self.assertEqual(cmp(d,d), 0) - self.assertEqual(cmp(p,d), -1) - self.assertEqual(cmp(d,p), 1) + self.assertEqual(p, p) + self.assertEqual(d, d) + self.assertNotEqual(p, d) + self.assertNotEqual(d, p) #__non__zero__ if p: self.fail("Empty mapping must compare to False") if not d: self.fail("Full mapping must compare to True") @@ -623,9 +623,10 @@ class TestHashMappingProtocol(TestMappingProtocol): d = self._full_mapping({1: BadRepr()}) self.assertRaises(Exc, repr, d) - def test_le(self): - self.assert_(not (self._empty_mapping() < self._empty_mapping())) - self.assert_(not (self._full_mapping({1: 2}) < self._full_mapping({1L: 2L}))) + def test_eq(self): + self.assertEqual(self._empty_mapping(), self._empty_mapping()) + self.assertEqual(self._full_mapping({1: 2}), + self._full_mapping({1L: 2L})) class Exc(Exception): pass @@ -633,16 +634,12 @@ class TestHashMappingProtocol(TestMappingProtocol): def __eq__(self, other): raise Exc() def __hash__(self): - return 42 + return 1 d1 = self._full_mapping({BadCmp(): 1}) d2 = self._full_mapping({1: 1}) - try: - d1 < d2 - except Exc: - pass - else: - self.fail("< didn't raise Exc") + self.assertRaises(Exc, lambda: BadCmp()==1) + self.assertRaises(Exc, lambda: d1==d2) def test_setdefault(self): TestMappingProtocol.test_setdefault(self) diff --git a/Lib/test/output/test_class b/Lib/test/output/test_class index 14d43a8a18a..7d8ab5e4652 100644 --- a/Lib/test/output/test_class +++ b/Lib/test/output/test_class @@ -51,16 +51,16 @@ __hex__: () __hash__: () __repr__: () __str__: () -__cmp__: (1,) -__cmp__: (1,) -__cmp__: (1,) -__cmp__: (1,) -__cmp__: (1,) -__cmp__: (1,) -__cmp__: (1,) -__cmp__: (1,) -__cmp__: (1,) -__cmp__: (1,) +__eq__: (1,) +__lt__: (1,) +__gt__: (1,) +__ne__: (1,) +__ne__: (1,) +__eq__: (1,) +__gt__: (1,) +__lt__: (1,) +__ne__: (1,) +__ne__: (1,) __del__: () __getattr__: ('spam',) __setattr__: ('eggs', 'spam, spam, spam and ham') diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 42853c4ac21..30c1e7c59da 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -65,8 +65,8 @@ class ExtensionSaver: copy_reg.add_extension(pair[0], pair[1], code) class C: - def __cmp__(self, other): - return cmp(self.__dict__, other.__dict__) + def __eq__(self, other): + return self.__dict__ == other.__dict__ import __main__ __main__.C = C diff --git a/Lib/test/test_bisect.py b/Lib/test/test_bisect.py index 302ff639a74..772e8083622 100644 --- a/Lib/test/test_bisect.py +++ b/Lib/test/test_bisect.py @@ -174,8 +174,13 @@ class GetOnly: class CmpErr: "Dummy element that always raises an error during comparison" - def __cmp__(self, other): + def __lt__(self, other): raise ZeroDivisionError + __gt__ = __lt__ + __le__ = __lt__ + __ge__ = __lt__ + __eq__ = __lt__ + __ne__ = __lt__ class TestErrorHandling(unittest.TestCase): diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py new file mode 100644 index 00000000000..eb6e9ea59b6 --- /dev/null +++ b/Lib/test/test_buffer.py @@ -0,0 +1,44 @@ +"""Unit tests for buffer objects. + +For now, we just test (the brand new) rich comparison. + +""" + +import unittest +from test import test_support + +class BufferTests(unittest.TestCase): + + def test_comparison(self): + a = buffer("a.b.c") + b = buffer("a.b" + ".c") + self.assert_(a == b) + self.assert_(a <= b) + self.assert_(a >= b) + self.assert_(a == "a.b.c") + self.assert_(a <= "a.b.c") + self.assert_(a >= "a.b.c") + b = buffer("a.b.c.d") + self.assert_(a != b) + self.assert_(a <= b) + self.assert_(a < b) + self.assert_(a != "a.b.c.d") + self.assert_(a < "a.b.c.d") + self.assert_(a <= "a.b.c.d") + b = buffer("a.b") + self.assert_(a != b) + self.assert_(a >= b) + self.assert_(a > b) + self.assert_(a != "a.b") + self.assert_(a > "a.b") + self.assert_(a >= "a.b") + b = object() + self.assert_(a != b) + self.failIf(a == b) + self.assertRaises(TypeError, lambda: a < b) + +def test_main(): + test_support.run_unittest(BufferTests) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index b0dbd4a17b6..d3a272bfb3a 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -179,7 +179,8 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(ValueError, chr, 256) self.assertRaises(TypeError, chr) - def test_cmp(self): + def XXX_test_cmp(self): + # cmp() is no longer supported self.assertEqual(cmp(-1, 1), -1) self.assertEqual(cmp(1, -1), 1) self.assertEqual(cmp(1, 1), 0) @@ -1132,8 +1133,14 @@ class BuiltinTest(unittest.TestCase): map(None, Squares(3), Squares(2)), [(0,0), (1,1), (4,None)] ) + def Max(a, b): + if a is None: + return b + if b is None: + return a + return max(a, b) self.assertEqual( - map(max, Squares(3), Squares(2)), + map(Max, Squares(3), Squares(2)), [0, 1, 4] ) self.assertRaises(TypeError, map) @@ -1201,7 +1208,7 @@ class BuiltinTest(unittest.TestCase): class BadNumber: def __cmp__(self, other): raise ValueError - self.assertRaises(ValueError, min, (42, BadNumber())) + self.assertRaises(TypeError, min, (42, BadNumber())) for stmt in ( "min(key=int)", # no args @@ -1379,8 +1386,11 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(ValueError, range, a, a + 1, long(0)) class badzero(int): - def __cmp__(self, other): + def __eq__(self, other): raise RuntimeError + __ne__ = __lt__ = __gt__ = __le__ = __ge__ = __eq__ + + # XXX This won't (but should!) raise RuntimeError if a is an int... self.assertRaises(RuntimeError, range, a, a + 1, badzero(1)) # Reject floats when it would require PyLongs to represent. @@ -1594,7 +1604,7 @@ class TestSorted(unittest.TestCase): data.reverse() random.shuffle(copy) - self.assertEqual(data, sorted(copy, cmp=lambda x, y: cmp(y,x))) + self.assertEqual(data, sorted(copy, cmp=lambda x, y: (x < y) - (x > y))) self.assertNotEqual(data, copy) random.shuffle(copy) self.assertEqual(data, sorted(copy, key=lambda x: -x)) diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index e414324d67a..893890d12b8 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -212,7 +212,7 @@ class CalendarTestCase(unittest.TestCase): self.assertEqual(calendar.isleap(2003), 0) def test_setfirstweekday(self): - self.assertRaises(ValueError, calendar.setfirstweekday, 'flabber') + self.assertRaises(TypeError, calendar.setfirstweekday, 'flabber') self.assertRaises(ValueError, calendar.setfirstweekday, -1) self.assertRaises(ValueError, calendar.setfirstweekday, 200) orig = calendar.firstweekday() diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index 8fe487bb91d..f93fa555c5e 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -25,13 +25,11 @@ class ComparableException: def __str__(self): return str(self.err) - def __cmp__(self, anExc): + def __eq__(self, anExc): if not isinstance(anExc, Exception): - return -1 - x = cmp(self.err.__class__, anExc.__class__) - if x != 0: - return x - return cmp(self.err.args, anExc.args) + return NotImplemented + return (self.err.__class__ == anExc.__class__ and + self.err.args == anExc.args) def __getattr__(self, attr): return getattr(self.err, attr) @@ -118,10 +116,10 @@ parse_strict_test_cases = [ }) ] -def norm(list): - if type(list) == type([]): - list.sort() - return list +def norm(seq): + if isinstance(seq, list): + seq.sort(key=repr) + return seq def first_elts(list): return map(lambda x:x[0], list) diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index a0b3300c84d..66f426515ac 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -100,6 +100,30 @@ class AllTests: print "__cmp__:", args return 0 + def __eq__(self, *args): + print "__eq__:", args + return True + + def __ne__(self, *args): + print "__ne__:", args + return False + + def __lt__(self, *args): + print "__lt__:", args + return False + + def __le__(self, *args): + print "__le__:", args + return True + + def __gt__(self, *args): + print "__gt__:", args + return False + + def __ge__(self, *args): + print "__ge__:", args + return True + def __del__(self, *args): print "__del__:", args diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 7c8119471a5..8f38e3b3dbe 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -13,8 +13,8 @@ class Cmp: def __repr__(self): return '' % self.arg - def __cmp__(self, other): - return cmp(self.arg, other) + def __eq__(self, other): + return self.arg == other class ComparisonTest(unittest.TestCase): set1 = [2, 2.0, 2L, 2+0j, Cmp(2.0)] @@ -36,7 +36,7 @@ class ComparisonTest(unittest.TestCase): L.insert(len(L)//2, Empty()) for a in L: for b in L: - self.assertEqual(cmp(a, b), cmp(id(a), id(b)), + self.assertEqual(a == b, id(a) == id(b), 'a=%r, b=%r' % (a, b)) def test_main(): diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index ff4c987eba2..416a755dad0 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -104,8 +104,8 @@ class TestCopy(unittest.TestCase): class C: def __init__(self, foo): self.foo = foo - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C(42) self.assertEqual(copy.copy(x), x) @@ -115,8 +115,8 @@ class TestCopy(unittest.TestCase): self.foo = foo def __copy__(self): return C(self.foo) - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C(42) self.assertEqual(copy.copy(x), x) @@ -126,8 +126,8 @@ class TestCopy(unittest.TestCase): self.foo = foo def __getinitargs__(self): return (self.foo,) - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C(42) self.assertEqual(copy.copy(x), x) @@ -137,8 +137,8 @@ class TestCopy(unittest.TestCase): self.foo = foo def __getstate__(self): return {"foo": self.foo} - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C(42) self.assertEqual(copy.copy(x), x) @@ -148,8 +148,8 @@ class TestCopy(unittest.TestCase): self.foo = foo def __setstate__(self, state): self.foo = state["foo"] - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C(42) self.assertEqual(copy.copy(x), x) @@ -161,8 +161,8 @@ class TestCopy(unittest.TestCase): return self.foo def __setstate__(self, state): self.foo = state - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C(42) self.assertEqual(copy.copy(x), x) @@ -304,7 +304,7 @@ class TestCopy(unittest.TestCase): x = {} x['foo'] = x y = copy.deepcopy(x) - self.assertRaises(RuntimeError, cmp, y, x) + self.assertRaises(TypeError, cmp, y, x) self.assert_(y is not x) self.assert_(y['foo'] is y) self.assertEqual(len(y), 1) @@ -319,8 +319,8 @@ class TestCopy(unittest.TestCase): class C: def __init__(self, foo): self.foo = foo - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C([42]) y = copy.deepcopy(x) self.assertEqual(y, x) @@ -332,8 +332,8 @@ class TestCopy(unittest.TestCase): self.foo = foo def __deepcopy__(self, memo): return C(copy.deepcopy(self.foo, memo)) - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C([42]) y = copy.deepcopy(x) self.assertEqual(y, x) @@ -346,8 +346,8 @@ class TestCopy(unittest.TestCase): self.foo = foo def __getinitargs__(self): return (self.foo,) - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C([42]) y = copy.deepcopy(x) self.assertEqual(y, x) @@ -360,8 +360,8 @@ class TestCopy(unittest.TestCase): self.foo = foo def __getstate__(self): return {"foo": self.foo} - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C([42]) y = copy.deepcopy(x) self.assertEqual(y, x) @@ -374,8 +374,8 @@ class TestCopy(unittest.TestCase): self.foo = foo def __setstate__(self, state): self.foo = state["foo"] - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C([42]) y = copy.deepcopy(x) self.assertEqual(y, x) @@ -390,8 +390,8 @@ class TestCopy(unittest.TestCase): return self.foo def __setstate__(self, state): self.foo = state - def __cmp__(self, other): - return cmp(self.foo, other.foo) + def __eq__(self, other): + return self.foo == other.foo x = C([42]) y = copy.deepcopy(x) self.assertEqual(y, x) @@ -434,8 +434,8 @@ class TestCopy(unittest.TestCase): class C(object): def __reduce__(self): return (C, (), self.__dict__) - def __cmp__(self, other): - return cmp(self.__dict__, other.__dict__) + def __eq__(self, other): + return self.__dict__ == other.__dict__ x = C() x.foo = [42] y = copy.copy(x) @@ -450,8 +450,8 @@ class TestCopy(unittest.TestCase): return (C, (), self.__dict__) def __setstate__(self, state): self.__dict__.update(state) - def __cmp__(self, other): - return cmp(self.__dict__, other.__dict__) + def __eq__(self, other): + return self.__dict__ == other.__dict__ x = C() x.foo = [42] y = copy.copy(x) @@ -475,9 +475,9 @@ class TestCopy(unittest.TestCase): class C(list): def __reduce__(self): return (C, (), self.__dict__, iter(self)) - def __cmp__(self, other): - return (cmp(list(self), list(other)) or - cmp(self.__dict__, other.__dict__)) + def __eq__(self, other): + return (list(self) == list(other) and + self.__dict__ == other.__dict__) x = C([[1, 2], 3]) y = copy.copy(x) self.assertEqual(x, y) @@ -492,9 +492,9 @@ class TestCopy(unittest.TestCase): class C(dict): def __reduce__(self): return (C, (), self.__dict__, None, self.iteritems()) - def __cmp__(self, other): - return (cmp(dict(self), list(dict)) or - cmp(self.__dict__, other.__dict__)) + def __eq__(self, other): + return (dict(self) == dict(other) and + self.__dict__ == other.__dict__) x = C([("foo", [1, 2]), ("bar", 3)]) y = copy.copy(x) self.assertEqual(x, y) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 40c44661e06..5fef4eb5cce 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -276,8 +276,8 @@ class DecimalTest(unittest.TestCase): myexceptions = self.getexceptions() self.context.clear_flags() - myexceptions.sort() - theirexceptions.sort() + myexceptions.sort(key=repr) + theirexceptions.sort(key=repr) self.assertEqual(result, ans, 'Incorrect answer for ' + s + ' -- got ' + result) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 00800a7dc25..3fb01e73c08 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -151,7 +151,7 @@ def lists(): def dicts(): if verbose: print "Testing dict operations..." - testbinop({1:2}, {2:1}, -1, "cmp(a,b)", "__cmp__") + ##testbinop({1:2}, {2:1}, -1, "cmp(a,b)", "__cmp__") testbinop({1:2,3:4}, 1, 1, "b in a", "__contains__") testbinop({1:2,3:4}, 2, 0, "b in a", "__contains__") testbinop({1:2,3:4}, 1, 2, "a[b]", "__getitem__") @@ -523,7 +523,7 @@ def spamdicts(): # This is an ugly hack: copy._deepcopy_dispatch[spam.spamdict] = spamdict - testbinop(spamdict({1:2}), spamdict({2:1}), -1, "cmp(a,b)", "__cmp__") + ##testbinop(spamdict({1:2}), spamdict({2:1}), -1, "cmp(a,b)", "__cmp__") testbinop(spamdict({1:2,3:4}), 1, 1, "b in a", "__contains__") testbinop(spamdict({1:2,3:4}), 2, 0, "b in a", "__contains__") testbinop(spamdict({1:2,3:4}), 1, 2, "a[b]", "__getitem__") @@ -1641,7 +1641,7 @@ def specials(): verify(id(c1) != id(c2)) hash(c1) hash(c2) - vereq(cmp(c1, c2), cmp(id(c1), id(c2))) + ##vereq(cmp(c1, c2), cmp(id(c1), id(c2))) vereq(c1, c1) verify(c1 != c2) verify(not c1 != c1) @@ -1665,7 +1665,7 @@ def specials(): verify(id(d1) != id(d2)) hash(d1) hash(d2) - vereq(cmp(d1, d2), cmp(id(d1), id(d2))) + ##vereq(cmp(d1, d2), cmp(id(d1), id(d2))) vereq(d1, d1) verify(d1 != d2) verify(not d1 != d1) @@ -1758,21 +1758,21 @@ def specials(): for i in range(10): verify(i in p10) verify(10 not in p10) - # Safety test for __cmp__ - def unsafecmp(a, b): - try: - a.__class__.__cmp__(a, b) - except TypeError: - pass - else: - raise TestFailed, "shouldn't allow %s.__cmp__(%r, %r)" % ( - a.__class__, a, b) - unsafecmp(u"123", "123") - unsafecmp("123", u"123") - unsafecmp(1, 1.0) - unsafecmp(1.0, 1) - unsafecmp(1, 1L) - unsafecmp(1L, 1) +## # Safety test for __cmp__ +## def unsafecmp(a, b): +## try: +## a.__class__.__cmp__(a, b) +## except TypeError: +## pass +## else: +## raise TestFailed, "shouldn't allow %s.__cmp__(%r, %r)" % ( +## a.__class__, a, b) +## unsafecmp(u"123", "123") +## unsafecmp("123", u"123") +## unsafecmp(1, 1.0) +## unsafecmp(1.0, 1) +## unsafecmp(1, 1L) +## unsafecmp(1L, 1) class Letter(str): def __new__(cls, letter): @@ -2469,12 +2469,43 @@ def classic_comparisons(): class C(base): def __init__(self, value): self.value = int(value) - def __cmp__(self, other): + def __eq__(self, other): if isinstance(other, C): - return cmp(self.value, other.value) + return self.value == other.value if isinstance(other, int) or isinstance(other, long): - return cmp(self.value, other) + return self.value == other return NotImplemented + def __ne__(self, other): + if isinstance(other, C): + return self.value != other.value + if isinstance(other, int) or isinstance(other, long): + return self.value != other + return NotImplemented + def __lt__(self, other): + if isinstance(other, C): + return self.value < other.value + if isinstance(other, int) or isinstance(other, long): + return self.value < other + return NotImplemented + def __le__(self, other): + if isinstance(other, C): + return self.value <= other.value + if isinstance(other, int) or isinstance(other, long): + return self.value <= other + return NotImplemented + def __gt__(self, other): + if isinstance(other, C): + return self.value > other.value + if isinstance(other, int) or isinstance(other, long): + return self.value > other + return NotImplemented + def __ge__(self, other): + if isinstance(other, C): + return self.value >= other.value + if isinstance(other, int) or isinstance(other, long): + return self.value >= other + return NotImplemented + c1 = C(1) c2 = C(2) c3 = C(3) @@ -2482,12 +2513,12 @@ def classic_comparisons(): c = {1: c1, 2: c2, 3: c3} for x in 1, 2, 3: for y in 1, 2, 3: - verify(cmp(c[x], c[y]) == cmp(x, y), "x=%d, y=%d" % (x, y)) + ##verify(cmp(c[x], c[y]) == cmp(x, y), "x=%d, y=%d" % (x, y)) for op in "<", "<=", "==", "!=", ">", ">=": verify(eval("c[x] %s c[y]" % op) == eval("x %s y" % op), "x=%d, y=%d" % (x, y)) - verify(cmp(c[x], y) == cmp(x, y), "x=%d, y=%d" % (x, y)) - verify(cmp(x, c[y]) == cmp(x, y), "x=%d, y=%d" % (x, y)) + ##verify(cmp(c[x], y) == cmp(x, y), "x=%d, y=%d" % (x, y)) + ##verify(cmp(x, c[y]) == cmp(x, y), "x=%d, y=%d" % (x, y)) def rich_comparisons(): if verbose: @@ -3875,6 +3906,8 @@ def methodwrapper(): if verbose: print "Testing method-wrapper objects..." + return # XXX should methods really support __eq__? + l = [] vereq(l.__add__, l.__add__) vereq(l.__add__, [].__add__) diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py index 6a5310c657b..28d7d13f6cf 100644 --- a/Lib/test/test_descrtut.py +++ b/Lib/test/test_descrtut.py @@ -65,14 +65,11 @@ We can also use the new type in contexts where classic only allows "real" dictionaries, such as the locals/globals dictionaries for the exec statement or the built-in function eval(): - >>> def sorted(seq): - ... seq.sort() - ... return seq >>> print sorted(a.keys()) [1, 2] >>> exec "x = 3; print x" in a 3 - >>> print sorted(a.keys()) + >>> print sorted(a.keys(), key=lambda x: (str(type(x)), x)) [1, 2, '__builtins__', 'x'] >>> print a['x'] 3 diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 7295f41b786..717ed5e3f3e 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -5,6 +5,7 @@ import sys, UserDict, cStringIO class DictTest(unittest.TestCase): + def test_constructor(self): # calling built-in types without argument must return empty self.assertEqual(dict(), {}) @@ -368,9 +369,9 @@ class DictTest(unittest.TestCase): d = {1: BadRepr()} self.assertRaises(Exc, repr, d) - def test_le(self): - self.assert_(not ({} < {})) - self.assert_(not ({1: 2} < {1L: 2L})) + def test_eq(self): + self.assertEqual({}, {}) + self.assertEqual({1: 2}, {1L: 2L}) class Exc(Exception): pass @@ -378,12 +379,12 @@ class DictTest(unittest.TestCase): def __eq__(self, other): raise Exc() def __hash__(self): - return 42 + return 1 d1 = {BadCmp(): 1} d2 = {1: 1} try: - d1 < d2 + d1 == d2 except Exc: pass else: diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 296dc9ba585..458bfa2a163 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -681,7 +681,7 @@ d[1,] = 2 d[1,2] = 3 d[1,2,3] = 4 L = list(d) -L.sort() +L.sort(key=lambda x: x if isinstance(x, tuple) else ()) print L @@ -741,7 +741,7 @@ print [(i, s) for i in nums for s in [f for f in strs if "n" in f]] print [(lambda a:[a**i for i in range(a+1)])(j) for j in range(5)] def test_in_func(l): - return [None < x < 3 for x in l if x > 2] + return [0 < x < 3 for x in l if x > 2] print test_in_func(nums) diff --git a/Lib/test/test_heapq.py b/Lib/test/test_heapq.py index 2da4f8c49ac..191644986b3 100644 --- a/Lib/test/test_heapq.py +++ b/Lib/test/test_heapq.py @@ -136,6 +136,7 @@ class CmpErr: "Dummy element that always raises an error during comparison" def __cmp__(self, other): raise ZeroDivisionError + __eq__ = __ne__ = __lt__ = __le__ = __gt__ = __ge__ = __cmp__ def R(seqn): 'Regular generator' @@ -253,7 +254,7 @@ class TestErrorHandling(unittest.TestCase): def test_iterable_args(self): for f in (nlargest, nsmallest): - for s in ("123", "", range(1000), ('do', 1.2), xrange(2000,2200,5)): + for s in ("123", "", range(1000), (1, 1.2), xrange(2000,2200,5)): for g in (G, I, Ig, L, R): self.assertEqual(f(2, g(s)), f(2,s)) self.assertEqual(f(2, S(s)), []) diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index b2a9b55655a..29abd4bbc09 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -132,13 +132,13 @@ class TestBasicOps(unittest.TestCase): # __cmp__ failure class DummyCmp: - def __cmp__(self, dst): + def __eq__(self, dst): raise ExpectedError s = [DummyCmp(), DummyCmp(), None] - # __cmp__ failure on outer object + # __eq__ failure on outer object self.assertRaises(ExpectedError, gulp, s, func=id) - # __cmp__ failure on inner object + # __eq__ failure on inner object self.assertRaises(ExpectedError, gulp, s) # keyfunc failure diff --git a/Lib/test/test_mutants.py b/Lib/test/test_mutants.py index df58944d884..39ea806b6d3 100644 --- a/Lib/test/test_mutants.py +++ b/Lib/test/test_mutants.py @@ -27,7 +27,7 @@ import os # ran it. Indeed, at the start, the driver never got beyond 6 iterations # before the test died. -# The dicts are global to make it easy to mutate tham from within functions. +# The dicts are global to make it easy to mutate them from within functions. dict1 = {} dict2 = {} @@ -93,9 +93,9 @@ class Horrid: def __hash__(self): return self.hashcode - def __cmp__(self, other): + def __eq__(self, other): maybe_mutate() # The point of the test. - return cmp(self.i, other.i) + return self.i == other.i def __repr__(self): return "Horrid(%d)" % self.i @@ -132,7 +132,8 @@ def test_one(n): while dict1 and len(dict1) == len(dict2): if verbose: print ".", - c = cmp(dict1, dict2) + c = dict1 == dict2 + XXX # Can't figure out how to make this work if verbose: print diff --git a/Lib/test/test_operations.py b/Lib/test/test_operations.py index 0b558de4d6e..67e77aad278 100644 --- a/Lib/test/test_operations.py +++ b/Lib/test/test_operations.py @@ -12,7 +12,7 @@ class BadDictKey: def __hash__(self): return hash(self.__class__) - def __cmp__(self, other): + def __eq__(self, other): if isinstance(other, self.__class__): print "raising error" raise RuntimeError, "gotcha" @@ -34,7 +34,7 @@ for stmt in ['d[x2] = 2', except RuntimeError: print "%s: caught the RuntimeError outside" % (stmt,) else: - print "%s: No exception passed through!" # old CPython behavior + print "%s: No exception passed through!" % (stmt,) # old CPython behavior # Dict resizing bug, found by Jack Jansen in 2.2 CVS development. diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 0268be272d8..23926bef727 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -18,7 +18,7 @@ def check_pass_thru(): class BadCmp: def __hash__(self): return 1 - def __cmp__(self, other): + def __eq__(self, other): raise RuntimeError class TestJointOps(unittest.TestCase): @@ -781,11 +781,8 @@ class TestBinaryOps(unittest.TestCase): a, b = set('a'), set('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 True, - # which Python thinks is good enough to synthesize a cmp() result - # without calling __cmp__. - self.assertEqual(cmp(a, a), 0) + # In py3k, this works! + self.assertRaises(TypeError, cmp, a, a) self.assertRaises(TypeError, cmp, a, 12) self.assertRaises(TypeError, cmp, "abc", a) @@ -1201,8 +1198,8 @@ class TestCopying(unittest.TestCase): def test_copy(self): dup = self.set.copy() - dup_list = list(dup); dup_list.sort() - set_list = list(self.set); set_list.sort() + dup_list = sorted(dup, key=repr) + set_list = sorted(self.set, key=repr) self.assertEqual(len(dup_list), len(set_list)) for i in range(len(dup_list)): self.failUnless(dup_list[i] is set_list[i]) @@ -1210,8 +1207,8 @@ class TestCopying(unittest.TestCase): def test_deep_copy(self): dup = copy.deepcopy(self.set) ##print type(dup), repr(dup) - dup_list = list(dup); dup_list.sort() - set_list = list(self.set); set_list.sort() + dup_list = sorted(dup, key=repr) + set_list = sorted(self.set, key=repr) self.assertEqual(len(dup_list), len(set_list)) for i in range(len(dup_list)): self.assertEqual(dup_list[i], set_list[i]) @@ -1374,7 +1371,7 @@ class TestVariousIteratorArgs(unittest.TestCase): for cons in (set, frozenset): for s in ("123", "", range(1000), ('do', 1.2), xrange(2000,2200,5)): for g in (G, I, Ig, S, L, R): - self.assertEqual(sorted(cons(g(s))), sorted(g(s))) + self.assertEqual(sorted(cons(g(s)), key=repr), sorted(g(s), key=repr)) self.assertRaises(TypeError, cons , X(s)) self.assertRaises(TypeError, cons , N(s)) self.assertRaises(ZeroDivisionError, cons , E(s)) @@ -1386,7 +1383,7 @@ class TestVariousIteratorArgs(unittest.TestCase): for g in (G, I, Ig, L, R): expected = meth(data) actual = meth(G(data)) - self.assertEqual(sorted(actual), sorted(expected)) + self.assertEqual(sorted(actual, key=repr), sorted(expected, key=repr)) self.assertRaises(TypeError, meth, X(s)) self.assertRaises(TypeError, meth, N(s)) self.assertRaises(ZeroDivisionError, meth, E(s)) @@ -1400,7 +1397,7 @@ class TestVariousIteratorArgs(unittest.TestCase): t = s.copy() getattr(s, methname)(list(g(data))) getattr(t, methname)(g(data)) - self.assertEqual(sorted(s), sorted(t)) + self.assertEqual(sorted(s, key=repr), sorted(t, key=repr)) self.assertRaises(TypeError, getattr(set('january'), methname), X(data)) self.assertRaises(TypeError, getattr(set('january'), methname), N(data)) diff --git a/Lib/test/test_sets.py b/Lib/test/test_sets.py index 85e4a22e840..88cfcacdf9e 100644 --- a/Lib/test/test_sets.py +++ b/Lib/test/test_sets.py @@ -234,11 +234,8 @@ class TestBinaryOps(unittest.TestCase): a, b = Set('a'), Set('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 True, - # which Python thinks is good enough to synthesize a cmp() result - # without calling __cmp__. - self.assertEqual(cmp(a, a), 0) + # In py3k, this works! + self.assertRaises(TypeError, cmp, a, a) self.assertRaises(TypeError, cmp, a, 12) self.assertRaises(TypeError, cmp, "abc", a) @@ -675,8 +672,8 @@ class TestCopying(unittest.TestCase): def test_copy(self): dup = self.set.copy() - dup_list = list(dup); dup_list.sort() - set_list = list(self.set); set_list.sort() + dup_list = sorted(dup, key=repr) + set_list = sorted(self.set, key=repr) self.assertEqual(len(dup_list), len(set_list)) for i in range(len(dup_list)): self.failUnless(dup_list[i] is set_list[i]) @@ -684,8 +681,8 @@ class TestCopying(unittest.TestCase): def test_deep_copy(self): dup = copy.deepcopy(self.set) ##print type(dup), repr(dup) - dup_list = list(dup); dup_list.sort() - set_list = list(self.set); set_list.sort() + dup_list = sorted(dup, key=repr) + set_list = sorted(self.set, key=repr) self.assertEqual(len(dup_list), len(set_list)) for i in range(len(dup_list)): self.assertEqual(dup_list[i], set_list[i]) diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index d8eb88205c7..bd6e6216d4a 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -35,18 +35,18 @@ class SliceTest(unittest.TestCase): s1 = slice(BadCmp()) s2 = slice(BadCmp()) - self.assertRaises(Exc, cmp, s1, s2) self.assertEqual(s1, s1) + self.assertRaises(Exc, lambda: s1 == s2) s1 = slice(1, BadCmp()) s2 = slice(1, BadCmp()) self.assertEqual(s1, s1) - self.assertRaises(Exc, cmp, s1, s2) + self.assertRaises(Exc, lambda: s1 == s2) s1 = slice(1, 2, BadCmp()) s2 = slice(1, 2, BadCmp()) self.assertEqual(s1, s1) - self.assertRaises(Exc, cmp, s1, s2) + self.assertRaises(Exc, lambda: s1 == s2) def test_members(self): s = slice(1) diff --git a/Lib/test/test_sort.py b/Lib/test/test_sort.py index 84c92cc9704..031780adb89 100644 --- a/Lib/test/test_sort.py +++ b/Lib/test/test_sort.py @@ -68,8 +68,8 @@ class TestBase(unittest.TestCase): self.key = key self.index = i - def __cmp__(self, other): - return cmp(self.key, other.key) + def __lt__(self, other): + return self.key < other.key def __repr__(self): return "Stable(%d, %d)" % (self.key, self.index) @@ -225,6 +225,8 @@ class TestDecorateSortUndecorate(unittest.TestCase): def __del__(self): del data[:] data[:] = range(20) + def __lt__(self, other): + return id(self) < id(other) self.assertRaises(ValueError, data.sort, key=SortKiller) def test_key_with_mutating_del_and_exception(self): diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 03971185cf3..d260fc5ca6c 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -122,8 +122,8 @@ def fcmp(x, y): # fuzzy comparison function outcome = fcmp(x[i], y[i]) if outcome != 0: return outcome - return cmp(len(x), len(y)) - return cmp(x, y) + return (len(x) > len(y)) - (len(x) < len(y)) + return (x > y) - (x < y) try: unicode diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 2d299c36f29..f0bdfdebf72 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -233,7 +233,6 @@ print 'Buffers' try: buffer('asdf', -1) except ValueError: pass else: raise TestFailed, "buffer('asdf', -1) should raise ValueError" -cmp(buffer("abc"), buffer("def")) # used to raise a warning: tp_compare didn't return -1, 0, or 1 try: buffer(None) except TypeError: pass diff --git a/Lib/test/test_userdict.py b/Lib/test/test_userdict.py index ecb33d1e652..bc45bf3e9bb 100644 --- a/Lib/test/test_userdict.py +++ b/Lib/test/test_userdict.py @@ -52,7 +52,7 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol): all = [d0, d1, d2, u, u0, u1, u2, uu, uu0, uu1, uu2] for a in all: for b in all: - self.assertEqual(cmp(a, b), cmp(len(a), len(b))) + self.assertEqual(a == b, len(a) == len(b)) # Test __getitem__ self.assertEqual(u2["one"], 1) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index a4fb7f333cf..f5114b26ce0 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -701,6 +701,12 @@ class Object: self.arg = arg def __repr__(self): return "" % self.arg + def __lt__(self, other): + if isinstance(other, Object): + return self.arg < other.arg + return NotImplemented + def __hash__(self): + return hash(self.arg) class MappingTestCase(TestBase): diff --git a/Lib/uuid.py b/Lib/uuid.py index ae3da25ca55..5bf5c35a09e 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -177,9 +177,36 @@ class UUID(object): int |= version << 76L self.__dict__['int'] = int - def __cmp__(self, other): + def __eq__(self, other): if isinstance(other, UUID): - return cmp(self.int, other.int) + return self.int == other.int + return NotImplemented + + def __ne__(self, other): + if isinstance(other, UUID): + return self.int != other.int + return NotImplemented + + # XXX What's the value of being able to sort UUIDs? + + def __lt__(self, other): + if isinstance(other, UUID): + return self.int < other.int + return NotImplemented + + def __gt__(self, other): + if isinstance(other, UUID): + return self.int > other.int + return NotImplemented + + def __le__(self, other): + if isinstance(other, UUID): + return self.int <= other.int + return NotImplemented + + def __ge__(self, other): + if isinstance(other, UUID): + return self.int >= other.int return NotImplemented def __hash__(self): @@ -488,7 +515,7 @@ def uuid1(node=None, clock_seq=None): # 0x01b21dd213814000 is the number of 100-ns intervals between the # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. timestamp = int(nanoseconds/100) + 0x01b21dd213814000L - if timestamp <= _last_timestamp: + if _last_timestamp is not None and timestamp <= _last_timestamp: timestamp = _last_timestamp + 1 _last_timestamp = timestamp if clock_seq is None: diff --git a/Lib/xmlrpclib.py b/Lib/xmlrpclib.py index d1bd2d81c68..72866f1cf81 100644 --- a/Lib/xmlrpclib.py +++ b/Lib/xmlrpclib.py @@ -282,56 +282,9 @@ class Fault(Error): # @param value A boolean value. Any true value is interpreted as True, # all other values are interpreted as False. -if _bool_is_builtin: - boolean = Boolean = bool - # to avoid breaking code which references xmlrpclib.{True,False} - True, False = True, False -else: - class Boolean: - """Boolean-value wrapper. - - Use True or False to generate a "boolean" XML-RPC value. - """ - - def __init__(self, value = 0): - self.value = operator.truth(value) - - def encode(self, out): - out.write("%d\n" % self.value) - - def __cmp__(self, other): - if isinstance(other, Boolean): - other = other.value - return cmp(self.value, other) - - def __repr__(self): - if self.value: - return "" % id(self) - else: - return "" % id(self) - - def __int__(self): - return self.value - - def __nonzero__(self): - return self.value - - True, False = Boolean(1), Boolean(0) - - ## - # Map true or false value to XML-RPC boolean values. - # - # @def boolean(value) - # @param value A boolean value. Any true value is mapped to True, - # all other values are mapped to False. - # @return xmlrpclib.True or xmlrpclib.False. - # @see Boolean - # @see True - # @see False - - def boolean(value, _truefalse=(False, True)): - """Convert any Python value to XML-RPC 'boolean'.""" - return _truefalse[operator.truth(value)] +boolean = Boolean = bool +# to avoid breaking code which references xmlrpclib.{True,False} +True, False = True, False ## # Wrapper for XML-RPC DateTime values. This converts a time value to @@ -371,10 +324,15 @@ class DateTime: value = time.strftime("%Y%m%dT%H:%M:%S", value) self.value = value - def __cmp__(self, other): + def __eq__(self, other): if isinstance(other, DateTime): other = other.value - return cmp(self.value, other) + return self.value == other + + def __ne__(self, other): + if isinstance(other, DateTime): + other = other.value + return self.value != other ## # Get date/time value. @@ -432,10 +390,15 @@ class Binary: def __str__(self): return self.data or "" - def __cmp__(self, other): + def __eq__(self, other): if isinstance(other, Binary): other = other.data - return cmp(self.data, other) + return self.data == other + + def __ne__(self, other): + if isinstance(other, Binary): + other = other.data + return self.data != other def decode(self, data): self.data = base64.decodestring(data) diff --git a/Modules/threadmodule.c b/Modules/threadmodule.c index 448b11cd67a..361429668a6 100644 --- a/Modules/threadmodule.c +++ b/Modules/threadmodule.c @@ -696,6 +696,8 @@ initthread(void) /* Initialize types: */ if (PyType_Ready(&localtype) < 0) return; + if (PyType_Ready(&Locktype) < 0) + return; /* Create the module and add the functions */ m = Py_InitModule3("thread", thread_methods, thread_doc); diff --git a/Objects/bufferobject.c b/Objects/bufferobject.c index 3a0e3d5589c..ddef8685193 100644 --- a/Objects/bufferobject.c +++ b/Objects/bufferobject.c @@ -252,23 +252,65 @@ buffer_dealloc(PyBufferObject *self) } static int -buffer_compare(PyBufferObject *self, PyBufferObject *other) +get_bufx(PyObject *obj, void **ptr, Py_ssize_t *size) +{ + PyBufferProcs *bp; + + if (PyBuffer_Check(obj)) { + if (!get_buf((PyBufferObject *)obj, ptr, size, ANY_BUFFER)) { + PyErr_Clear(); + return 0; + } + else + return 1; + } + bp = obj->ob_type->tp_as_buffer; + if (bp == NULL || + bp->bf_getreadbuffer == NULL || + bp->bf_getsegcount == NULL) + return 0; + if ((*bp->bf_getsegcount)(obj, NULL) != 1) + return 0; + *size = (*bp->bf_getreadbuffer)(obj, 0, ptr); + if (*size < 0) { + PyErr_Clear(); + return 0; + } + return 1; +} + +static PyObject * +buffer_richcompare(PyObject *self, PyObject *other, int op) { void *p1, *p2; - Py_ssize_t len_self, len_other, min_len; - int cmp; + Py_ssize_t len1, len2, min_len; + int cmp, ok; - if (!get_buf(self, &p1, &len_self, ANY_BUFFER)) - return -1; - if (!get_buf(other, &p2, &len_other, ANY_BUFFER)) - return -1; - min_len = (len_self < len_other) ? len_self : len_other; - if (min_len > 0) { - cmp = memcmp(p1, p2, min_len); - if (cmp != 0) - return cmp < 0 ? -1 : 1; + ok = 1; + if (!get_bufx(self, &p1, &len1)) + ok = 0; + if (!get_bufx(other, &p2, &len2)) + ok = 0; + if (!ok) { + /* If we can't get the buffers, + == and != are still defined + (and the objects are unequal) */ + PyObject *result; + if (op == Py_EQ) + result = Py_False; + else if (op == Py_NE) + result = Py_True; + else + result = Py_NotImplemented; + Py_INCREF(result); + return result; } - return (len_self < len_other) ? -1 : (len_self > len_other) ? 1 : 0; + min_len = (len1 < len2) ? len1 : len2; + cmp = memcmp(p1, p2, min_len); + if (cmp == 0) + cmp = (len1 < len2) ? -1 : + (len1 > len2) ? 1 : 0; + return Py_CmpToRich(op, cmp); } static PyObject * @@ -667,7 +709,7 @@ PyTypeObject PyBuffer_Type = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)buffer_compare, /* tp_compare */ + 0, /* tp_compare */ (reprfunc)buffer_repr, /* tp_repr */ 0, /* tp_as_number */ &buffer_as_sequence, /* tp_as_sequence */ @@ -682,7 +724,7 @@ PyTypeObject PyBuffer_Type = { buffer_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + buffer_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/Objects/cellobject.c b/Objects/cellobject.c index 65a29aaca83..74fa247fd12 100644 --- a/Objects/cellobject.c +++ b/Objects/cellobject.c @@ -49,18 +49,6 @@ cell_dealloc(PyCellObject *op) PyObject_GC_Del(op); } -static int -cell_compare(PyCellObject *a, PyCellObject *b) -{ - if (a->ob_ref == NULL) { - if (b->ob_ref == NULL) - return 0; - return -1; - } else if (b->ob_ref == NULL) - return 1; - return PyObject_Compare(a->ob_ref, b->ob_ref); -} - static PyObject * cell_repr(PyCellObject *op) { @@ -108,7 +96,7 @@ PyTypeObject PyCell_Type = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)cell_compare, /* tp_compare */ + 0, /* tp_compare */ (reprfunc)cell_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ diff --git a/Objects/classobject.c b/Objects/classobject.c index cc09960d0e2..11079776f36 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -80,7 +80,7 @@ PyMethod_New(PyObject *func, PyObject *self, PyObject *klass) #define OFF(x) offsetof(PyMethodObject, x) -static PyMemberDef instancemethod_memberlist[] = { +static PyMemberDef method_memberlist[] = { {"im_class", T_OBJECT, OFF(im_class), READONLY|RESTRICTED, "the class associated with a method"}, {"im_func", T_OBJECT, OFF(im_func), READONLY|RESTRICTED, @@ -96,7 +96,7 @@ static PyMemberDef instancemethod_memberlist[] = { should only be used for the class, not for instances */ static PyObject * -instancemethod_get_doc(PyMethodObject *im, void *context) +method_get_doc(PyMethodObject *im, void *context) { static PyObject *docstr; if (docstr == NULL) { @@ -107,13 +107,13 @@ instancemethod_get_doc(PyMethodObject *im, void *context) return PyObject_GetAttr(im->im_func, docstr); } -static PyGetSetDef instancemethod_getset[] = { - {"__doc__", (getter)instancemethod_get_doc, NULL, NULL}, +static PyGetSetDef method_getset[] = { + {"__doc__", (getter)method_get_doc, NULL, NULL}, {0} }; static PyObject * -instancemethod_getattro(PyObject *obj, PyObject *name) +method_getattro(PyObject *obj, PyObject *name) { PyMethodObject *im = (PyMethodObject *)obj; PyTypeObject *tp = obj->ob_type; @@ -140,19 +140,19 @@ instancemethod_getattro(PyObject *obj, PyObject *name) return PyObject_GetAttr(im->im_func, name); } -PyDoc_STRVAR(instancemethod_doc, -"instancemethod(function, instance, class)\n\ +PyDoc_STRVAR(method_doc, +"method(function, instance, class)\n\ \n\ Create an instance method object."); static PyObject * -instancemethod_new(PyTypeObject* type, PyObject* args, PyObject *kw) +method_new(PyTypeObject* type, PyObject* args, PyObject *kw) { PyObject *func; PyObject *self; PyObject *classObj = NULL; - if (!PyArg_UnpackTuple(args, "instancemethod", 2, 3, + if (!PyArg_UnpackTuple(args, "method", 2, 3, &func, &self, &classObj)) return NULL; if (!PyCallable_Check(func)) { @@ -172,7 +172,7 @@ instancemethod_new(PyTypeObject* type, PyObject* args, PyObject *kw) } static void -instancemethod_dealloc(register PyMethodObject *im) +method_dealloc(register PyMethodObject *im) { _PyObject_GC_UNTRACK(im); if (im->im_weakreflist != NULL) @@ -184,24 +184,42 @@ instancemethod_dealloc(register PyMethodObject *im) free_list = im; } -static int -instancemethod_compare(PyMethodObject *a, PyMethodObject *b) +static PyObject * +method_richcompare(PyObject *self, PyObject *other, int op) { - int cmp; - cmp = PyObject_Compare(a->im_func, b->im_func); - if (cmp) - return cmp; + PyMethodObject *a, *b; + PyObject *res; + int eq; - if (a->im_self == b->im_self) - return 0; - if (a->im_self == NULL || b->im_self == NULL) - return (a->im_self < b->im_self) ? -1 : 1; + if ((op != Py_EQ && op != Py_NE) || + !PyMethod_Check(self) || + !PyMethod_Check(other)) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + a = (PyMethodObject *)self; + b = (PyMethodObject *)other; + eq = PyObject_RichCompareBool(a->im_func, b->im_func, Py_EQ); + if (eq == 1) { + if (a->im_self == NULL || b->im_self == NULL) + eq = a->im_self == b->im_self; + else + eq = PyObject_RichCompareBool(a->im_self, b->im_self, + Py_EQ); + } + if (eq < 0) + return NULL; + if (op == Py_EQ) + res = eq ? Py_True : Py_False; else - return PyObject_Compare(a->im_self, b->im_self); + res = eq ? Py_False : Py_True; + Py_INCREF(res); + return res; } static PyObject * -instancemethod_repr(PyMethodObject *a) +method_repr(PyMethodObject *a) { PyObject *self = a->im_self; PyObject *func = a->im_func; @@ -261,7 +279,7 @@ instancemethod_repr(PyMethodObject *a) } static long -instancemethod_hash(PyMethodObject *a) +method_hash(PyMethodObject *a) { long x, y; if (a->im_self == NULL) @@ -280,7 +298,7 @@ instancemethod_hash(PyMethodObject *a) } static int -instancemethod_traverse(PyMethodObject *im, visitproc visit, void *arg) +method_traverse(PyMethodObject *im, visitproc visit, void *arg) { Py_VISIT(im->im_func); Py_VISIT(im->im_self); @@ -333,7 +351,7 @@ getinstclassname(PyObject *inst, char *buf, int bufsize) } static PyObject * -instancemethod_call(PyObject *func, PyObject *arg, PyObject *kw) +method_call(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *self = PyMethod_GET_SELF(func); PyObject *klass = PyMethod_GET_CLASS(func); @@ -392,7 +410,7 @@ instancemethod_call(PyObject *func, PyObject *arg, PyObject *kw) } static PyObject * -instancemethod_descr_get(PyObject *meth, PyObject *obj, PyObject *cls) +method_descr_get(PyObject *meth, PyObject *obj, PyObject *cls) { /* Don't rebind an already bound method, or an unbound method of a class that's not a base class of cls. */ @@ -420,43 +438,43 @@ instancemethod_descr_get(PyObject *meth, PyObject *obj, PyObject *cls) PyTypeObject PyMethod_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, - "instancemethod", + "method", sizeof(PyMethodObject), 0, - (destructor)instancemethod_dealloc, /* tp_dealloc */ + (destructor)method_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)instancemethod_compare, /* tp_compare */ - (reprfunc)instancemethod_repr, /* tp_repr */ + 0, /* tp_compare */ + (reprfunc)method_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - (hashfunc)instancemethod_hash, /* tp_hash */ - instancemethod_call, /* tp_call */ + (hashfunc)method_hash, /* tp_hash */ + method_call, /* tp_call */ 0, /* tp_str */ - instancemethod_getattro, /* tp_getattro */ + method_getattro, /* tp_getattro */ PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - instancemethod_doc, /* tp_doc */ - (traverseproc)instancemethod_traverse, /* tp_traverse */ + method_doc, /* tp_doc */ + (traverseproc)method_traverse, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + method_richcompare, /* tp_richcompare */ offsetof(PyMethodObject, im_weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ - instancemethod_memberlist, /* tp_members */ - instancemethod_getset, /* tp_getset */ + method_memberlist, /* tp_members */ + method_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ - instancemethod_descr_get, /* tp_descr_get */ + method_descr_get, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ - instancemethod_new, /* tp_new */ + method_new, /* tp_new */ }; /* Clear out the free list */ diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 89871d6cf12..19dcc47f646 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -291,9 +291,15 @@ code_repr(PyCodeObject *co) return PyString_FromString(buf); } -static int -code_compare(PyCodeObject *co, PyCodeObject *cp) +static PyObject * +code_richcompare(PyObject *self, PyObject *other, int op) { + /* Temporarily make this unsupported */ + _Py_Break(); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + +#if 0 int cmp; cmp = PyObject_Compare(co->co_name, cp->co_name); if (cmp) return cmp; @@ -325,6 +331,7 @@ code_compare(PyCodeObject *co, PyCodeObject *cp) return -1; else return 0; +#endif } static long @@ -363,12 +370,12 @@ PyTypeObject PyCode_Type = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)code_compare, /* tp_compare */ + 0, /* tp_compare */ (reprfunc)code_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - (hashfunc)code_hash, /* tp_hash */ + 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ diff --git a/Objects/dictobject.c b/Objects/dictobject.c index b3fdbf1a0b6..320befbbc45 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -582,6 +582,36 @@ PyDict_GetItem(PyObject *op, PyObject *key) return ep->me_value; } +/* Variant of PyDict_GetItem() that doesn't suppress exceptions. + This returns NULL *with* an exception set if an exception occurred. + It returns NULL *without* an exception set if the key wasn't present. +*/ +PyObject * +PyDict_GetItemWithError(PyObject *op, PyObject *key) +{ + long hash; + dictobject *mp = (dictobject *)op; + dictentry *ep; + + if (!PyDict_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) + { + hash = PyObject_Hash(key); + if (hash == -1) { + return NULL; + } + } + + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL) + return NULL; + return ep->me_value; +} + /* CAUTION: PyDict_SetItem() must guarantee that it won't resize the * dictionary if it's merely replacing the value for an existing key. * This means that it's safe to loop over a dictionary with PyDict_Next() @@ -1432,136 +1462,6 @@ PyDict_Items(PyObject *mp) return dict_items((dictobject *)mp); } -/* Subroutine which returns the smallest key in a for which b's value - is different or absent. The value is returned too, through the - pval argument. Both are NULL if no key in a is found for which b's status - differs. The refcounts on (and only on) non-NULL *pval and function return - values must be decremented by the caller (characterize() increments them - to ensure that mutating comparison and PyDict_GetItem calls can't delete - them before the caller is done looking at them). */ - -static PyObject * -characterize(dictobject *a, dictobject *b, PyObject **pval) -{ - PyObject *akey = NULL; /* smallest key in a s.t. a[akey] != b[akey] */ - PyObject *aval = NULL; /* a[akey] */ - Py_ssize_t i; - int cmp; - - for (i = 0; i <= a->ma_mask; i++) { - PyObject *thiskey, *thisaval, *thisbval; - if (a->ma_table[i].me_value == NULL) - continue; - thiskey = a->ma_table[i].me_key; - Py_INCREF(thiskey); /* keep alive across compares */ - if (akey != NULL) { - cmp = PyObject_RichCompareBool(akey, thiskey, Py_LT); - if (cmp < 0) { - Py_DECREF(thiskey); - goto Fail; - } - if (cmp > 0 || - i > a->ma_mask || - a->ma_table[i].me_value == NULL) - { - /* Not the *smallest* a key; or maybe it is - * but the compare shrunk the dict so we can't - * find its associated value anymore; or - * maybe it is but the compare deleted the - * a[thiskey] entry. - */ - Py_DECREF(thiskey); - continue; - } - } - - /* Compare a[thiskey] to b[thiskey]; cmp <- true iff equal. */ - thisaval = a->ma_table[i].me_value; - assert(thisaval); - Py_INCREF(thisaval); /* keep alive */ - thisbval = PyDict_GetItem((PyObject *)b, thiskey); - if (thisbval == NULL) - cmp = 0; - else { - /* both dicts have thiskey: same values? */ - cmp = PyObject_RichCompareBool( - thisaval, thisbval, Py_EQ); - if (cmp < 0) { - Py_DECREF(thiskey); - Py_DECREF(thisaval); - goto Fail; - } - } - if (cmp == 0) { - /* New winner. */ - Py_XDECREF(akey); - Py_XDECREF(aval); - akey = thiskey; - aval = thisaval; - } - else { - Py_DECREF(thiskey); - Py_DECREF(thisaval); - } - } - *pval = aval; - return akey; - -Fail: - Py_XDECREF(akey); - Py_XDECREF(aval); - *pval = NULL; - return NULL; -} - -static int -dict_compare(dictobject *a, dictobject *b) -{ - PyObject *adiff, *bdiff, *aval, *bval; - int res; - - /* Compare lengths first */ - if (a->ma_used < b->ma_used) - return -1; /* a is shorter */ - else if (a->ma_used > b->ma_used) - return 1; /* b is shorter */ - - /* Same length -- check all keys */ - bdiff = bval = NULL; - adiff = characterize(a, b, &aval); - if (adiff == NULL) { - assert(!aval); - /* Either an error, or a is a subset with the same length so - * must be equal. - */ - res = PyErr_Occurred() ? -1 : 0; - goto Finished; - } - bdiff = characterize(b, a, &bval); - if (bdiff == NULL && PyErr_Occurred()) { - assert(!bval); - res = -1; - goto Finished; - } - res = 0; - if (bdiff) { - /* bdiff == NULL "should be" impossible now, but perhaps - * the last comparison done by the characterize() on a had - * the side effect of making the dicts equal! - */ - res = PyObject_Compare(adiff, bdiff); - } - if (res == 0 && bval != NULL) - res = PyObject_Compare(aval, bval); - -Finished: - Py_XDECREF(adiff); - Py_XDECREF(bdiff); - Py_XDECREF(aval); - Py_XDECREF(bval); - return res; -} - /* Return 1 if dicts equal, 0 if not, -1 if error. * Gets out as soon as any difference is detected. * Uses only Py_EQ comparison. @@ -1585,9 +1485,11 @@ dict_equal(dictobject *a, dictobject *b) /* temporarily bump aval's refcount to ensure it stays alive until we're done with it */ Py_INCREF(aval); - bval = PyDict_GetItem((PyObject *)b, key); + bval = PyDict_GetItemWithError((PyObject *)b, key); if (bval == NULL) { Py_DECREF(aval); + if (PyErr_Occurred()) + return -1; return 0; } cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); @@ -2028,7 +1930,7 @@ PyTypeObject PyDict_Type = { (printfunc)dict_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)dict_compare, /* tp_compare */ + 0, /* tp_compare */ (reprfunc)dict_repr, /* tp_repr */ 0, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ diff --git a/Objects/intobject.c b/Objects/intobject.c index 0ff232112c9..e64d0b1f9a0 100644 --- a/Objects/intobject.c +++ b/Objects/intobject.c @@ -446,6 +446,17 @@ int_compare(PyIntObject *v, PyIntObject *w) return (i < j) ? -1 : (i > j) ? 1 : 0; } +static PyObject * +int_richcompare(PyObject *self, PyObject *other, int op) +{ + if (!PyInt_Check(self) || !PyInt_Check(other)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return Py_CmpToRich(op, int_compare((PyIntObject *)self, + (PyIntObject *)other)); +} + static long int_hash(PyIntObject *v) { @@ -1063,7 +1074,7 @@ PyTypeObject PyInt_Type = { (printfunc)int_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)int_compare, /* tp_compare */ + 0, /* tp_compare */ (reprfunc)int_repr, /* tp_repr */ &int_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -1078,7 +1089,7 @@ PyTypeObject PyInt_Type = { int_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + int_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/Objects/listobject.c b/Objects/listobject.c index ab408e92b53..3d81656930b 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -1971,6 +1971,26 @@ build_cmpwrapper(PyObject *cmpfunc) return (PyObject *)co; } +static int +is_default_cmp(PyObject *cmpfunc) +{ + PyCFunctionObject *f; + if (cmpfunc == NULL || cmpfunc == Py_None) + return 1; + if (!PyCFunction_Check(cmpfunc)) + return 0; + f = (PyCFunction *)cmpfunc; + if (f->m_self != NULL) + return 0; + if (!PyString_Check(f->m_module)) + return 0; + if (strcmp(PyString_AS_STRING(f->m_module), "__builtin__") != 0) + return 0; + if (strcmp(f->m_ml->ml_name, "cmp") != 0) + return 0; + return 1; +} + /* An adaptive, stable, natural mergesort. See listsort.txt. * Returns Py_None on success, NULL on error. Even in case of error, the * list will be some permutation of its input state (nothing is lost or @@ -2001,7 +2021,7 @@ listsort(PyListObject *self, PyObject *args, PyObject *kwds) kwlist, &compare, &keyfunc, &reverse)) return NULL; } - if (compare == Py_None) + if (is_default_cmp(compare)) compare = NULL; if (keyfunc == Py_None) keyfunc = NULL; diff --git a/Objects/longobject.c b/Objects/longobject.c index 16c70435a16..ab8a8d78861 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -1,5 +1,3 @@ - - /* Long (arbitrary precision) integer object implementation */ /* XXX The functional organization of this file is terrible */ @@ -1882,6 +1880,14 @@ long_compare(PyLongObject *a, PyLongObject *b) return sign < 0 ? -1 : sign > 0 ? 1 : 0; } +static PyObject * +long_richcompare(PyObject *self, PyObject *other, int op) +{ + PyLongObject *a, *b; + CONVERT_BINOP((PyObject *)self, (PyObject *)other, &a, &b); + return Py_CmpToRich(op, long_compare(a, b)); +} + static long long_hash(PyLongObject *v) { @@ -3357,7 +3363,7 @@ PyTypeObject PyLong_Type = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)long_compare, /* tp_compare */ + 0, /* tp_compare */ long_repr, /* tp_repr */ &long_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -3372,7 +3378,7 @@ PyTypeObject PyLong_Type = { long_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + long_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/Objects/methodobject.c b/Objects/methodobject.c index ecc9a0ab525..862acd1f5ca 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -196,17 +196,31 @@ meth_repr(PyCFunctionObject *m) m->m_self); } -static int -meth_compare(PyCFunctionObject *a, PyCFunctionObject *b) +static PyObject * +meth_richcompare(PyObject *self, PyObject *other, int op) { - if (a->m_self != b->m_self) - return (a->m_self < b->m_self) ? -1 : 1; - if (a->m_ml->ml_meth == b->m_ml->ml_meth) - return 0; - if (strcmp(a->m_ml->ml_name, b->m_ml->ml_name) < 0) - return -1; + PyCFunctionObject *a, *b; + PyObject *res; + int eq; + + if ((op != Py_EQ && op != Py_NE) || + !PyCFunction_Check(self) || + !PyCFunction_Check(other)) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + a = (PyCFunctionObject *)self; + b = (PyCFunctionObject *)other; + eq = a->m_self == b->m_self; + if (eq) + eq = a->m_ml->ml_meth == b->m_ml->ml_meth; + if (op == Py_EQ) + res = eq ? Py_True : Py_False; else - return 1; + res = eq ? Py_False : Py_True; + Py_INCREF(res); + return res; } static long @@ -240,7 +254,7 @@ PyTypeObject PyCFunction_Type = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)meth_compare, /* tp_compare */ + 0, /* tp_compare */ (reprfunc)meth_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -255,7 +269,7 @@ PyTypeObject PyCFunction_Type = { 0, /* tp_doc */ (traverseproc)meth_traverse, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + meth_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/Objects/object.c b/Objects/object.c index 80111b4ee1a..9bcf08b39b0 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -252,14 +252,6 @@ _PyObject_NewVar(PyTypeObject *tp, Py_ssize_t nitems) return PyObject_INIT_VAR(op, tp, nitems); } -/* for binary compatibility with 2.2 */ -#undef _PyObject_Del -void -_PyObject_Del(PyObject *op) -{ - PyObject_FREE(op); -} - /* Implementation of PyObject_Print with recursion checking */ static int internal_print(PyObject *op, FILE *fp, int flags, int nesting) @@ -513,431 +505,200 @@ PyObject_Unicode(PyObject *v) #endif -/* Helper to warn about deprecated tp_compare return values. Return: - -2 for an exception; - -1 if v < w; - 0 if v == w; - 1 if v > w. - (This function cannot return 2.) +/* The new comparison philosophy is: we completely separate three-way + comparison from rich comparison. That is, PyObject_Compare() and + PyObject_Cmp() *just* use the tp_compare slot. And PyObject_RichCompare() + and PyObject_RichCompareBool() *just* use the tp_richcompare slot. + + See (*) below for practical amendments. + + IOW, only cmp() uses tp_compare; the comparison operators (==, !=, <=, <, + >=, >) only use tp_richcompare. Note that list.sort() only uses <. + + (And yes, eventually we'll rip out cmp() and tp_compare.) + + The calling conventions are different: tp_compare only gets called with two + objects of the appropriate type; tp_richcompare gets called with a first + argument of the appropriate type and a second object of an arbitrary type. + We never do any kind of coercion. + + The return conventions are also different. + + The tp_compare slot should return a C int, as follows: + + -1 if a < b or if an exception occurred + 0 if a == b + +1 if a > b + + No other return values are allowed. PyObject_Compare() has the same + calling convention. + + The tp_richcompare slot should return an object, as follows: + + NULL if an exception occurred + NotImplemented if the requested comparison is not implemented + any other false value if the requested comparison is false + any other true value if the requested comparison is true + + The PyObject_RichCompare[Bool]() wrappers raise TypeError when they get + NotImplemented. + + (*) Practical amendments: + + - If rich comparison returns NotImplemented, == and != are decided by + comparing the object pointer (i.e. falling back to the base object + implementation). + + - If three-way comparison is not implemented, it falls back on rich + comparison (but not the other way around!). + */ + +/* Forward */ +static PyObject *do_richcompare(PyObject *v, PyObject *w, int op); + +/* Perform a three-way comparison, raising TypeError if three-way comparison + is not supported. */ static int -adjust_tp_compare(int c) +do_compare(PyObject *v, PyObject *w) { - if (PyErr_Occurred()) { - if (c != -1 && c != -2) { - PyObject *t, *v, *tb; - PyErr_Fetch(&t, &v, &tb); - if (PyErr_Warn(PyExc_RuntimeWarning, - "tp_compare didn't return -1 or -2 " - "for exception") < 0) { - Py_XDECREF(t); - Py_XDECREF(v); - Py_XDECREF(tb); - } - else - PyErr_Restore(t, v, tb); - } - return -2; - } - else if (c < -1 || c > 1) { - if (PyErr_Warn(PyExc_RuntimeWarning, - "tp_compare didn't return -1, 0 or 1") < 0) - return -2; - else - return c < -1 ? -1 : 1; - } - else { - assert(c >= -1 && c <= 1); - return c; + cmpfunc f; + int ok; + + if (v->ob_type == w->ob_type && + (f = v->ob_type->tp_compare) != NULL) { + return (*f)(v, w); } + + /* Now try three-way compare before giving up. This is intentionally + elaborate; if you have a it will raise TypeError if it detects two + objects that aren't ordered with respect to each other. */ + ok = PyObject_RichCompareBool(v, w, Py_LT); + if (ok < 0) + return -1; /* Error */ + if (ok) + return -1; /* Less than */ + ok = PyObject_RichCompareBool(v, w, Py_GT); + if (ok < 0) + return -1; /* Error */ + if (ok) + return 1; /* Greater than */ + ok = PyObject_RichCompareBool(v, w, Py_EQ); + if (ok < 0) + return -1; /* Error */ + if (ok) + return 0; /* Equal */ + + /* Give up */ + PyErr_Format(PyExc_TypeError, + "unorderable types: '%.100s' <> '%.100s'", + v->ob_type->tp_name, + w->ob_type->tp_name); + return -1; } +/* Perform a three-way comparison. This wraps do_compare() with a check for + NULL arguments and a recursion check. */ +int +PyObject_Compare(PyObject *v, PyObject *w) +{ + int res; -/* Macro to get the tp_richcompare field of a type if defined */ -#define RICHCOMPARE(t) ((t)->tp_richcompare) + if (v == NULL || w == NULL) { + if (!PyErr_Occurred()) + PyErr_BadInternalCall(); + return -1; + } + if (Py_EnterRecursiveCall(" in cmp")) + return -1; + res = do_compare(v, w); + Py_LeaveRecursiveCall(); + return res < 0 ? -1 : res; +} -/* Map rich comparison operators to their swapped version, e.g. LT --> GT */ +/* Map rich comparison operators to their swapped version, e.g. LT <--> GT */ int _Py_SwappedOp[] = {Py_GT, Py_GE, Py_EQ, Py_NE, Py_LT, Py_LE}; -/* Try a genuine rich comparison, returning an object. Return: - NULL for exception; - NotImplemented if this particular rich comparison is not implemented or - undefined; - some object not equal to NotImplemented if it is implemented - (this latter object may not be a Boolean). -*/ +static char *opstrings[] = {">", ">=", "==", "!=", "<", "<="}; + +/* Perform a rich comparison, raising TypeError when the requested comparison + operator is not supported. */ static PyObject * -try_rich_compare(PyObject *v, PyObject *w, int op) +do_richcompare(PyObject *v, PyObject *w, int op) { richcmpfunc f; PyObject *res; if (v->ob_type != w->ob_type && PyType_IsSubtype(w->ob_type, v->ob_type) && - (f = RICHCOMPARE(w->ob_type)) != NULL) { + (f = w->ob_type->tp_richcompare) != NULL) { res = (*f)(w, v, _Py_SwappedOp[op]); if (res != Py_NotImplemented) return res; Py_DECREF(res); } - if ((f = RICHCOMPARE(v->ob_type)) != NULL) { + if ((f = v->ob_type->tp_richcompare) != NULL) { res = (*f)(v, w, op); if (res != Py_NotImplemented) return res; Py_DECREF(res); } - if ((f = RICHCOMPARE(w->ob_type)) != NULL) { - return (*f)(w, v, _Py_SwappedOp[op]); + if ((f = w->ob_type->tp_richcompare) != NULL) { + res = (*f)(w, v, _Py_SwappedOp[op]); + if (res != Py_NotImplemented) + return res; + Py_DECREF(res); + } + /* If neither object implements it, provide a sensible default + for == and !=, but raise an exception for ordering. */ + switch (op) { + case Py_EQ: + res = (v == w) ? Py_True : Py_False; + break; + case Py_NE: + res = (v != w) ? Py_True : Py_False; + break; + default: + PyErr_Format(PyExc_TypeError, + "unorderable types: %.100s() %s %.100s()", + v->ob_type->tp_name, + opstrings[op], + w->ob_type->tp_name); + return NULL; } - res = Py_NotImplemented; Py_INCREF(res); return res; } -/* Try a genuine rich comparison, returning an int. Return: - -1 for exception (including the case where try_rich_compare() returns an - object that's not a Boolean); - 0 if the outcome is false; - 1 if the outcome is true; - 2 if this particular rich comparison is not implemented or undefined. -*/ -static int -try_rich_compare_bool(PyObject *v, PyObject *w, int op) -{ - PyObject *res; - int ok; +/* Perform a rich comparison with object result. This wraps do_richcompare() + with a check for NULL arguments and a recursion check. */ - if (RICHCOMPARE(v->ob_type) == NULL && RICHCOMPARE(w->ob_type) == NULL) - return 2; /* Shortcut, avoid INCREF+DECREF */ - res = try_rich_compare(v, w, op); - if (res == NULL) - return -1; - if (res == Py_NotImplemented) { - Py_DECREF(res); - return 2; - } - ok = PyObject_IsTrue(res); - Py_DECREF(res); - return ok; -} - -/* Try rich comparisons to determine a 3-way comparison. Return: - -2 for an exception; - -1 if v < w; - 0 if v == w; - 1 if v > w; - 2 if this particular rich comparison is not implemented or undefined. -*/ -static int -try_rich_to_3way_compare(PyObject *v, PyObject *w) -{ - static struct { int op; int outcome; } tries[3] = { - /* Try this operator, and if it is true, use this outcome: */ - {Py_EQ, 0}, - {Py_LT, -1}, - {Py_GT, 1}, - }; - int i; - - if (RICHCOMPARE(v->ob_type) == NULL && RICHCOMPARE(w->ob_type) == NULL) - return 2; /* Shortcut */ - - for (i = 0; i < 3; i++) { - switch (try_rich_compare_bool(v, w, tries[i].op)) { - case -1: - return -2; - case 1: - return tries[i].outcome; - } - } - - return 2; -} - -/* Try a 3-way comparison, returning an int. Return: - -2 for an exception; - -1 if v < w; - 0 if v == w; - 1 if v > w; - 2 if this particular 3-way comparison is not implemented or undefined. -*/ -static int -try_3way_compare(PyObject *v, PyObject *w) -{ - int c; - cmpfunc f; - - /* Comparisons involving instances are given to instance_compare, - which has the same return conventions as this function. */ - - f = v->ob_type->tp_compare; - - /* If both have the same (non-NULL) tp_compare, use it. */ - if (f != NULL && f == w->ob_type->tp_compare) { - c = (*f)(v, w); - return adjust_tp_compare(c); - } - - /* If either tp_compare is _PyObject_SlotCompare, that's safe. */ - if (f == _PyObject_SlotCompare || - w->ob_type->tp_compare == _PyObject_SlotCompare) - return _PyObject_SlotCompare(v, w); - - /* If we're here, v and w, - a) are not instances; - b) have different types or a type without tp_compare; and - c) don't have a user-defined tp_compare. - tp_compare implementations in C assume that both arguments - have their type, so we give up if the coercion fails. - */ - c = PyNumber_CoerceEx(&v, &w); - if (c < 0) - return -2; - if (c > 0) - return 2; - f = v->ob_type->tp_compare; - if (f != NULL && f == w->ob_type->tp_compare) { - c = (*f)(v, w); - Py_DECREF(v); - Py_DECREF(w); - return adjust_tp_compare(c); - } - - /* No comparison defined */ - Py_DECREF(v); - Py_DECREF(w); - return 2; -} - -/* Final fallback 3-way comparison, returning an int. Return: - -2 if an error occurred; - -1 if v < w; - 0 if v == w; - 1 if v > w. -*/ -static int -default_3way_compare(PyObject *v, PyObject *w) -{ - int c; - const char *vname, *wname; - - if (v->ob_type == w->ob_type) { - /* When comparing these pointers, they must be cast to - * integer types (i.e. Py_uintptr_t, our spelling of C9X's - * uintptr_t). ANSI specifies that pointer compares other - * than == and != to non-related structures are undefined. - */ - Py_uintptr_t vv = (Py_uintptr_t)v; - Py_uintptr_t ww = (Py_uintptr_t)w; - return (vv < ww) ? -1 : (vv > ww) ? 1 : 0; - } - - /* None is smaller than anything */ - if (v == Py_None) - return -1; - if (w == Py_None) - return 1; - - /* different type: compare type names; numbers are smaller */ - if (PyNumber_Check(v)) - vname = ""; - else - vname = v->ob_type->tp_name; - if (PyNumber_Check(w)) - wname = ""; - else - wname = w->ob_type->tp_name; - c = strcmp(vname, wname); - if (c < 0) - return -1; - if (c > 0) - return 1; - /* Same type name, or (more likely) incomparable numeric types */ - return ((Py_uintptr_t)(v->ob_type) < ( - Py_uintptr_t)(w->ob_type)) ? -1 : 1; -} - -/* Do a 3-way comparison, by hook or by crook. Return: - -2 for an exception (but see below); - -1 if v < w; - 0 if v == w; - 1 if v > w; - BUT: if the object implements a tp_compare function, it returns - whatever this function returns (whether with an exception or not). -*/ -static int -do_cmp(PyObject *v, PyObject *w) -{ - int c; - cmpfunc f; - - if (v->ob_type == w->ob_type - && (f = v->ob_type->tp_compare) != NULL) { - c = (*f)(v, w); - return adjust_tp_compare(c); - } - /* We only get here if one of the following is true: - a) v and w have different types - b) v and w have the same type, which doesn't have tp_compare - c) v and w are instances, and either __cmp__ is not defined or - __cmp__ returns NotImplemented - */ - c = try_rich_to_3way_compare(v, w); - if (c < 2) - return c; - c = try_3way_compare(v, w); - if (c < 2) - return c; - return default_3way_compare(v, w); -} - -/* Compare v to w. Return - -1 if v < w or exception (PyErr_Occurred() true in latter case). - 0 if v == w. - 1 if v > w. - XXX The docs (C API manual) say the return value is undefined in case - XXX of error. -*/ -int -PyObject_Compare(PyObject *v, PyObject *w) -{ - int result; - - if (v == NULL || w == NULL) { - PyErr_BadInternalCall(); - return -1; - } - if (v == w) - return 0; - if (Py_EnterRecursiveCall(" in cmp")) - return -1; - result = do_cmp(v, w); - Py_LeaveRecursiveCall(); - return result < 0 ? -1 : result; -} - -/* Return (new reference to) Py_True or Py_False. */ -static PyObject * -convert_3way_to_object(int op, int c) -{ - PyObject *result; - switch (op) { - case Py_LT: c = c < 0; break; - case Py_LE: c = c <= 0; break; - case Py_EQ: c = c == 0; break; - case Py_NE: c = c != 0; break; - case Py_GT: c = c > 0; break; - case Py_GE: c = c >= 0; break; - } - result = c ? Py_True : Py_False; - Py_INCREF(result); - return result; -} - -/* We want a rich comparison but don't have one. Try a 3-way cmp instead. - Return - NULL if error - Py_True if v op w - Py_False if not (v op w) -*/ -static PyObject * -try_3way_to_rich_compare(PyObject *v, PyObject *w, int op) -{ - int c; - - c = try_3way_compare(v, w); - if (c >= 2) - c = default_3way_compare(v, w); - if (c <= -2) - return NULL; - return convert_3way_to_object(op, c); -} - -/* Do rich comparison on v and w. Return - NULL if error - Else a new reference to an object other than Py_NotImplemented, usually(?): - Py_True if v op w - Py_False if not (v op w) -*/ -static PyObject * -do_richcmp(PyObject *v, PyObject *w, int op) -{ - PyObject *res; - - res = try_rich_compare(v, w, op); - if (res != Py_NotImplemented) - return res; - Py_DECREF(res); - - return try_3way_to_rich_compare(v, w, op); -} - -/* Return: - NULL for exception; - some object not equal to NotImplemented if it is implemented - (this latter object may not be a Boolean). -*/ PyObject * PyObject_RichCompare(PyObject *v, PyObject *w, int op) { PyObject *res; assert(Py_LT <= op && op <= Py_GE); + if (v == NULL || w == NULL) { + if (!PyErr_Occurred()) + PyErr_BadInternalCall(); + return NULL; + } if (Py_EnterRecursiveCall(" in cmp")) return NULL; - - /* If the types are equal, and not old-style instances, try to - get out cheap (don't bother with coercions etc.). */ - if (v->ob_type == w->ob_type) { - cmpfunc fcmp; - richcmpfunc frich = RICHCOMPARE(v->ob_type); - /* If the type has richcmp, try it first. try_rich_compare - tries it two-sided, which is not needed since we've a - single type only. */ - if (frich != NULL) { - res = (*frich)(v, w, op); - if (res != Py_NotImplemented) - goto Done; - Py_DECREF(res); - } - /* No richcmp, or this particular richmp not implemented. - Try 3-way cmp. */ - fcmp = v->ob_type->tp_compare; - if (fcmp != NULL) { - int c = (*fcmp)(v, w); - c = adjust_tp_compare(c); - if (c == -2) { - res = NULL; - goto Done; - } - res = convert_3way_to_object(op, c); - goto Done; - } - } - - /* Fast path not taken, or couldn't deliver a useful result. */ - res = do_richcmp(v, w, op); -Done: + res = do_richcompare(v, w, op); Py_LeaveRecursiveCall(); return res; } -/* Return -1 if error; 1 if v op w; 0 if not (v op w). */ +/* Perform a rich comparison with integer result. This wraps + PyObject_RichCompare(), returning -1 for error, 0 for false, 1 for true. */ int PyObject_RichCompareBool(PyObject *v, PyObject *w, int op) { PyObject *res; int ok; - /* Quick result when objects are the same. - Guarantees that identity implies equality. */ - if (v == w) { - if (op == Py_EQ) - return 1; - else if (op == Py_NE) - return 0; - } - res = PyObject_RichCompare(v, w, op); if (res == NULL) return -1; @@ -949,6 +710,44 @@ PyObject_RichCompareBool(PyObject *v, PyObject *w, int op) return ok; } +/* Turn the result of a three-way comparison into the result expected by a + rich comparison. */ +PyObject * +Py_CmpToRich(int op, int cmp) +{ + PyObject *res; + int ok; + + if (PyErr_Occurred()) + return NULL; + switch (op) { + case Py_LT: + ok = cmp < 0; + break; + case Py_LE: + ok = cmp <= 0; + break; + case Py_EQ: + ok = cmp == 0; + break; + case Py_NE: + ok = cmp != 0; + break; + case Py_GT: + ok = cmp > 0; + break; + case Py_GE: + ok = cmp >= 0; + break; + default: + PyErr_BadArgument(); + return NULL; + } + res = ok ? Py_True : Py_False; + Py_INCREF(res); + return res; +} + /* Set of hash utility functions to help maintaining the invariant that if a==b then hash(a)==hash(b) @@ -1832,6 +1631,9 @@ _Py_ReadyTypes(void) if (PyType_Ready(&PyNotImplemented_Type) < 0) Py_FatalError("Can't initialize type(NotImplemented)"); + + if (PyType_Ready(&PyCode_Type) < 0) + Py_FatalError("Can't initialize 'code'"); } diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index d8a24653a7d..0075a4e3d04 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -280,25 +280,55 @@ static PyMethodDef slice_methods[] = { {NULL, NULL} }; -static int -slice_compare(PySliceObject *v, PySliceObject *w) +static PyObject * +slice_richcompare(PyObject *v, PyObject *w, int op) { - int result = 0; + PyObject *t1; + PyObject *t2; + PyObject *res; - if (v == w) - return 0; + if (v == w) { + /* XXX Do we really need this shortcut? + There's a unit test for it, but is that fair? */ + switch (op) { + case Py_EQ: + case Py_LE: + case Py_GE: + res = Py_True; + break; + default: + res = Py_False; + break; + } + Py_INCREF(res); + return res; + } - if (PyObject_Cmp(v->start, w->start, &result) < 0) - return -2; - if (result != 0) - return result; - if (PyObject_Cmp(v->stop, w->stop, &result) < 0) - return -2; - if (result != 0) - return result; - if (PyObject_Cmp(v->step, w->step, &result) < 0) - return -2; - return result; + t1 = PyTuple_New(3); + t2 = PyTuple_New(3); + if (t1 == NULL || t2 == NULL) + return NULL; + + PyTuple_SET_ITEM(t1, 0, ((PySliceObject *)v)->start); + PyTuple_SET_ITEM(t1, 1, ((PySliceObject *)v)->stop); + PyTuple_SET_ITEM(t1, 2, ((PySliceObject *)v)->step); + PyTuple_SET_ITEM(t2, 0, ((PySliceObject *)w)->start); + PyTuple_SET_ITEM(t2, 1, ((PySliceObject *)w)->stop); + PyTuple_SET_ITEM(t2, 2, ((PySliceObject *)w)->step); + + res = PyObject_RichCompare(t1, t2, op); + + PyTuple_SET_ITEM(t1, 0, NULL); + PyTuple_SET_ITEM(t1, 1, NULL); + PyTuple_SET_ITEM(t1, 2, NULL); + PyTuple_SET_ITEM(t2, 0, NULL); + PyTuple_SET_ITEM(t2, 1, NULL); + PyTuple_SET_ITEM(t2, 2, NULL); + + Py_DECREF(t1); + Py_DECREF(t2); + + return res; } static long @@ -318,7 +348,7 @@ PyTypeObject PySlice_Type = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)slice_compare, /* tp_compare */ + 0, /* tp_compare */ (reprfunc)slice_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -333,7 +363,7 @@ PyTypeObject PySlice_Type = { slice_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + slice_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index d19801f38ff..d16c6b4daec 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -361,16 +361,6 @@ static PyGetSetDef type_getsets[] = { {0} }; -static int -type_compare(PyObject *v, PyObject *w) -{ - /* This is called with type objects only. So we - can just compare the addresses. */ - Py_uintptr_t vv = (Py_uintptr_t)v; - Py_uintptr_t ww = (Py_uintptr_t)w; - return (vv < ww) ? -1 : (vv > ww) ? 1 : 0; -} - static PyObject * type_repr(PyTypeObject *type) { @@ -2192,12 +2182,12 @@ PyTypeObject PyType_Type = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - type_compare, /* tp_compare */ + 0, /* tp_compare */ (reprfunc)type_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - (hashfunc)_Py_HashPointer, /* tp_hash */ + 0, /* tp_hash */ (ternaryfunc)type_call, /* tp_call */ 0, /* tp_str */ (getattrofunc)type_getattro, /* tp_getattro */ @@ -2301,6 +2291,30 @@ object_str(PyObject *self) return f(self); } +static PyObject * +object_richcompare(PyObject *self, PyObject *other, int op) +{ + PyObject *res; + + switch (op) { + + case Py_EQ: + res = (self == other) ? Py_True : Py_False; + break; + + case Py_NE: + res = (self != other) ? Py_True : Py_False; + break; + + default: + res = Py_NotImplemented; + break; + } + + Py_INCREF(res); + return res; +} + static PyObject * object_get_class(PyObject *self, void *closure) { @@ -2703,7 +2717,7 @@ PyTypeObject PyBaseObject_Type = { PyDoc_STR("The most base type"), /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + object_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index f8143069302..206a45512b6 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -184,7 +184,9 @@ weakref_repr(PyWeakReference *self) static PyObject * weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op) { - if (op != Py_EQ || self->ob_type != other->ob_type) { + if ((op != Py_EQ && op != Py_NE) || + !PyWeakref_Check(self) || + !PyWeakref_Check(other)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } @@ -458,12 +460,12 @@ proxy_setattr(PyWeakReference *proxy, PyObject *name, PyObject *value) return PyObject_SetAttr(PyWeakref_GET_OBJECT(proxy), name, value); } -static int -proxy_compare(PyObject *proxy, PyObject *v) +static PyObject * +proxy_richcompare(PyObject *proxy, PyObject *v, int op) { - UNWRAP_I(proxy); - UNWRAP_I(v); - return PyObject_Compare(proxy, v); + UNWRAP(proxy); + UNWRAP(v); + return PyObject_RichCompare(proxy, v, op); } /* number slots */ @@ -649,7 +651,7 @@ _PyWeakref_ProxyType = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - proxy_compare, /* tp_compare */ + 0, /* tp_compare */ (reprfunc)proxy_repr, /* tp_repr */ &proxy_as_number, /* tp_as_number */ &proxy_as_sequence, /* tp_as_sequence */ @@ -664,7 +666,7 @@ _PyWeakref_ProxyType = { 0, /* tp_doc */ (traverseproc)gc_traverse, /* tp_traverse */ (inquiry)gc_clear, /* tp_clear */ - 0, /* tp_richcompare */ + proxy_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)proxy_iter, /* tp_iter */ (iternextfunc)proxy_iternext, /* tp_iternext */ @@ -683,7 +685,7 @@ _PyWeakref_CallableProxyType = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - proxy_compare, /* tp_compare */ + 0, /* tp_compare */ (unaryfunc)proxy_repr, /* tp_repr */ &proxy_as_number, /* tp_as_number */ &proxy_as_sequence, /* tp_as_sequence */ @@ -698,7 +700,7 @@ _PyWeakref_CallableProxyType = { 0, /* tp_doc */ (traverseproc)gc_traverse, /* tp_traverse */ (inquiry)gc_clear, /* tp_clear */ - 0, /* tp_richcompare */ + proxy_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)proxy_iter, /* tp_iter */ (iternextfunc)proxy_iternext, /* tp_iternext */ diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 16758d7928d..6309624d93a 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1324,9 +1324,10 @@ get_len_of_range_longs(PyObject *lo, PyObject *hi, PyObject *step) PyObject *tmp1 = NULL, *tmp2 = NULL, *tmp3 = NULL; /* holds sub-expression evaluations */ - /* if (lo >= hi), return length of 0. */ - if (PyObject_Compare(lo, hi) >= 0) - return 0; + /* If (lo >= hi), return length of 0 (or error). */ + n = PyObject_RichCompareBool(lo, hi, Py_LT); + if (n <= 0) + return n; if ((one = PyLong_FromLong(1L)) == NULL) goto Fail; @@ -1378,7 +1379,7 @@ handle_range_longs(PyObject *self, PyObject *args) PyObject *v = NULL; long bign; int i, n; - int cmp_result; + int step_pos; PyObject *zero = PyLong_FromLong(0); @@ -1439,17 +1440,20 @@ handle_range_longs(PyObject *self, PyObject *args) goto Fail; } - if (PyObject_Cmp(istep, zero, &cmp_result) == -1) + step_pos = PyObject_RichCompareBool(istep, zero, Py_GT); + if (step_pos < 0) goto Fail; - if (cmp_result == 0) { - PyErr_SetString(PyExc_ValueError, - "range() step argument must not be zero"); - goto Fail; - } - - if (cmp_result > 0) + if (step_pos) bign = get_len_of_range_longs(ilow, ihigh, istep); else { + int step_zero = PyObject_RichCompareBool(istep, zero, Py_EQ); + if (step_zero < 0) + goto Fail; + if (step_zero) { + PyErr_SetString(PyExc_ValueError, + "range() step argument must not be zero"); + goto Fail; + } PyObject *neg_istep = PyNumber_Negative(istep); if (neg_istep == NULL) goto Fail;