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