| import copy | |
| import unittest | |
| from test.test_support import run_unittest, TestFailed, check_warnings | |
| # Fake a number that implements numeric methods through __coerce__ | |
| class CoerceNumber: | |
| def __init__(self, arg): | |
| self.arg = arg | |
| def __repr__(self): | |
| return '<CoerceNumber %s>' % repr(self.arg) | |
| def __coerce__(self, other): | |
| if isinstance(other, CoerceNumber): | |
| return self.arg, other.arg | |
| else: | |
| return (self.arg, other) | |
| # New-style class version of CoerceNumber | |
| class CoerceTo(object): | |
| def __init__(self, arg): | |
| self.arg = arg | |
| def __coerce__(self, other): | |
| if isinstance(other, CoerceTo): | |
| return self.arg, other.arg | |
| else: | |
| return self.arg, other | |
| # Fake a number that implements numeric ops through methods. | |
| class MethodNumber: | |
| def __init__(self,arg): | |
| self.arg = arg | |
| def __repr__(self): | |
| return '<MethodNumber %s>' % repr(self.arg) | |
| def __add__(self,other): | |
| return self.arg + other | |
| def __radd__(self,other): | |
| return other + self.arg | |
| def __sub__(self,other): | |
| return self.arg - other | |
| def __rsub__(self,other): | |
| return other - self.arg | |
| def __mul__(self,other): | |
| return self.arg * other | |
| def __rmul__(self,other): | |
| return other * self.arg | |
| def __div__(self,other): | |
| return self.arg / other | |
| def __rdiv__(self,other): | |
| return other / self.arg | |
| def __truediv__(self,other): | |
| return self.arg / other | |
| def __rtruediv__(self,other): | |
| return other / self.arg | |
| def __floordiv__(self,other): | |
| return self.arg // other | |
| def __rfloordiv__(self,other): | |
| return other // self.arg | |
| def __pow__(self,other): | |
| return self.arg ** other | |
| def __rpow__(self,other): | |
| return other ** self.arg | |
| def __mod__(self,other): | |
| return self.arg % other | |
| def __rmod__(self,other): | |
| return other % self.arg | |
| def __cmp__(self, other): | |
| return cmp(self.arg, other) | |
| candidates = [2, 2L, 4.0, 2+0j, [1], (2,), None, | |
| MethodNumber(2), CoerceNumber(2)] | |
| infix_binops = [ '+', '-', '*', '**', '%', '//', '/' ] | |
| TE = TypeError | |
| # b = both normal and augmented give same result list | |
| # s = single result lists for normal and augmented | |
| # e = equals other results | |
| # result lists: ['+', '-', '*', '**', '%', '//', ('classic /', 'new /')] | |
| # ^^^^^^^^^^^^^^^^^^^^^^ | |
| # 2-tuple if results differ | |
| # else only one value | |
| infix_results = { | |
| # 2 | |
| (0,0): ('b', [4, 0, 4, 4, 0, 1, (1, 1.0)]), | |
| (0,1): ('e', (0,0)), | |
| (0,2): ('b', [6.0, -2.0, 8.0, 16.0, 2.0, 0.0, 0.5]), | |
| (0,3): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), | |
| (0,4): ('b', [TE, TE, [1, 1], TE, TE, TE, TE]), | |
| (0,5): ('b', [TE, TE, (2, 2), TE, TE, TE, TE]), | |
| (0,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), | |
| (0,7): ('e', (0,0)), | |
| (0,8): ('e', (0,0)), | |
| # 2L | |
| (1,0): ('e', (0,0)), | |
| (1,1): ('e', (0,1)), | |
| (1,2): ('e', (0,2)), | |
| (1,3): ('e', (0,3)), | |
| (1,4): ('e', (0,4)), | |
| (1,5): ('e', (0,5)), | |
| (1,6): ('e', (0,6)), | |
| (1,7): ('e', (0,7)), | |
| (1,8): ('e', (0,8)), | |
| # 4.0 | |
| (2,0): ('b', [6.0, 2.0, 8.0, 16.0, 0.0, 2.0, 2.0]), | |
| (2,1): ('e', (2,0)), | |
| (2,2): ('b', [8.0, 0.0, 16.0, 256.0, 0.0, 1.0, 1.0]), | |
| (2,3): ('b', [6+0j, 2+0j, 8+0j, 16+0j, 0+0j, 2+0j, 2+0j]), | |
| (2,4): ('b', [TE, TE, TE, TE, TE, TE, TE]), | |
| (2,5): ('e', (2,4)), | |
| (2,6): ('e', (2,4)), | |
| (2,7): ('e', (2,0)), | |
| (2,8): ('e', (2,0)), | |
| # (2+0j) | |
| (3,0): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), | |
| (3,1): ('e', (3,0)), | |
| (3,2): ('b', [6+0j, -2+0j, 8+0j, 16+0j, 2+0j, 0+0j, 0.5+0j]), | |
| (3,3): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), | |
| (3,4): ('b', [TE, TE, TE, TE, TE, TE, TE]), | |
| (3,5): ('e', (3,4)), | |
| (3,6): ('e', (3,4)), | |
| (3,7): ('e', (3,0)), | |
| (3,8): ('e', (3,0)), | |
| # [1] | |
| (4,0): ('b', [TE, TE, [1, 1], TE, TE, TE, TE]), | |
| (4,1): ('e', (4,0)), | |
| (4,2): ('b', [TE, TE, TE, TE, TE, TE, TE]), | |
| (4,3): ('b', [TE, TE, TE, TE, TE, TE, TE]), | |
| (4,4): ('b', [[1, 1], TE, TE, TE, TE, TE, TE]), | |
| (4,5): ('s', [TE, TE, TE, TE, TE, TE, TE], [[1, 2], TE, TE, TE, TE, TE, TE]), | |
| (4,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), | |
| (4,7): ('e', (4,0)), | |
| (4,8): ('e', (4,0)), | |
| # (2,) | |
| (5,0): ('b', [TE, TE, (2, 2), TE, TE, TE, TE]), | |
| (5,1): ('e', (5,0)), | |
| (5,2): ('b', [TE, TE, TE, TE, TE, TE, TE]), | |
| (5,3): ('e', (5,2)), | |
| (5,4): ('e', (5,2)), | |
| (5,5): ('b', [(2, 2), TE, TE, TE, TE, TE, TE]), | |
| (5,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), | |
| (5,7): ('e', (5,0)), | |
| (5,8): ('e', (5,0)), | |
| # None | |
| (6,0): ('b', [TE, TE, TE, TE, TE, TE, TE]), | |
| (6,1): ('e', (6,0)), | |
| (6,2): ('e', (6,0)), | |
| (6,3): ('e', (6,0)), | |
| (6,4): ('e', (6,0)), | |
| (6,5): ('e', (6,0)), | |
| (6,6): ('e', (6,0)), | |
| (6,7): ('e', (6,0)), | |
| (6,8): ('e', (6,0)), | |
| # MethodNumber(2) | |
| (7,0): ('e', (0,0)), | |
| (7,1): ('e', (0,1)), | |
| (7,2): ('e', (0,2)), | |
| (7,3): ('e', (0,3)), | |
| (7,4): ('e', (0,4)), | |
| (7,5): ('e', (0,5)), | |
| (7,6): ('e', (0,6)), | |
| (7,7): ('e', (0,7)), | |
| (7,8): ('e', (0,8)), | |
| # CoerceNumber(2) | |
| (8,0): ('e', (0,0)), | |
| (8,1): ('e', (0,1)), | |
| (8,2): ('e', (0,2)), | |
| (8,3): ('e', (0,3)), | |
| (8,4): ('e', (0,4)), | |
| (8,5): ('e', (0,5)), | |
| (8,6): ('e', (0,6)), | |
| (8,7): ('e', (0,7)), | |
| (8,8): ('e', (0,8)), | |
| } | |
| def process_infix_results(): | |
| for key in sorted(infix_results): | |
| val = infix_results[key] | |
| if val[0] == 'e': | |
| infix_results[key] = infix_results[val[1]] | |
| else: | |
| if val[0] == 's': | |
| res = (val[1], val[2]) | |
| elif val[0] == 'b': | |
| res = (val[1], val[1]) | |
| for i in range(1): | |
| if isinstance(res[i][6], tuple): | |
| if 1/2 == 0: | |
| # testing with classic (floor) division | |
| res[i][6] = res[i][6][0] | |
| else: | |
| # testing with -Qnew | |
| res[i][6] = res[i][6][1] | |
| infix_results[key] = res | |
| with check_warnings(("classic (int|long) division", DeprecationWarning), | |
| quiet=True): | |
| process_infix_results() | |
| # now infix_results has two lists of results for every pairing. | |
| prefix_binops = [ 'divmod' ] | |
| prefix_results = [ | |
| [(1,0), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1,0)], | |
| [(1L,0L), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1L,0L)], | |
| [(2.0,0.0), (2.0,0.0), (1.0,0.0), ((2+0j),0j), TE, TE, TE, TE, (2.0,0.0)], | |
| [((1+0j),0j), ((1+0j),0j), (0j,(2+0j)), ((1+0j),0j), TE, TE, TE, TE, ((1+0j),0j)], | |
| [TE, TE, TE, TE, TE, TE, TE, TE, TE], | |
| [TE, TE, TE, TE, TE, TE, TE, TE, TE], | |
| [TE, TE, TE, TE, TE, TE, TE, TE, TE], | |
| [TE, TE, TE, TE, TE, TE, TE, TE, TE], | |
| [(1,0), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1,0)] | |
| ] | |
| def format_float(value): | |
| if abs(value) < 0.01: | |
| return '0.0' | |
| else: | |
| return '%.1f' % value | |
| # avoid testing platform fp quirks | |
| def format_result(value): | |
| if isinstance(value, complex): | |
| return '(%s + %sj)' % (format_float(value.real), | |
| format_float(value.imag)) | |
| elif isinstance(value, float): | |
| return format_float(value) | |
| return str(value) | |
| class CoercionTest(unittest.TestCase): | |
| def test_infix_binops(self): | |
| for ia, a in enumerate(candidates): | |
| for ib, b in enumerate(candidates): | |
| results = infix_results[(ia, ib)] | |
| for op, res, ires in zip(infix_binops, results[0], results[1]): | |
| if res is TE: | |
| self.assertRaises(TypeError, eval, | |
| 'a %s b' % op, {'a': a, 'b': b}) | |
| else: | |
| self.assertEqual(format_result(res), | |
| format_result(eval('a %s b' % op)), | |
| '%s %s %s == %s failed' % (a, op, b, res)) | |
| try: | |
| z = copy.copy(a) | |
| except copy.Error: | |
| z = a # assume it has no inplace ops | |
| if ires is TE: | |
| try: | |
| exec 'z %s= b' % op | |
| except TypeError: | |
| pass | |
| else: | |
| self.fail("TypeError not raised") | |
| else: | |
| exec('z %s= b' % op) | |
| self.assertEqual(ires, z) | |
| def test_prefix_binops(self): | |
| for ia, a in enumerate(candidates): | |
| for ib, b in enumerate(candidates): | |
| for op in prefix_binops: | |
| res = prefix_results[ia][ib] | |
| if res is TE: | |
| self.assertRaises(TypeError, eval, | |
| '%s(a, b)' % op, {'a': a, 'b': b}) | |
| else: | |
| self.assertEqual(format_result(res), | |
| format_result(eval('%s(a, b)' % op)), | |
| '%s(%s, %s) == %s failed' % (op, a, b, res)) | |
| def test_cmptypes(self): | |
| # Built-in tp_compare slots expect their arguments to have the | |
| # same type, but a user-defined __coerce__ doesn't have to obey. | |
| # SF #980352 | |
| evil_coercer = CoerceTo(42) | |
| # Make sure these don't crash any more | |
| self.assertNotEqual(cmp(u'fish', evil_coercer), 0) | |
| self.assertNotEqual(cmp(slice(1), evil_coercer), 0) | |
| # ...but that this still works | |
| class WackyComparer(object): | |
| def __cmp__(slf, other): | |
| self.assertTrue(other == 42, 'expected evil_coercer, got %r' % other) | |
| return 0 | |
| __hash__ = None # Invalid cmp makes this unhashable | |
| self.assertEqual(cmp(WackyComparer(), evil_coercer), 0) | |
| # ...and classic classes too, since that code path is a little different | |
| class ClassicWackyComparer: | |
| def __cmp__(slf, other): | |
| self.assertTrue(other == 42, 'expected evil_coercer, got %r' % other) | |
| return 0 | |
| self.assertEqual(cmp(ClassicWackyComparer(), evil_coercer), 0) | |
| def test_infinite_rec_classic_classes(self): | |
| # if __coerce__() returns its arguments reversed it causes an infinite | |
| # recursion for classic classes. | |
| class Tester: | |
| def __coerce__(self, other): | |
| return other, self | |
| exc = TestFailed("__coerce__() returning its arguments reverse " | |
| "should raise RuntimeError") | |
| try: | |
| Tester() + 1 | |
| except (RuntimeError, TypeError): | |
| return | |
| except: | |
| raise exc | |
| else: | |
| raise exc | |
| def test_main(): | |
| with check_warnings(("complex divmod.., // and % are deprecated", | |
| DeprecationWarning), | |
| ("classic (int|long) division", DeprecationWarning), | |
| quiet=True): | |
| run_unittest(CoercionTest) | |
| if __name__ == "__main__": | |
| test_main() |