4 from test
.test_support
import run_unittest
, TestFailed
6 # Fake a number that implements numeric methods through __coerce__
8 def __init__(self
, arg
):
12 return '<CoerceNumber %s>' % repr(self
.arg
)
14 def __coerce__(self
, other
):
15 if isinstance(other
, CoerceNumber
):
16 return self
.arg
, other
.arg
18 return (self
.arg
, other
)
20 # New-style class version of CoerceNumber
21 class CoerceTo(object):
22 def __init__(self
, arg
):
24 def __coerce__(self
, other
):
25 if isinstance(other
, CoerceTo
):
26 return self
.arg
, other
.arg
28 return self
.arg
, other
31 # Fake a number that implements numeric ops through methods.
33 def __init__(self
,arg
):
37 return '<MethodNumber %s>' % repr(self
.arg
)
39 def __add__(self
,other
):
40 return self
.arg
+ other
42 def __radd__(self
,other
):
43 return other
+ self
.arg
45 def __sub__(self
,other
):
46 return self
.arg
- other
48 def __rsub__(self
,other
):
49 return other
- self
.arg
51 def __mul__(self
,other
):
52 return self
.arg
* other
54 def __rmul__(self
,other
):
55 return other
* self
.arg
57 def __div__(self
,other
):
58 return self
.arg
/ other
60 def __rdiv__(self
,other
):
61 return other
/ self
.arg
63 def __truediv__(self
,other
):
64 return self
.arg
/ other
66 def __rtruediv__(self
,other
):
67 return other
/ self
.arg
69 def __floordiv__(self
,other
):
70 return self
.arg
// other
72 def __rfloordiv__(self
,other
):
73 return other
// self
.arg
75 def __pow__(self
,other
):
76 return self
.arg
** other
78 def __rpow__(self
,other
):
79 return other
** self
.arg
81 def __mod__(self
,other
):
82 return self
.arg
% other
84 def __rmod__(self
,other
):
85 return other
% self
.arg
87 def __cmp__(self
, other
):
88 return cmp(self
.arg
, other
)
91 candidates
= [2, 2L, 4.0, 2+0j
, [1], (2,), None,
92 MethodNumber(2), CoerceNumber(2)]
94 infix_binops
= [ '+', '-', '*', '**', '%', '//', '/' ]
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
106 (0,0): ('b', [4, 0, 4, 4, 0, 1, (1, 1.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
]),
128 (2,0): ('b', [6.0, 2.0, 8.0, 16.0, 0.0, 2.0, 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
]),
139 (3,0): ('b', [4+0j
, 0+0j
, 4+0j
, 4+0j
, 0+0j
, 1+0j
, 1+0j
]),
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
]),
150 (4,0): ('b', [TE
, TE
, [1, 1], TE
, TE
, TE
, TE
]),
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
]),
161 (5,0): ('b', [TE
, TE
, (2, 2), TE
, TE
, TE
, TE
]),
163 (5,2): ('b', [TE
, TE
, TE
, TE
, TE
, TE
, TE
]),
166 (5,5): ('b', [(2, 2), TE
, TE
, TE
, TE
, TE
, TE
]),
167 (5,6): ('b', [TE
, TE
, TE
, TE
, TE
, TE
, TE
]),
172 (6,0): ('b', [TE
, TE
, TE
, TE
, TE
, TE
, TE
]),
205 def process_infix_results():
206 for key
in sorted(infix_results
):
207 val
= infix_results
[key
]
209 infix_results
[key
] = infix_results
[val
[1]]
212 res
= (val
[1], val
[2])
214 res
= (val
[1], val
[1])
216 if isinstance(res
[i
][6], tuple):
218 # testing with classic (floor) division
219 res
[i
][6] = res
[i
][6][0]
222 res
[i
][6] = res
[i
][6][1]
223 infix_results
[key
] = res
227 process_infix_results()
228 # now infix_results has two lists of results for every pairing.
230 prefix_binops
= [ 'divmod' ]
232 [(1,0), (1L,0L), (0.0,2.0), ((1+0j
),0j
), TE
, TE
, TE
, TE
, (1,0)],
233 [(1L,0L), (1L,0L), (0.0,2.0), ((1+0j
),0j
), TE
, TE
, TE
, TE
, (1L,0L)],
234 [(2.0,0.0), (2.0,0.0), (1.0,0.0), ((2+0j
),0j
), TE
, TE
, TE
, TE
, (2.0,0.0)],
235 [((1+0j
),0j
), ((1+0j
),0j
), (0j
,(2+0j
)), ((1+0j
),0j
), TE
, TE
, TE
, TE
, ((1+0j
),0j
)],
236 [TE
, TE
, TE
, TE
, TE
, TE
, TE
, TE
, TE
],
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 [(1,0), (1L,0L), (0.0,2.0), ((1+0j
),0j
), TE
, TE
, TE
, TE
, (1,0)]
243 def format_float(value
):
244 if abs(value
) < 0.01:
247 return '%.1f' % value
249 # avoid testing platform fp quirks
250 def format_result(value
):
251 if isinstance(value
, complex):
252 return '(%s + %sj)' % (format_float(value
.real
),
253 format_float(value
.imag
))
254 elif isinstance(value
, float):
255 return format_float(value
)
258 class CoercionTest(unittest
.TestCase
):
259 def test_infix_binops(self
):
260 for ia
, a
in enumerate(candidates
):
261 for ib
, b
in enumerate(candidates
):
262 results
= infix_results
[(ia
, ib
)]
263 for op
, res
, ires
in zip(infix_binops
, results
[0], results
[1]):
265 self
.assertRaises(TypeError, eval,
266 'a %s b' % op
, {'a': a
, 'b': b
})
268 self
.assertEquals(format_result(res
),
269 format_result(eval('a %s b' % op
)),
270 '%s %s %s == %s failed' % (a
, op
, b
, res
))
274 z
= a
# assume it has no inplace ops
281 self
.fail("TypeError not raised")
284 self
.assertEquals(ires
, z
)
286 def test_prefix_binops(self
):
287 for ia
, a
in enumerate(candidates
):
288 for ib
, b
in enumerate(candidates
):
289 for op
in prefix_binops
:
290 res
= prefix_results
[ia
][ib
]
292 self
.assertRaises(TypeError, eval,
293 '%s(a, b)' % op
, {'a': a
, 'b': b
})
295 self
.assertEquals(format_result(res
),
296 format_result(eval('%s(a, b)' % op
)),
297 '%s(%s, %s) == %s failed' % (op
, a
, b
, res
))
299 def test_cmptypes(self
):
300 # Built-in tp_compare slots expect their arguments to have the
301 # same type, but a user-defined __coerce__ doesn't have to obey.
303 evil_coercer
= CoerceTo(42)
304 # Make sure these don't crash any more
305 self
.assertNotEquals(cmp(u
'fish', evil_coercer
), 0)
306 self
.assertNotEquals(cmp(slice(1), evil_coercer
), 0)
307 # ...but that this still works
308 class WackyComparer(object):
309 def __cmp__(slf
, other
):
310 self
.assertTrue(other
== 42, 'expected evil_coercer, got %r' % other
)
312 __hash__
= None # Invalid cmp makes this unhashable
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
.assertTrue(other
== 42, 'expected evil_coercer, got %r' % other
)
319 self
.assertEquals(cmp(ClassicWackyComparer(), evil_coercer
), 0)
321 def test_infinite_rec_classic_classes(self
):
322 # if __coerce__() returns its arguments reversed it causes an infinite
323 # recursion for classic classes.
325 def __coerce__(self
, other
):
328 exc
= TestFailed("__coerce__() returning its arguments reverse "
329 "should raise RuntimeError")
332 except (RuntimeError, TypeError):
340 warnings
.filterwarnings("ignore",
341 r
'complex divmod\(\), // and % are deprecated',
343 r
'test.test_coercion$')
344 run_unittest(CoercionTest
)
346 if __name__
== "__main__":