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