Home | History | Annotate | Download | only in test
      1 # tests for slice objects; in particular the indices method.
      2 
      3 import itertools
      4 import operator
      5 import sys
      6 import unittest
      7 import weakref
      8 
      9 from pickle import loads, dumps
     10 from test import support
     11 
     12 
     13 def evaluate_slice_index(arg):
     14     """
     15     Helper function to convert a slice argument to an integer, and raise
     16     TypeError with a suitable message on failure.
     17 
     18     """
     19     if hasattr(arg, '__index__'):
     20         return operator.index(arg)
     21     else:
     22         raise TypeError(
     23             "slice indices must be integers or "
     24             "None or have an __index__ method")
     25 
     26 def slice_indices(slice, length):
     27     """
     28     Reference implementation for the slice.indices method.
     29 
     30     """
     31     # Compute step and length as integers.
     32     length = operator.index(length)
     33     step = 1 if slice.step is None else evaluate_slice_index(slice.step)
     34 
     35     # Raise ValueError for negative length or zero step.
     36     if length < 0:
     37         raise ValueError("length should not be negative")
     38     if step == 0:
     39         raise ValueError("slice step cannot be zero")
     40 
     41     # Find lower and upper bounds for start and stop.
     42     lower = -1 if step < 0 else 0
     43     upper = length - 1 if step < 0 else length
     44 
     45     # Compute start.
     46     if slice.start is None:
     47         start = upper if step < 0 else lower
     48     else:
     49         start = evaluate_slice_index(slice.start)
     50         start = max(start + length, lower) if start < 0 else min(start, upper)
     51 
     52     # Compute stop.
     53     if slice.stop is None:
     54         stop = lower if step < 0 else upper
     55     else:
     56         stop = evaluate_slice_index(slice.stop)
     57         stop = max(stop + length, lower) if stop < 0 else min(stop, upper)
     58 
     59     return start, stop, step
     60 
     61 
     62 # Class providing an __index__ method.  Used for testing slice.indices.
     63 
     64 class MyIndexable(object):
     65     def __init__(self, value):
     66         self.value = value
     67 
     68     def __index__(self):
     69         return self.value
     70 
     71 
     72 class SliceTest(unittest.TestCase):
     73 
     74     def test_constructor(self):
     75         self.assertRaises(TypeError, slice)
     76         self.assertRaises(TypeError, slice, 1, 2, 3, 4)
     77 
     78     def test_repr(self):
     79         self.assertEqual(repr(slice(1, 2, 3)), "slice(1, 2, 3)")
     80 
     81     def test_hash(self):
     82         # Verify clearing of SF bug #800796
     83         self.assertRaises(TypeError, hash, slice(5))
     84         with self.assertRaises(TypeError):
     85             slice(5).__hash__()
     86 
     87     def test_cmp(self):
     88         s1 = slice(1, 2, 3)
     89         s2 = slice(1, 2, 3)
     90         s3 = slice(1, 2, 4)
     91         self.assertEqual(s1, s2)
     92         self.assertNotEqual(s1, s3)
     93         self.assertNotEqual(s1, None)
     94         self.assertNotEqual(s1, (1, 2, 3))
     95         self.assertNotEqual(s1, "")
     96 
     97         class Exc(Exception):
     98             pass
     99 
    100         class BadCmp(object):
    101             def __eq__(self, other):
    102                 raise Exc
    103 
    104         s1 = slice(BadCmp())
    105         s2 = slice(BadCmp())
    106         self.assertEqual(s1, s1)
    107         self.assertRaises(Exc, lambda: s1 == s2)
    108 
    109         s1 = slice(1, BadCmp())
    110         s2 = slice(1, BadCmp())
    111         self.assertEqual(s1, s1)
    112         self.assertRaises(Exc, lambda: s1 == s2)
    113 
    114         s1 = slice(1, 2, BadCmp())
    115         s2 = slice(1, 2, BadCmp())
    116         self.assertEqual(s1, s1)
    117         self.assertRaises(Exc, lambda: s1 == s2)
    118 
    119     def test_members(self):
    120         s = slice(1)
    121         self.assertEqual(s.start, None)
    122         self.assertEqual(s.stop, 1)
    123         self.assertEqual(s.step, None)
    124 
    125         s = slice(1, 2)
    126         self.assertEqual(s.start, 1)
    127         self.assertEqual(s.stop, 2)
    128         self.assertEqual(s.step, None)
    129 
    130         s = slice(1, 2, 3)
    131         self.assertEqual(s.start, 1)
    132         self.assertEqual(s.stop, 2)
    133         self.assertEqual(s.step, 3)
    134 
    135         class AnyClass:
    136             pass
    137 
    138         obj = AnyClass()
    139         s = slice(obj)
    140         self.assertTrue(s.stop is obj)
    141 
    142     def check_indices(self, slice, length):
    143         try:
    144             actual = slice.indices(length)
    145         except ValueError:
    146             actual = "valueerror"
    147         try:
    148             expected = slice_indices(slice, length)
    149         except ValueError:
    150             expected = "valueerror"
    151         self.assertEqual(actual, expected)
    152 
    153         if length >= 0 and slice.step != 0:
    154             actual = range(*slice.indices(length))
    155             expected = range(length)[slice]
    156             self.assertEqual(actual, expected)
    157 
    158     def test_indices(self):
    159         self.assertEqual(slice(None           ).indices(10), (0, 10,  1))
    160         self.assertEqual(slice(None,  None,  2).indices(10), (0, 10,  2))
    161         self.assertEqual(slice(1,     None,  2).indices(10), (1, 10,  2))
    162         self.assertEqual(slice(None,  None, -1).indices(10), (9, -1, -1))
    163         self.assertEqual(slice(None,  None, -2).indices(10), (9, -1, -2))
    164         self.assertEqual(slice(3,     None, -2).indices(10), (3, -1, -2))
    165         # issue 3004 tests
    166         self.assertEqual(slice(None, -9).indices(10), (0, 1, 1))
    167         self.assertEqual(slice(None, -10).indices(10), (0, 0, 1))
    168         self.assertEqual(slice(None, -11).indices(10), (0, 0, 1))
    169         self.assertEqual(slice(None, -10, -1).indices(10), (9, 0, -1))
    170         self.assertEqual(slice(None, -11, -1).indices(10), (9, -1, -1))
    171         self.assertEqual(slice(None, -12, -1).indices(10), (9, -1, -1))
    172         self.assertEqual(slice(None, 9).indices(10), (0, 9, 1))
    173         self.assertEqual(slice(None, 10).indices(10), (0, 10, 1))
    174         self.assertEqual(slice(None, 11).indices(10), (0, 10, 1))
    175         self.assertEqual(slice(None, 8, -1).indices(10), (9, 8, -1))
    176         self.assertEqual(slice(None, 9, -1).indices(10), (9, 9, -1))
    177         self.assertEqual(slice(None, 10, -1).indices(10), (9, 9, -1))
    178 
    179         self.assertEqual(
    180             slice(-100,  100     ).indices(10),
    181             slice(None).indices(10)
    182         )
    183         self.assertEqual(
    184             slice(100,  -100,  -1).indices(10),
    185             slice(None, None, -1).indices(10)
    186         )
    187         self.assertEqual(slice(-100, 100, 2).indices(10), (0, 10,  2))
    188 
    189         self.assertEqual(list(range(10))[::sys.maxsize - 1], [0])
    190 
    191         # Check a variety of start, stop, step and length values, including
    192         # values exceeding sys.maxsize (see issue #14794).
    193         vals = [None, -2**100, -2**30, -53, -7, -1, 0, 1, 7, 53, 2**30, 2**100]
    194         lengths = [0, 1, 7, 53, 2**30, 2**100]
    195         for slice_args in itertools.product(vals, repeat=3):
    196             s = slice(*slice_args)
    197             for length in lengths:
    198                 self.check_indices(s, length)
    199         self.check_indices(slice(0, 10, 1), -3)
    200 
    201         # Negative length should raise ValueError
    202         with self.assertRaises(ValueError):
    203             slice(None).indices(-1)
    204 
    205         # Zero step should raise ValueError
    206         with self.assertRaises(ValueError):
    207             slice(0, 10, 0).indices(5)
    208 
    209         # Using a start, stop or step or length that can't be interpreted as an
    210         # integer should give a TypeError ...
    211         with self.assertRaises(TypeError):
    212             slice(0.0, 10, 1).indices(5)
    213         with self.assertRaises(TypeError):
    214             slice(0, 10.0, 1).indices(5)
    215         with self.assertRaises(TypeError):
    216             slice(0, 10, 1.0).indices(5)
    217         with self.assertRaises(TypeError):
    218             slice(0, 10, 1).indices(5.0)
    219 
    220         # ... but it should be fine to use a custom class that provides index.
    221         self.assertEqual(slice(0, 10, 1).indices(5), (0, 5, 1))
    222         self.assertEqual(slice(MyIndexable(0), 10, 1).indices(5), (0, 5, 1))
    223         self.assertEqual(slice(0, MyIndexable(10), 1).indices(5), (0, 5, 1))
    224         self.assertEqual(slice(0, 10, MyIndexable(1)).indices(5), (0, 5, 1))
    225         self.assertEqual(slice(0, 10, 1).indices(MyIndexable(5)), (0, 5, 1))
    226 
    227     def test_setslice_without_getslice(self):
    228         tmp = []
    229         class X(object):
    230             def __setitem__(self, i, k):
    231                 tmp.append((i, k))
    232 
    233         x = X()
    234         x[1:2] = 42
    235         self.assertEqual(tmp, [(slice(1, 2), 42)])
    236 
    237     def test_pickle(self):
    238         s = slice(10, 20, 3)
    239         for protocol in (0,1,2):
    240             t = loads(dumps(s, protocol))
    241             self.assertEqual(s, t)
    242             self.assertEqual(s.indices(15), t.indices(15))
    243             self.assertNotEqual(id(s), id(t))
    244 
    245     def test_cycle(self):
    246         class myobj(): pass
    247         o = myobj()
    248         o.s = slice(o)
    249         w = weakref.ref(o)
    250         o = None
    251         support.gc_collect()
    252         self.assertIsNone(w())
    253 
    254 if __name__ == "__main__":
    255     unittest.main()
    256