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