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