move sections
[python/dscho.git] / Lib / test / test_coercion.py
blob8a74b5159f3c9a2d1288dbbe4efb90d670b64ae9
1 import copy
2 import unittest
3 from test.test_support import run_unittest, TestFailed, check_warnings
5 # Fake a number that implements numeric methods through __coerce__
6 class CoerceNumber:
7 def __init__(self, arg):
8 self.arg = arg
10 def __repr__(self):
11 return '<CoerceNumber %s>' % repr(self.arg)
13 def __coerce__(self, other):
14 if isinstance(other, CoerceNumber):
15 return self.arg, other.arg
16 else:
17 return (self.arg, other)
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
30 # Fake a number that implements numeric ops through methods.
31 class MethodNumber:
32 def __init__(self,arg):
33 self.arg = arg
35 def __repr__(self):
36 return '<MethodNumber %s>' % repr(self.arg)
38 def __add__(self,other):
39 return self.arg + other
41 def __radd__(self,other):
42 return other + self.arg
44 def __sub__(self,other):
45 return self.arg - other
47 def __rsub__(self,other):
48 return other - self.arg
50 def __mul__(self,other):
51 return self.arg * other
53 def __rmul__(self,other):
54 return other * self.arg
56 def __div__(self,other):
57 return self.arg / other
59 def __rdiv__(self,other):
60 return other / self.arg
62 def __truediv__(self,other):
63 return self.arg / other
65 def __rtruediv__(self,other):
66 return other / self.arg
68 def __floordiv__(self,other):
69 return self.arg // other
71 def __rfloordiv__(self,other):
72 return other // self.arg
74 def __pow__(self,other):
75 return self.arg ** other
77 def __rpow__(self,other):
78 return other ** self.arg
80 def __mod__(self,other):
81 return self.arg % other
83 def __rmod__(self,other):
84 return other % self.arg
86 def __cmp__(self, other):
87 return cmp(self.arg, other)
90 candidates = [2, 2L, 4.0, 2+0j, [1], (2,), None,
91 MethodNumber(2), CoerceNumber(2)]
93 infix_binops = [ '+', '-', '*', '**', '%', '//', '/' ]
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 = {
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)),
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)),
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)),
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)),
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)),
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)),
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)),
181 # MethodNumber(2)
182 (7,0): ('e', (0,0)),
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)),
192 # CoerceNumber(2)
193 (8,0): ('e', (0,0)),
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)),
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
225 prefix_binops = [ 'divmod' ]
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)]
238 def format_float(value):
239 if abs(value) < 0.01:
240 return '0.0'
241 else:
242 return '%.1f' % value
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)
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)
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))
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.assertTrue(other == 42, 'expected evil_coercer, got %r' % other)
306 return 0
307 __hash__ = None # Invalid cmp makes this unhashable
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.assertTrue(other == 42, 'expected evil_coercer, got %r' % other)
313 return 0
314 self.assertEquals(cmp(ClassicWackyComparer(), evil_coercer), 0)
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
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
334 def test_main():
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.
342 run_unittest(CoercionTest)
344 if __name__ == "__main__":
345 test_main()