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 # 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 ESC[4;38;5;81mExc(ESC[4;38;5;149mException):
99 pass
100
101 class ESC[4;38;5;81mBadCmp(ESC[4;38;5;149mobject):
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 ESC[4;38;5;81mAnyClass:
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 ESC[4;38;5;81mX(ESC[4;38;5;149mobject):
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 ESC[4;38;5;81mmyobj(): 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
292 if __name__ == "__main__":
293 unittest.main()