1 import copy 2 import unittest 3 from test.test_support import run_unittest, TestFailed, check_warnings 4 5 6 # Fake a number that implements numeric methods through __coerce__ 7 class CoerceNumber: 8 def __init__(self, arg): 9 self.arg = arg 10 11 def __repr__(self): 12 return '<CoerceNumber %s>' % repr(self.arg) 13 14 def __coerce__(self, other): 15 if isinstance(other, CoerceNumber): 16 return self.arg, other.arg 17 else: 18 return (self.arg, other) 19 20 # New-style class version of CoerceNumber 21 class CoerceTo(object): 22 def __init__(self, arg): 23 self.arg = arg 24 def __coerce__(self, other): 25 if isinstance(other, CoerceTo): 26 return self.arg, other.arg 27 else: 28 return self.arg, other 29 30 31 # Fake a number that implements numeric ops through methods. 32 class MethodNumber: 33 def __init__(self,arg): 34 self.arg = arg 35 36 def __repr__(self): 37 return '<MethodNumber %s>' % repr(self.arg) 38 39 def __add__(self,other): 40 return self.arg + other 41 42 def __radd__(self,other): 43 return other + self.arg 44 45 def __sub__(self,other): 46 return self.arg - other 47 48 def __rsub__(self,other): 49 return other - self.arg 50 51 def __mul__(self,other): 52 return self.arg * other 53 54 def __rmul__(self,other): 55 return other * self.arg 56 57 def __div__(self,other): 58 return self.arg / other 59 60 def __rdiv__(self,other): 61 return other / self.arg 62 63 def __truediv__(self,other): 64 return self.arg / other 65 66 def __rtruediv__(self,other): 67 return other / self.arg 68 69 def __floordiv__(self,other): 70 return self.arg // other 71 72 def __rfloordiv__(self,other): 73 return other // self.arg 74 75 def __pow__(self,other): 76 return self.arg ** other 77 78 def __rpow__(self,other): 79 return other ** self.arg 80 81 def __mod__(self,other): 82 return self.arg % other 83 84 def __rmod__(self,other): 85 return other % self.arg 86 87 def __cmp__(self, other): 88 return cmp(self.arg, other) 89 90 91 candidates = [2, 2L, 4.0, 2+0j, [1], (2,), None, 92 MethodNumber(2), CoerceNumber(2)] 93 94 infix_binops = [ '+', '-', '*', '**', '%', '//', '/' ] 95 96 TE = TypeError 97 # b = both normal and augmented give same result list 98 # s = single result lists for normal and augmented 99 # e = equals other results 100 # result lists: ['+', '-', '*', '**', '%', '//', ('classic /', 'new /')] 101 # ^^^^^^^^^^^^^^^^^^^^^^ 102 # 2-tuple if results differ 103 # else only one value 104 infix_results = { 105 # 2 106 (0,0): ('b', [4, 0, 4, 4, 0, 1, (1, 1.0)]), 107 (0,1): ('e', (0,0)), 108 (0,2): ('b', [6.0, -2.0, 8.0, 16.0, 2.0, 0.0, 0.5]), 109 (0,3): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), 110 (0,4): ('b', [TE, TE, [1, 1], TE, TE, TE, TE]), 111 (0,5): ('b', [TE, TE, (2, 2), TE, TE, TE, TE]), 112 (0,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), 113 (0,7): ('e', (0,0)), 114 (0,8): ('e', (0,0)), 115 116 # 2L 117 (1,0): ('e', (0,0)), 118 (1,1): ('e', (0,1)), 119 (1,2): ('e', (0,2)), 120 (1,3): ('e', (0,3)), 121 (1,4): ('e', (0,4)), 122 (1,5): ('e', (0,5)), 123 (1,6): ('e', (0,6)), 124 (1,7): ('e', (0,7)), 125 (1,8): ('e', (0,8)), 126 127 # 4.0 128 (2,0): ('b', [6.0, 2.0, 8.0, 16.0, 0.0, 2.0, 2.0]), 129 (2,1): ('e', (2,0)), 130 (2,2): ('b', [8.0, 0.0, 16.0, 256.0, 0.0, 1.0, 1.0]), 131 (2,3): ('b', [6+0j, 2+0j, 8+0j, 16+0j, 0+0j, 2+0j, 2+0j]), 132 (2,4): ('b', [TE, TE, TE, TE, TE, TE, TE]), 133 (2,5): ('e', (2,4)), 134 (2,6): ('e', (2,4)), 135 (2,7): ('e', (2,0)), 136 (2,8): ('e', (2,0)), 137 138 # (2+0j) 139 (3,0): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), 140 (3,1): ('e', (3,0)), 141 (3,2): ('b', [6+0j, -2+0j, 8+0j, 16+0j, 2+0j, 0+0j, 0.5+0j]), 142 (3,3): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), 143 (3,4): ('b', [TE, TE, TE, TE, TE, TE, TE]), 144 (3,5): ('e', (3,4)), 145 (3,6): ('e', (3,4)), 146 (3,7): ('e', (3,0)), 147 (3,8): ('e', (3,0)), 148 149 # [1] 150 (4,0): ('b', [TE, TE, [1, 1], TE, TE, TE, TE]), 151 (4,1): ('e', (4,0)), 152 (4,2): ('b', [TE, TE, TE, TE, TE, TE, TE]), 153 (4,3): ('b', [TE, TE, TE, TE, TE, TE, TE]), 154 (4,4): ('b', [[1, 1], TE, TE, TE, TE, TE, TE]), 155 (4,5): ('s', [TE, TE, TE, TE, TE, TE, TE], [[1, 2], TE, TE, TE, TE, TE, TE]), 156 (4,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), 157 (4,7): ('e', (4,0)), 158 (4,8): ('e', (4,0)), 159 160 # (2,) 161 (5,0): ('b', [TE, TE, (2, 2), TE, TE, TE, TE]), 162 (5,1): ('e', (5,0)), 163 (5,2): ('b', [TE, TE, TE, TE, TE, TE, TE]), 164 (5,3): ('e', (5,2)), 165 (5,4): ('e', (5,2)), 166 (5,5): ('b', [(2, 2), TE, TE, TE, TE, TE, TE]), 167 (5,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), 168 (5,7): ('e', (5,0)), 169 (5,8): ('e', (5,0)), 170 171 # None 172 (6,0): ('b', [TE, TE, TE, TE, TE, TE, TE]), 173 (6,1): ('e', (6,0)), 174 (6,2): ('e', (6,0)), 175 (6,3): ('e', (6,0)), 176 (6,4): ('e', (6,0)), 177 (6,5): ('e', (6,0)), 178 (6,6): ('e', (6,0)), 179 (6,7): ('e', (6,0)), 180 (6,8): ('e', (6,0)), 181 182 # MethodNumber(2) 183 (7,0): ('e', (0,0)), 184 (7,1): ('e', (0,1)), 185 (7,2): ('e', (0,2)), 186 (7,3): ('e', (0,3)), 187 (7,4): ('e', (0,4)), 188 (7,5): ('e', (0,5)), 189 (7,6): ('e', (0,6)), 190 (7,7): ('e', (0,7)), 191 (7,8): ('e', (0,8)), 192 193 # CoerceNumber(2) 194 (8,0): ('e', (0,0)), 195 (8,1): ('e', (0,1)), 196 (8,2): ('e', (0,2)), 197 (8,3): ('e', (0,3)), 198 (8,4): ('e', (0,4)), 199 (8,5): ('e', (0,5)), 200 (8,6): ('e', (0,6)), 201 (8,7): ('e', (0,7)), 202 (8,8): ('e', (0,8)), 203 } 204 205 def process_infix_results(): 206 for key in sorted(infix_results): 207 val = infix_results[key] 208 if val[0] == 'e': 209 infix_results[key] = infix_results[val[1]] 210 else: 211 if val[0] == 's': 212 res = (val[1], val[2]) 213 elif val[0] == 'b': 214 res = (val[1], val[1]) 215 for i in range(1): 216 if isinstance(res[i][6], tuple): 217 if 1/2 == 0: 218 # testing with classic (floor) division 219 res[i][6] = res[i][6][0] 220 else: 221 # testing with -Qnew 222 res[i][6] = res[i][6][1] 223 infix_results[key] = res 224 225 226 with check_warnings(("classic (int|long) division", DeprecationWarning), 227 quiet=True): 228 process_infix_results() 229 # now infix_results has two lists of results for every pairing. 230 231 prefix_binops = [ 'divmod' ] 232 prefix_results = [ 233 [(1,0), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1,0)], 234 [(1L,0L), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1L,0L)], 235 [(2.0,0.0), (2.0,0.0), (1.0,0.0), ((2+0j),0j), TE, TE, TE, TE, (2.0,0.0)], 236 [((1+0j),0j), ((1+0j),0j), (0j,(2+0j)), ((1+0j),0j), TE, TE, TE, TE, ((1+0j),0j)], 237 [TE, TE, TE, TE, TE, TE, TE, TE, TE], 238 [TE, TE, TE, TE, TE, TE, TE, TE, TE], 239 [TE, TE, TE, TE, TE, TE, TE, TE, TE], 240 [TE, TE, TE, TE, TE, TE, TE, TE, TE], 241 [(1,0), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1,0)] 242 ] 243 244 def format_float(value): 245 if abs(value) < 0.01: 246 return '0.0' 247 else: 248 return '%.1f' % value 249 250 # avoid testing platform fp quirks 251 def format_result(value): 252 if isinstance(value, complex): 253 return '(%s + %sj)' % (format_float(value.real), 254 format_float(value.imag)) 255 elif isinstance(value, float): 256 return format_float(value) 257 return str(value) 258 259 class CoercionTest(unittest.TestCase): 260 def test_infix_binops(self): 261 for ia, a in enumerate(candidates): 262 for ib, b in enumerate(candidates): 263 results = infix_results[(ia, ib)] 264 for op, res, ires in zip(infix_binops, results[0], results[1]): 265 if res is TE: 266 self.assertRaises(TypeError, eval, 267 'a %s b' % op, {'a': a, 'b': b}) 268 else: 269 self.assertEqual(format_result(res), 270 format_result(eval('a %s b' % op)), 271 '%s %s %s == %s failed' % (a, op, b, res)) 272 try: 273 z = copy.copy(a) 274 except copy.Error: 275 z = a # assume it has no inplace ops 276 if ires is TE: 277 try: 278 exec 'z %s= b' % op 279 except TypeError: 280 pass 281 else: 282 self.fail("TypeError not raised") 283 else: 284 exec('z %s= b' % op) 285 self.assertEqual(ires, z) 286 287 def test_prefix_binops(self): 288 for ia, a in enumerate(candidates): 289 for ib, b in enumerate(candidates): 290 for op in prefix_binops: 291 res = prefix_results[ia][ib] 292 if res is TE: 293 self.assertRaises(TypeError, eval, 294 '%s(a, b)' % op, {'a': a, 'b': b}) 295 else: 296 self.assertEqual(format_result(res), 297 format_result(eval('%s(a, b)' % op)), 298 '%s(%s, %s) == %s failed' % (op, a, b, res)) 299 300 def test_cmptypes(self): 301 # Built-in tp_compare slots expect their arguments to have the 302 # same type, but a user-defined __coerce__ doesn't have to obey. 303 # SF #980352 304 evil_coercer = CoerceTo(42) 305 # Make sure these don't crash any more 306 self.assertNotEqual(cmp(u'fish', evil_coercer), 0) 307 self.assertNotEqual(cmp(slice(1), evil_coercer), 0) 308 # ...but that this still works 309 class WackyComparer(object): 310 def __cmp__(slf, other): 311 self.assertTrue(other == 42, 'expected evil_coercer, got %r' % other) 312 return 0 313 __hash__ = None # Invalid cmp makes this unhashable 314 self.assertEqual(cmp(WackyComparer(), evil_coercer), 0) 315 # ...and classic classes too, since that code path is a little different 316 class ClassicWackyComparer: 317 def __cmp__(slf, other): 318 self.assertTrue(other == 42, 'expected evil_coercer, got %r' % other) 319 return 0 320 self.assertEqual(cmp(ClassicWackyComparer(), evil_coercer), 0) 321 322 def test_infinite_rec_classic_classes(self): 323 # if __coerce__() returns its arguments reversed it causes an infinite 324 # recursion for classic classes. 325 class Tester: 326 def __coerce__(self, other): 327 return other, self 328 329 exc = TestFailed("__coerce__() returning its arguments reverse " 330 "should raise RuntimeError") 331 try: 332 Tester() + 1 333 except (RuntimeError, TypeError): 334 return 335 except: 336 raise exc 337 else: 338 raise exc 339 340 def test_main(): 341 with check_warnings(("complex divmod.., // and % are deprecated", 342 DeprecationWarning), 343 ("classic (int|long) division", DeprecationWarning), 344 quiet=True): 345 run_unittest(CoercionTest) 346 347 if __name__ == "__main__": 348 test_main() 349