Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 1 | import copy |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 2 | import unittest |
Florent Xicluna | 6257a7b | 2010-03-31 22:01:03 +0000 | [diff] [blame] | 3 | from test.test_support import run_unittest, TestFailed, check_warnings |
Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 4 | |
Florent Xicluna | 96c4df4 | 2010-07-04 14:24:40 +0000 | [diff] [blame] | 5 | |
Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 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 | |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 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 | |
Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 30 | |
| 31 | # Fake a number that implements numeric ops through methods. |
| 32 | class MethodNumber: |
Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 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 | |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 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 | |
Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 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 | |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 91 | candidates = [2, 2L, 4.0, 2+0j, [1], (2,), None, |
| 92 | MethodNumber(2), CoerceNumber(2)] |
Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 93 | |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 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) |
Anthony Baxter | 4ef3a23 | 2006-03-30 12:59:11 +0000 | [diff] [blame] | 183 | (7,0): ('e', (0,0)), |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 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) |
Anthony Baxter | 4ef3a23 | 2006-03-30 12:59:11 +0000 | [diff] [blame] | 194 | (8,0): ('e', (0,0)), |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 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 | |
Anthony Baxter | 4ef3a23 | 2006-03-30 12:59:11 +0000 | [diff] [blame] | 225 | |
Florent Xicluna | 96c4df4 | 2010-07-04 14:24:40 +0000 | [diff] [blame] | 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 | |
Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 231 | prefix_binops = [ 'divmod' ] |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 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 | ] |
Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 243 | |
Neil Schemenauer | 6cbba50 | 2004-03-10 17:30:03 +0000 | [diff] [blame] | 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 | |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 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: |
Ezio Melotti | 2623a37 | 2010-11-21 13:34:58 +0000 | [diff] [blame^] | 269 | self.assertEqual(format_result(res), |
| 270 | format_result(eval('a %s b' % op)), |
| 271 | '%s %s %s == %s failed' % (a, op, b, res)) |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 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) |
Ezio Melotti | 2623a37 | 2010-11-21 13:34:58 +0000 | [diff] [blame^] | 285 | self.assertEqual(ires, z) |
Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 286 | |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 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: |
Ezio Melotti | 2623a37 | 2010-11-21 13:34:58 +0000 | [diff] [blame^] | 296 | self.assertEqual(format_result(res), |
| 297 | format_result(eval('%s(a, b)' % op)), |
| 298 | '%s(%s, %s) == %s failed' % (op, a, b, res)) |
Neil Schemenauer | fd288c7 | 2001-01-02 16:30:31 +0000 | [diff] [blame] | 299 | |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 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 |
Ezio Melotti | 2623a37 | 2010-11-21 13:34:58 +0000 | [diff] [blame^] | 306 | self.assertNotEqual(cmp(u'fish', evil_coercer), 0) |
| 307 | self.assertNotEqual(cmp(slice(1), evil_coercer), 0) |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 308 | # ...but that this still works |
| 309 | class WackyComparer(object): |
| 310 | def __cmp__(slf, other): |
Benjamin Peterson | 5c8da86 | 2009-06-30 22:57:08 +0000 | [diff] [blame] | 311 | self.assertTrue(other == 42, 'expected evil_coercer, got %r' % other) |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 312 | return 0 |
Nick Coghlan | 48361f5 | 2008-08-11 15:45:58 +0000 | [diff] [blame] | 313 | __hash__ = None # Invalid cmp makes this unhashable |
Ezio Melotti | 2623a37 | 2010-11-21 13:34:58 +0000 | [diff] [blame^] | 314 | self.assertEqual(cmp(WackyComparer(), evil_coercer), 0) |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 315 | # ...and classic classes too, since that code path is a little different |
| 316 | class ClassicWackyComparer: |
| 317 | def __cmp__(slf, other): |
Benjamin Peterson | 5c8da86 | 2009-06-30 22:57:08 +0000 | [diff] [blame] | 318 | self.assertTrue(other == 42, 'expected evil_coercer, got %r' % other) |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 319 | return 0 |
Ezio Melotti | 2623a37 | 2010-11-21 13:34:58 +0000 | [diff] [blame^] | 320 | self.assertEqual(cmp(ClassicWackyComparer(), evil_coercer), 0) |
Armin Rigo | a174813 | 2004-12-23 22:13:13 +0000 | [diff] [blame] | 321 | |
Brett Cannon | 5dc3e3f | 2006-06-13 22:26:13 +0000 | [diff] [blame] | 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 | |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 340 | def test_main(): |
Florent Xicluna | 6257a7b | 2010-03-31 22:01:03 +0000 | [diff] [blame] | 341 | with check_warnings(("complex divmod.., // and % are deprecated", |
| 342 | DeprecationWarning), |
| 343 | ("classic (int|long) division", DeprecationWarning), |
| 344 | quiet=True): |
Ezio Melotti | ef49096 | 2010-01-31 11:46:54 +0000 | [diff] [blame] | 345 | run_unittest(CoercionTest) |
Armin Rigo | a174813 | 2004-12-23 22:13:13 +0000 | [diff] [blame] | 346 | |
Georg Brandl | 686eaeb | 2006-03-28 10:00:53 +0000 | [diff] [blame] | 347 | if __name__ == "__main__": |
| 348 | test_main() |