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 import copy
9
10 from pickle import loads, dumps
11 from test import support
12
13
14 def 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
27 def 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
65 class ESC[4;38;5;81mMyIndexable(ESC[4;38;5;149mobject):
66 def __init__(self, value):
67 self.value = value
68
69 def __index__(self):
70 return self.value
71
72
73 class ESC[4;38;5;81mSliceTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
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 self.assertEqual(hash(slice(5)), slice(5).__hash__())
84 self.assertEqual(hash(slice(1, 2)), slice(1, 2).__hash__())
85 self.assertEqual(hash(slice(1, 2, 3)), slice(1, 2, 3).__hash__())
86 self.assertNotEqual(slice(5), slice(6))
87
88 with self.assertRaises(TypeError):
89 hash(slice(1, 2, []))
90
91 with self.assertRaises(TypeError):
92 hash(slice(4, {}))
93
94 def test_cmp(self):
95 s1 = slice(1, 2, 3)
96 s2 = slice(1, 2, 3)
97 s3 = slice(1, 2, 4)
98 self.assertEqual(s1, s2)
99 self.assertNotEqual(s1, s3)
100 self.assertNotEqual(s1, None)
101 self.assertNotEqual(s1, (1, 2, 3))
102 self.assertNotEqual(s1, "")
103
104 class ESC[4;38;5;81mExc(ESC[4;38;5;149mException):
105 pass
106
107 class ESC[4;38;5;81mBadCmp(ESC[4;38;5;149mobject):
108 def __eq__(self, other):
109 raise Exc
110
111 s1 = slice(BadCmp())
112 s2 = slice(BadCmp())
113 self.assertEqual(s1, s1)
114 self.assertRaises(Exc, lambda: s1 == s2)
115
116 s1 = slice(1, BadCmp())
117 s2 = slice(1, BadCmp())
118 self.assertEqual(s1, s1)
119 self.assertRaises(Exc, lambda: s1 == s2)
120
121 s1 = slice(1, 2, BadCmp())
122 s2 = slice(1, 2, BadCmp())
123 self.assertEqual(s1, s1)
124 self.assertRaises(Exc, lambda: s1 == s2)
125
126 def test_members(self):
127 s = slice(1)
128 self.assertEqual(s.start, None)
129 self.assertEqual(s.stop, 1)
130 self.assertEqual(s.step, None)
131
132 s = slice(1, 2)
133 self.assertEqual(s.start, 1)
134 self.assertEqual(s.stop, 2)
135 self.assertEqual(s.step, None)
136
137 s = slice(1, 2, 3)
138 self.assertEqual(s.start, 1)
139 self.assertEqual(s.stop, 2)
140 self.assertEqual(s.step, 3)
141
142 class ESC[4;38;5;81mAnyClass:
143 pass
144
145 obj = AnyClass()
146 s = slice(obj)
147 self.assertTrue(s.stop is obj)
148
149 def check_indices(self, slice, length):
150 try:
151 actual = slice.indices(length)
152 except ValueError:
153 actual = "valueerror"
154 try:
155 expected = slice_indices(slice, length)
156 except ValueError:
157 expected = "valueerror"
158 self.assertEqual(actual, expected)
159
160 if length >= 0 and slice.step != 0:
161 actual = range(*slice.indices(length))
162 expected = range(length)[slice]
163 self.assertEqual(actual, expected)
164
165 def test_indices(self):
166 self.assertEqual(slice(None ).indices(10), (0, 10, 1))
167 self.assertEqual(slice(None, None, 2).indices(10), (0, 10, 2))
168 self.assertEqual(slice(1, None, 2).indices(10), (1, 10, 2))
169 self.assertEqual(slice(None, None, -1).indices(10), (9, -1, -1))
170 self.assertEqual(slice(None, None, -2).indices(10), (9, -1, -2))
171 self.assertEqual(slice(3, None, -2).indices(10), (3, -1, -2))
172 # issue 3004 tests
173 self.assertEqual(slice(None, -9).indices(10), (0, 1, 1))
174 self.assertEqual(slice(None, -10).indices(10), (0, 0, 1))
175 self.assertEqual(slice(None, -11).indices(10), (0, 0, 1))
176 self.assertEqual(slice(None, -10, -1).indices(10), (9, 0, -1))
177 self.assertEqual(slice(None, -11, -1).indices(10), (9, -1, -1))
178 self.assertEqual(slice(None, -12, -1).indices(10), (9, -1, -1))
179 self.assertEqual(slice(None, 9).indices(10), (0, 9, 1))
180 self.assertEqual(slice(None, 10).indices(10), (0, 10, 1))
181 self.assertEqual(slice(None, 11).indices(10), (0, 10, 1))
182 self.assertEqual(slice(None, 8, -1).indices(10), (9, 8, -1))
183 self.assertEqual(slice(None, 9, -1).indices(10), (9, 9, -1))
184 self.assertEqual(slice(None, 10, -1).indices(10), (9, 9, -1))
185
186 self.assertEqual(
187 slice(-100, 100 ).indices(10),
188 slice(None).indices(10)
189 )
190 self.assertEqual(
191 slice(100, -100, -1).indices(10),
192 slice(None, None, -1).indices(10)
193 )
194 self.assertEqual(slice(-100, 100, 2).indices(10), (0, 10, 2))
195
196 self.assertEqual(list(range(10))[::sys.maxsize - 1], [0])
197
198 # Check a variety of start, stop, step and length values, including
199 # values exceeding sys.maxsize (see issue #14794).
200 vals = [None, -2**100, -2**30, -53, -7, -1, 0, 1, 7, 53, 2**30, 2**100]
201 lengths = [0, 1, 7, 53, 2**30, 2**100]
202 for slice_args in itertools.product(vals, repeat=3):
203 s = slice(*slice_args)
204 for length in lengths:
205 self.check_indices(s, length)
206 self.check_indices(slice(0, 10, 1), -3)
207
208 # Negative length should raise ValueError
209 with self.assertRaises(ValueError):
210 slice(None).indices(-1)
211
212 # Zero step should raise ValueError
213 with self.assertRaises(ValueError):
214 slice(0, 10, 0).indices(5)
215
216 # Using a start, stop or step or length that can't be interpreted as an
217 # integer should give a TypeError ...
218 with self.assertRaises(TypeError):
219 slice(0.0, 10, 1).indices(5)
220 with self.assertRaises(TypeError):
221 slice(0, 10.0, 1).indices(5)
222 with self.assertRaises(TypeError):
223 slice(0, 10, 1.0).indices(5)
224 with self.assertRaises(TypeError):
225 slice(0, 10, 1).indices(5.0)
226
227 # ... but it should be fine to use a custom class that provides index.
228 self.assertEqual(slice(0, 10, 1).indices(5), (0, 5, 1))
229 self.assertEqual(slice(MyIndexable(0), 10, 1).indices(5), (0, 5, 1))
230 self.assertEqual(slice(0, MyIndexable(10), 1).indices(5), (0, 5, 1))
231 self.assertEqual(slice(0, 10, MyIndexable(1)).indices(5), (0, 5, 1))
232 self.assertEqual(slice(0, 10, 1).indices(MyIndexable(5)), (0, 5, 1))
233
234 def test_setslice_without_getslice(self):
235 tmp = []
236 class ESC[4;38;5;81mX(ESC[4;38;5;149mobject):
237 def __setitem__(self, i, k):
238 tmp.append((i, k))
239
240 x = X()
241 x[1:2] = 42
242 self.assertEqual(tmp, [(slice(1, 2), 42)])
243
244 def test_pickle(self):
245 import pickle
246
247 s = slice(10, 20, 3)
248 for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
249 t = loads(dumps(s, protocol))
250 self.assertEqual(s, t)
251 self.assertEqual(s.indices(15), t.indices(15))
252 self.assertNotEqual(id(s), id(t))
253
254 def test_copy(self):
255 s = slice(1, 10)
256 c = copy.copy(s)
257 self.assertIs(s, c)
258
259 s = slice(1, 10, 2)
260 c = copy.copy(s)
261 self.assertIs(s, c)
262
263 # Corner case for mutable indices:
264 s = slice([1, 2], [3, 4], [5, 6])
265 c = copy.copy(s)
266 self.assertIs(s, c)
267 self.assertIs(s.start, c.start)
268 self.assertIs(s.stop, c.stop)
269 self.assertIs(s.step, c.step)
270
271 def test_deepcopy(self):
272 s = slice(1, 10)
273 c = copy.deepcopy(s)
274 self.assertEqual(s, c)
275
276 s = slice(1, 10, 2)
277 c = copy.deepcopy(s)
278 self.assertEqual(s, c)
279
280 # Corner case for mutable indices:
281 s = slice([1, 2], [3, 4], [5, 6])
282 c = copy.deepcopy(s)
283 self.assertIsNot(s, c)
284 self.assertEqual(s, c)
285 self.assertIsNot(s.start, c.start)
286 self.assertIsNot(s.stop, c.stop)
287 self.assertIsNot(s.step, c.step)
288
289 def test_cycle(self):
290 class ESC[4;38;5;81mmyobj(): pass
291 o = myobj()
292 o.s = slice(o)
293 w = weakref.ref(o)
294 o = None
295 support.gc_collect()
296 self.assertIsNone(w())
297
298 if __name__ == "__main__":
299 unittest.main()