test_coercion.py 11 KB

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