blob: d88577640aba07c75e7f91c9da03f24d052cc9b0 [file] [log] [blame]
Michael W. Hudsonf0d777c2002-07-19 15:47:06 +00001# tests for slice objects; in particular the indices method.
2
Raymond Hettinger5d2e7772003-09-02 01:53:01 +00003import unittest
Guido van Rossum99603b02007-07-20 00:22:32 +00004from pickle import loads, dumps
Raymond Hettinger5d2e7772003-09-02 01:53:01 +00005
Mark Dickinsonc8a69672012-11-10 14:52:10 +00006import itertools
7import operator
Michael W. Hudson173f11d2002-11-05 15:28:51 +00008import sys
Michael W. Hudsonf0d777c2002-07-19 15:47:06 +00009
Mark Dickinsonc8a69672012-11-10 14:52:10 +000010
11def evaluate_slice_index(arg):
12 """
13 Helper function to convert a slice argument to an integer, and raise
14 TypeError with a suitable message on failure.
15
16 """
17 if hasattr(arg, '__index__'):
18 return operator.index(arg)
19 else:
20 raise TypeError(
21 "slice indices must be integers or "
22 "None or have an __index__ method")
23
24def slice_indices(slice, length):
25 """
26 Reference implementation for the slice.indices method.
27
28 """
29 # Compute step and length as integers.
30 length = operator.index(length)
31 step = 1 if slice.step is None else evaluate_slice_index(slice.step)
32
33 # Raise ValueError for negative length or zero step.
34 if length < 0:
35 raise ValueError("length should not be negative")
36 if step == 0:
37 raise ValueError("slice step cannot be zero")
38
39 # Find lower and upper bounds for start and stop.
40 lower = -1 if step < 0 else 0
41 upper = length - 1 if step < 0 else length
42
43 # Compute start.
44 if slice.start is None:
45 start = upper if step < 0 else lower
46 else:
47 start = evaluate_slice_index(slice.start)
48 start = max(start + length, lower) if start < 0 else min(start, upper)
49
50 # Compute stop.
51 if slice.stop is None:
52 stop = lower if step < 0 else upper
53 else:
54 stop = evaluate_slice_index(slice.stop)
55 stop = max(stop + length, lower) if stop < 0 else min(stop, upper)
56
57 return start, stop, step
58
59
60# Class providing an __index__ method. Used for testing slice.indices.
61
62class MyIndexable(object):
63 def __init__(self, value):
64 self.value = value
65
66 def __index__(self):
67 return self.value
68
69
Raymond Hettinger5d2e7772003-09-02 01:53:01 +000070class SliceTest(unittest.TestCase):
Michael W. Hudson173f11d2002-11-05 15:28:51 +000071
Raymond Hettinger5d2e7772003-09-02 01:53:01 +000072 def test_constructor(self):
73 self.assertRaises(TypeError, slice)
74 self.assertRaises(TypeError, slice, 1, 2, 3, 4)
75
76 def test_repr(self):
77 self.assertEqual(repr(slice(1, 2, 3)), "slice(1, 2, 3)")
78
Raymond Hettingerb859c072003-09-05 14:27:30 +000079 def test_hash(self):
80 # Verify clearing of SF bug #800796
81 self.assertRaises(TypeError, hash, slice(5))
82 self.assertRaises(TypeError, slice(5).__hash__)
83
Raymond Hettinger5d2e7772003-09-02 01:53:01 +000084 def test_cmp(self):
85 s1 = slice(1, 2, 3)
86 s2 = slice(1, 2, 3)
87 s3 = slice(1, 2, 4)
88 self.assertEqual(s1, s2)
89 self.assertNotEqual(s1, s3)
Thomas Wouters3e57b522007-08-28 23:07:26 +000090 self.assertNotEqual(s1, None)
91 self.assertNotEqual(s1, (1, 2, 3))
92 self.assertNotEqual(s1, "")
Raymond Hettinger5d2e7772003-09-02 01:53:01 +000093
94 class Exc(Exception):
95 pass
96
97 class BadCmp(object):
98 def __eq__(self, other):
99 raise Exc
100
101 s1 = slice(BadCmp())
102 s2 = slice(BadCmp())
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000103 self.assertEqual(s1, s1)
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000104 self.assertRaises(Exc, lambda: s1 == s2)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000105
106 s1 = slice(1, BadCmp())
107 s2 = slice(1, BadCmp())
108 self.assertEqual(s1, s1)
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000109 self.assertRaises(Exc, lambda: s1 == s2)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000110
111 s1 = slice(1, 2, BadCmp())
112 s2 = slice(1, 2, BadCmp())
113 self.assertEqual(s1, s1)
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000114 self.assertRaises(Exc, lambda: s1 == s2)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000115
116 def test_members(self):
117 s = slice(1)
118 self.assertEqual(s.start, None)
119 self.assertEqual(s.stop, 1)
120 self.assertEqual(s.step, None)
121
122 s = slice(1, 2)
123 self.assertEqual(s.start, 1)
124 self.assertEqual(s.stop, 2)
125 self.assertEqual(s.step, None)
126
127 s = slice(1, 2, 3)
128 self.assertEqual(s.start, 1)
129 self.assertEqual(s.stop, 2)
130 self.assertEqual(s.step, 3)
131
132 class AnyClass:
133 pass
134
135 obj = AnyClass()
136 s = slice(obj)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000137 self.assertTrue(s.stop is obj)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000138
Mark Dickinsonc8a69672012-11-10 14:52:10 +0000139 def check_indices(self, slice, length):
140 try:
141 actual = slice.indices(length)
142 except ValueError:
143 actual = "valueerror"
144 try:
145 expected = slice_indices(slice, length)
146 except ValueError:
147 expected = "valueerror"
148 self.assertEqual(actual, expected)
149
150 if length >= 0 and slice.step != 0:
151 actual = range(*slice.indices(length))
152 expected = range(length)[slice]
153 self.assertEqual(actual, expected)
154
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000155 def test_indices(self):
156 self.assertEqual(slice(None ).indices(10), (0, 10, 1))
157 self.assertEqual(slice(None, None, 2).indices(10), (0, 10, 2))
158 self.assertEqual(slice(1, None, 2).indices(10), (1, 10, 2))
159 self.assertEqual(slice(None, None, -1).indices(10), (9, -1, -1))
160 self.assertEqual(slice(None, None, -2).indices(10), (9, -1, -2))
161 self.assertEqual(slice(3, None, -2).indices(10), (3, -1, -2))
Benjamin Petersonfea6a942008-07-02 16:11:42 +0000162 # issue 3004 tests
163 self.assertEqual(slice(None, -9).indices(10), (0, 1, 1))
164 self.assertEqual(slice(None, -10).indices(10), (0, 0, 1))
165 self.assertEqual(slice(None, -11).indices(10), (0, 0, 1))
166 self.assertEqual(slice(None, -10, -1).indices(10), (9, 0, -1))
167 self.assertEqual(slice(None, -11, -1).indices(10), (9, -1, -1))
168 self.assertEqual(slice(None, -12, -1).indices(10), (9, -1, -1))
169 self.assertEqual(slice(None, 9).indices(10), (0, 9, 1))
170 self.assertEqual(slice(None, 10).indices(10), (0, 10, 1))
171 self.assertEqual(slice(None, 11).indices(10), (0, 10, 1))
172 self.assertEqual(slice(None, 8, -1).indices(10), (9, 8, -1))
173 self.assertEqual(slice(None, 9, -1).indices(10), (9, 9, -1))
174 self.assertEqual(slice(None, 10, -1).indices(10), (9, 9, -1))
175
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000176 self.assertEqual(
177 slice(-100, 100 ).indices(10),
178 slice(None).indices(10)
179 )
180 self.assertEqual(
181 slice(100, -100, -1).indices(10),
182 slice(None, None, -1).indices(10)
183 )
Guido van Rossume2a383d2007-01-15 16:59:06 +0000184 self.assertEqual(slice(-100, 100, 2).indices(10), (0, 10, 2))
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000185
Christian Heimesa37d4c62007-12-04 23:02:19 +0000186 self.assertEqual(list(range(10))[::sys.maxsize - 1], [0])
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000187
Mark Dickinsonc8a69672012-11-10 14:52:10 +0000188 # Check a variety of start, stop, step and length values, including
189 # values exceeding sys.maxsize (see issue #14794).
190 vals = [None, -2**100, -2**30, -53, -7, -1, 0, 1, 7, 53, 2**30, 2**100]
191 lengths = [0, 1, 7, 53, 2**30, 2**100]
192 for slice_args in itertools.product(vals, repeat=3):
193 s = slice(*slice_args)
194 for length in lengths:
195 self.check_indices(s, length)
196 self.check_indices(slice(0, 10, 1), -3)
197
198 # Negative length should raise ValueError
199 with self.assertRaises(ValueError):
200 slice(None).indices(-1)
201
202 # Zero step should raise ValueError
203 with self.assertRaises(ValueError):
204 slice(0, 10, 0).indices(5)
205
206 # Using a start, stop or step or length that can't be interpreted as an
207 # integer should give a TypeError ...
208 with self.assertRaises(TypeError):
209 slice(0.0, 10, 1).indices(5)
210 with self.assertRaises(TypeError):
211 slice(0, 10.0, 1).indices(5)
212 with self.assertRaises(TypeError):
213 slice(0, 10, 1.0).indices(5)
214 with self.assertRaises(TypeError):
215 slice(0, 10, 1).indices(5.0)
216
217 # ... but it should be fine to use a custom class that provides index.
218 self.assertEqual(slice(0, 10, 1).indices(5), (0, 5, 1))
219 self.assertEqual(slice(MyIndexable(0), 10, 1).indices(5), (0, 5, 1))
220 self.assertEqual(slice(0, MyIndexable(10), 1).indices(5), (0, 5, 1))
221 self.assertEqual(slice(0, 10, MyIndexable(1)).indices(5), (0, 5, 1))
222 self.assertEqual(slice(0, 10, 1).indices(MyIndexable(5)), (0, 5, 1))
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000223
Guido van Rossumd8faa362007-04-27 19:54:29 +0000224 def test_setslice_without_getslice(self):
225 tmp = []
226 class X(object):
Thomas Woutersd2cf20e2007-08-30 22:57:53 +0000227 def __setitem__(self, i, k):
228 tmp.append((i, k))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000229
230 x = X()
231 x[1:2] = 42
Ezio Melottib3aedd42010-11-20 19:04:17 +0000232 self.assertEqual(tmp, [(slice(1, 2), 42)])
Guido van Rossumd8faa362007-04-27 19:54:29 +0000233
234 def test_pickle(self):
235 s = slice(10, 20, 3)
236 for protocol in (0,1,2):
237 t = loads(dumps(s, protocol))
238 self.assertEqual(s, t)
239 self.assertEqual(s.indices(15), t.indices(15))
240 self.assertNotEqual(id(s), id(t))
241
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000242if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -0500243 unittest.main()