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