diff -r 74b7ff20e0e4 Lib/functools.py --- a/Lib/functools.py Tue Jul 02 00:17:14 2013 +0200 +++ b/Lib/functools.py Mon Jul 08 16:44:52 2013 +1000 @@ -87,21 +87,77 @@ ### total_ordering class decorator ################################################################################ +def _not_op(op, other): + # "not a < b" handles "a>= b" + # "not a <= b" handles "a> b" + # "not a>= b" handles "a < b" + # "not a> b" handles "a <= b" + op_result = op(other) + if op_result is NotImplemented: + return op_result + return not op_result + +def _op_or_eq(op, self, other): + # "a < b or a == b" handles "a <= b" + # "a> b or a == b" handles "a>= b" + op_result = op(other) + if op_result: + # Short circuit OR, as op is True + # NotImplemented is also passed back here + return op_result + return self.__eq__(other) + +def _not_op_and_not_eq(op, self, other): + # "not (a < b or a == b)" handles "a> b" + # "not a < b and a != b" is equivalent + # "not (a> b or a == b)" handles "a < b" + # "not a> b and a != b" is equivalent + op_result = op(other) + if op_result: + # Short circuit AND, as not_op is False + # NotImplemented is also passed back here + if op_result is NotImplemented: + return op_result + return not op_result + return self.__ne__(other) + +def _not_op_or_eq(op, self, other): + # "not a <= b or a == b" handles "a>= b" + # "not a>= b or a == b" handles "a <= b" + op_result = op(other) + if op_result is NotImplemented: + return op_result + if op_result: + return self.__eq__(other) + # Short circuit OR, as not_op is True + return not op_result + +def _op_and_not_eq(op, self, other): + # "a <= b and not a == b" handles "a < b" + # "a>= b and not a == b" handles "a> b" + op_result = op(other) + if op_result is NotImplemented: + return op_result + if op_result: + return self.__ne__(other) + # Short circuit AND, as op is False + return op_result + def total_ordering(cls): """Class decorator that fills in missing ordering methods""" convert = { - '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)), - ('__le__', lambda self, other: self < other or self == other), - ('__ge__', lambda self, other: not self < other)], - '__le__': [('__ge__', lambda self, other: not self <= other or self == other), - ('__lt__', lambda self, other: self <= other and not self == other), - ('__gt__', lambda self, other: not self <= other)], - '__gt__': [('__lt__', lambda self, other: not (self> other or self == other)), - ('__ge__', lambda self, other: self> other or self == other), - ('__le__', lambda self, other: not self> other)], - '__ge__': [('__le__', lambda self, other: (not self>= other) or self == other), - ('__gt__', lambda self, other: self>= other and not self == other), - ('__lt__', lambda self, other: not self>= other)] + '__lt__': [('__gt__', lambda self, other: _not_op_and_not_eq(self.__lt__, self, other)), + ('__le__', lambda self, other: _op_or_eq(self.__lt__, self, other)), + ('__ge__', lambda self, other: _not_op(self.__lt__, other))], + '__le__': [('__ge__', lambda self, other: _not_op_or_eq(self.__le__, self, other)), + ('__lt__', lambda self, other: _op_and_not_eq(self.__le__, self, other)), + ('__gt__', lambda self, other: _not_op(self.__le__, other))], + '__gt__': [('__lt__', lambda self, other: _not_op_and_not_eq(self.__gt__, self, other)), + ('__ge__', lambda self, other: _op_or_eq(self.__gt__, self, other)), + ('__le__', lambda self, other: _not_op(self.__gt__, other))], + '__ge__': [('__le__', lambda self, other: _not_op_or_eq(self.__ge__, self, other)), + ('__gt__', lambda self, other: _op_and_not_eq(self.__ge__, self, other)), + ('__lt__', lambda self, other: _not_op(self.__ge__, other))] } # Find user-defined comparisons (not those inherited from object). roots = [op for op in convert if getattr(cls, op, None) is not getattr(object, op, None)] diff -r 74b7ff20e0e4 Lib/test/test_functools.py --- a/Lib/test/test_functools.py Tue Jul 02 00:17:14 2013 +0200 +++ b/Lib/test/test_functools.py Mon Jul 08 16:44:52 2013 +1000 @@ -643,22 +643,87 @@ class A: pass - def test_bug_10042(self): + def test_type_error_when_not_implemented(self): + # bug 10042; ensure stack overflow does not occur + # when decorated types return NotImplemented @functools.total_ordering - class TestTO: + class ImplementsLessThan: def __init__(self, value): self.value = value def __eq__(self, other): - if isinstance(other, TestTO): + if isinstance(other, ImplementsLessThan): return self.value == other.value - return False + return False def __lt__(self, other): - if isinstance(other, TestTO): + if isinstance(other, ImplementsLessThan): return self.value < other.value - raise TypeError - with self.assertRaises(TypeError): - TestTO(8) <= () + return NotImplemented + @functools.total_ordering + class ImplementsGreaterThan: + def __init__(self, value): + self.value = value + def __eq__(self, other): + if isinstance(other, ImplementsGreaterThan): + return self.value == other.value + return False + def __gt__(self, other): + if isinstance(other, ImplementsGreaterThan): + return self.value> other.value + return NotImplemented + + @functools.total_ordering + class ImplementsLessThanEqualTo: + def __init__(self, value): + self.value = value + def __eq__(self, other): + if isinstance(other, ImplementsLessThanEqualTo): + return self.value == other.value + return False + def __le__(self, other): + if isinstance(other, ImplementsLessThanEqualTo): + return self.value <= other.value + return NotImplemented + + @functools.total_ordering + class ImplementsGreaterThanEqualTo: + def __init__(self, value): + self.value = value + def __eq__(self, other): + if isinstance(other, ImplementsGreaterThanEqualTo): + return self.value == other.value + return False + def __ge__(self, other): + if isinstance(other, ImplementsGreaterThanEqualTo): + return self.value>= other.value + return NotImplemented + + with self.subTest("LT < 1"), self.assertRaises(TypeError): + ImplementsLessThan(-1) < 1 + + with self.subTest("LT < LE"), self.assertRaises(TypeError): + ImplementsLessThan(0) < ImplementsLessThanEqualTo(0) + + with self.subTest("LT < GT"), self.assertRaises(TypeError): + ImplementsLessThan(1) < ImplementsGreaterThan(1) + + with self.subTest("LE <= LT"), self.assertRaises(TypeError): + ImplementsLessThanEqualTo(2) <= ImplementsLessThan(2) + + with self.subTest("LE <= GE"), self.assertRaises(TypeError): + ImplementsLessThanEqualTo(3) <= ImplementsGreaterThanEqualTo(3) + + with self.subTest("GT> GE"), self.assertRaises(TypeError): + ImplementsGreaterThan(4)> ImplementsGreaterThanEqualTo(4) + + with self.subTest("GT> LT"), self.assertRaises(TypeError): + ImplementsGreaterThan(5)> ImplementsLessThan(5) + + with self.subTest("GE>= GT"), self.assertRaises(TypeError): + ImplementsGreaterThanEqualTo(6)>= ImplementsGreaterThan(6) + + with self.subTest("GE>= LE"), self.assertRaises(TypeError): + ImplementsGreaterThanEqualTo(7)>= ImplementsLessThanEqualTo(7) class TestLRU(unittest.TestCase):

AltStyle によって変換されたページ (->オリジナル) /