1# tests for slice objects; in particular the indices method. 2 3import itertools 4import operator 5import sys 6import unittest 7import weakref 8 9from pickle import loads, dumps 10from test import support 11 12 13def 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 26def 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 64class MyIndexable(object): 65 def __init__(self, value): 66 self.value = value 67 68 def __index__(self): 69 return self.value 70 71 72class 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 254if __name__ == "__main__": 255 unittest.main() 256