|
1 import copy |
|
2 import sys |
|
3 import warnings |
|
4 import unittest |
|
5 from test.test_support import run_unittest, TestFailed |
|
6 |
|
7 # Fake a number that implements numeric methods through __coerce__ |
|
8 class CoerceNumber: |
|
9 def __init__(self, arg): |
|
10 self.arg = arg |
|
11 |
|
12 def __repr__(self): |
|
13 return '<CoerceNumber %s>' % repr(self.arg) |
|
14 |
|
15 def __coerce__(self, other): |
|
16 if isinstance(other, CoerceNumber): |
|
17 return self.arg, other.arg |
|
18 else: |
|
19 return (self.arg, other) |
|
20 |
|
21 # New-style class version of CoerceNumber |
|
22 class CoerceTo(object): |
|
23 def __init__(self, arg): |
|
24 self.arg = arg |
|
25 def __coerce__(self, other): |
|
26 if isinstance(other, CoerceTo): |
|
27 return self.arg, other.arg |
|
28 else: |
|
29 return self.arg, other |
|
30 |
|
31 |
|
32 # Fake a number that implements numeric ops through methods. |
|
33 class MethodNumber: |
|
34 def __init__(self,arg): |
|
35 self.arg = arg |
|
36 |
|
37 def __repr__(self): |
|
38 return '<MethodNumber %s>' % repr(self.arg) |
|
39 |
|
40 def __add__(self,other): |
|
41 return self.arg + other |
|
42 |
|
43 def __radd__(self,other): |
|
44 return other + self.arg |
|
45 |
|
46 def __sub__(self,other): |
|
47 return self.arg - other |
|
48 |
|
49 def __rsub__(self,other): |
|
50 return other - self.arg |
|
51 |
|
52 def __mul__(self,other): |
|
53 return self.arg * other |
|
54 |
|
55 def __rmul__(self,other): |
|
56 return other * self.arg |
|
57 |
|
58 def __div__(self,other): |
|
59 return self.arg / other |
|
60 |
|
61 def __rdiv__(self,other): |
|
62 return other / self.arg |
|
63 |
|
64 def __truediv__(self,other): |
|
65 return self.arg / other |
|
66 |
|
67 def __rtruediv__(self,other): |
|
68 return other / self.arg |
|
69 |
|
70 def __floordiv__(self,other): |
|
71 return self.arg // other |
|
72 |
|
73 def __rfloordiv__(self,other): |
|
74 return other // self.arg |
|
75 |
|
76 def __pow__(self,other): |
|
77 return self.arg ** other |
|
78 |
|
79 def __rpow__(self,other): |
|
80 return other ** self.arg |
|
81 |
|
82 def __mod__(self,other): |
|
83 return self.arg % other |
|
84 |
|
85 def __rmod__(self,other): |
|
86 return other % self.arg |
|
87 |
|
88 def __cmp__(self, other): |
|
89 return cmp(self.arg, other) |
|
90 |
|
91 |
|
92 candidates = [2, 2L, 4.0, 2+0j, [1], (2,), None, |
|
93 MethodNumber(2), CoerceNumber(2)] |
|
94 |
|
95 infix_binops = [ '+', '-', '*', '**', '%', '//', '/' ] |
|
96 |
|
97 TE = TypeError |
|
98 # b = both normal and augmented give same result list |
|
99 # s = single result lists for normal and augmented |
|
100 # e = equals other results |
|
101 # result lists: ['+', '-', '*', '**', '%', '//', ('classic /', 'new /')] |
|
102 # ^^^^^^^^^^^^^^^^^^^^^^ |
|
103 # 2-tuple if results differ |
|
104 # else only one value |
|
105 infix_results = { |
|
106 # 2 |
|
107 (0,0): ('b', [4, 0, 4, 4, 0, 1, (1, 1.0)]), |
|
108 (0,1): ('e', (0,0)), |
|
109 (0,2): ('b', [6.0, -2.0, 8.0, 16.0, 2.0, 0.0, 0.5]), |
|
110 (0,3): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), |
|
111 (0,4): ('b', [TE, TE, [1, 1], TE, TE, TE, TE]), |
|
112 (0,5): ('b', [TE, TE, (2, 2), TE, TE, TE, TE]), |
|
113 (0,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), |
|
114 (0,7): ('e', (0,0)), |
|
115 (0,8): ('e', (0,0)), |
|
116 |
|
117 # 2L |
|
118 (1,0): ('e', (0,0)), |
|
119 (1,1): ('e', (0,1)), |
|
120 (1,2): ('e', (0,2)), |
|
121 (1,3): ('e', (0,3)), |
|
122 (1,4): ('e', (0,4)), |
|
123 (1,5): ('e', (0,5)), |
|
124 (1,6): ('e', (0,6)), |
|
125 (1,7): ('e', (0,7)), |
|
126 (1,8): ('e', (0,8)), |
|
127 |
|
128 # 4.0 |
|
129 (2,0): ('b', [6.0, 2.0, 8.0, 16.0, 0.0, 2.0, 2.0]), |
|
130 (2,1): ('e', (2,0)), |
|
131 (2,2): ('b', [8.0, 0.0, 16.0, 256.0, 0.0, 1.0, 1.0]), |
|
132 (2,3): ('b', [6+0j, 2+0j, 8+0j, 16+0j, 0+0j, 2+0j, 2+0j]), |
|
133 (2,4): ('b', [TE, TE, TE, TE, TE, TE, TE]), |
|
134 (2,5): ('e', (2,4)), |
|
135 (2,6): ('e', (2,4)), |
|
136 (2,7): ('e', (2,0)), |
|
137 (2,8): ('e', (2,0)), |
|
138 |
|
139 # (2+0j) |
|
140 (3,0): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), |
|
141 (3,1): ('e', (3,0)), |
|
142 (3,2): ('b', [6+0j, -2+0j, 8+0j, 16+0j, 2+0j, 0+0j, 0.5+0j]), |
|
143 (3,3): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), |
|
144 (3,4): ('b', [TE, TE, TE, TE, TE, TE, TE]), |
|
145 (3,5): ('e', (3,4)), |
|
146 (3,6): ('e', (3,4)), |
|
147 (3,7): ('e', (3,0)), |
|
148 (3,8): ('e', (3,0)), |
|
149 |
|
150 # [1] |
|
151 (4,0): ('b', [TE, TE, [1, 1], TE, TE, TE, TE]), |
|
152 (4,1): ('e', (4,0)), |
|
153 (4,2): ('b', [TE, TE, TE, TE, TE, TE, TE]), |
|
154 (4,3): ('b', [TE, TE, TE, TE, TE, TE, TE]), |
|
155 (4,4): ('b', [[1, 1], TE, TE, TE, TE, TE, TE]), |
|
156 (4,5): ('s', [TE, TE, TE, TE, TE, TE, TE], [[1, 2], TE, TE, TE, TE, TE, TE]), |
|
157 (4,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), |
|
158 (4,7): ('e', (4,0)), |
|
159 (4,8): ('e', (4,0)), |
|
160 |
|
161 # (2,) |
|
162 (5,0): ('b', [TE, TE, (2, 2), TE, TE, TE, TE]), |
|
163 (5,1): ('e', (5,0)), |
|
164 (5,2): ('b', [TE, TE, TE, TE, TE, TE, TE]), |
|
165 (5,3): ('e', (5,2)), |
|
166 (5,4): ('e', (5,2)), |
|
167 (5,5): ('b', [(2, 2), TE, TE, TE, TE, TE, TE]), |
|
168 (5,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), |
|
169 (5,7): ('e', (5,0)), |
|
170 (5,8): ('e', (5,0)), |
|
171 |
|
172 # None |
|
173 (6,0): ('b', [TE, TE, TE, TE, TE, TE, TE]), |
|
174 (6,1): ('e', (6,0)), |
|
175 (6,2): ('e', (6,0)), |
|
176 (6,3): ('e', (6,0)), |
|
177 (6,4): ('e', (6,0)), |
|
178 (6,5): ('e', (6,0)), |
|
179 (6,6): ('e', (6,0)), |
|
180 (6,7): ('e', (6,0)), |
|
181 (6,8): ('e', (6,0)), |
|
182 |
|
183 # MethodNumber(2) |
|
184 (7,0): ('e', (0,0)), |
|
185 (7,1): ('e', (0,1)), |
|
186 (7,2): ('e', (0,2)), |
|
187 (7,3): ('e', (0,3)), |
|
188 (7,4): ('e', (0,4)), |
|
189 (7,5): ('e', (0,5)), |
|
190 (7,6): ('e', (0,6)), |
|
191 (7,7): ('e', (0,7)), |
|
192 (7,8): ('e', (0,8)), |
|
193 |
|
194 # CoerceNumber(2) |
|
195 (8,0): ('e', (0,0)), |
|
196 (8,1): ('e', (0,1)), |
|
197 (8,2): ('e', (0,2)), |
|
198 (8,3): ('e', (0,3)), |
|
199 (8,4): ('e', (0,4)), |
|
200 (8,5): ('e', (0,5)), |
|
201 (8,6): ('e', (0,6)), |
|
202 (8,7): ('e', (0,7)), |
|
203 (8,8): ('e', (0,8)), |
|
204 } |
|
205 |
|
206 def process_infix_results(): |
|
207 for key in sorted(infix_results): |
|
208 val = infix_results[key] |
|
209 if val[0] == 'e': |
|
210 infix_results[key] = infix_results[val[1]] |
|
211 else: |
|
212 if val[0] == 's': |
|
213 res = (val[1], val[2]) |
|
214 elif val[0] == 'b': |
|
215 res = (val[1], val[1]) |
|
216 for i in range(1): |
|
217 if isinstance(res[i][6], tuple): |
|
218 if 1/2 == 0: |
|
219 # testing with classic (floor) division |
|
220 res[i][6] = res[i][6][0] |
|
221 else: |
|
222 # testing with -Qnew |
|
223 res[i][6] = res[i][6][1] |
|
224 infix_results[key] = res |
|
225 |
|
226 |
|
227 |
|
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.assertEquals(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.assertEquals(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.assertEquals(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.assertNotEquals(cmp(u'fish', evil_coercer), 0) |
|
307 self.assertNotEquals(cmp(slice(1), evil_coercer), 0) |
|
308 # ...but that this still works |
|
309 class WackyComparer(object): |
|
310 def __cmp__(slf, other): |
|
311 self.assert_(other == 42, 'expected evil_coercer, got %r' % other) |
|
312 return 0 |
|
313 self.assertEquals(cmp(WackyComparer(), evil_coercer), 0) |
|
314 # ...and classic classes too, since that code path is a little different |
|
315 class ClassicWackyComparer: |
|
316 def __cmp__(slf, other): |
|
317 self.assert_(other == 42, 'expected evil_coercer, got %r' % other) |
|
318 return 0 |
|
319 self.assertEquals(cmp(ClassicWackyComparer(), evil_coercer), 0) |
|
320 |
|
321 def test_infinite_rec_classic_classes(self): |
|
322 # if __coerce__() returns its arguments reversed it causes an infinite |
|
323 # recursion for classic classes. |
|
324 class Tester: |
|
325 def __coerce__(self, other): |
|
326 return other, self |
|
327 |
|
328 exc = TestFailed("__coerce__() returning its arguments reverse " |
|
329 "should raise RuntimeError") |
|
330 try: |
|
331 Tester() + 1 |
|
332 except (RuntimeError, TypeError): |
|
333 return |
|
334 except: |
|
335 raise exc |
|
336 else: |
|
337 raise exc |
|
338 |
|
339 def test_main(): |
|
340 warnings.filterwarnings("ignore", |
|
341 r'complex divmod\(\), // and % are deprecated', |
|
342 DeprecationWarning, |
|
343 r'test.test_coercion$') |
|
344 run_unittest(CoercionTest) |
|
345 |
|
346 if __name__ == "__main__": |
|
347 test_main() |