blob: 1ed71f9575d417cda5565aef1e5dc6b7d53d760a [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))
Serhiy Storchakac0e00222015-05-20 18:37:37 +030083 with self.assertRaises(TypeError):
84 slice(5).__hash__()
Raymond Hettingerb859c072003-09-05 14:27:30 +000085
Raymond Hettinger5d2e7772003-09-02 01:53:01 +000086 def test_cmp(self):
87 s1 = slice(1, 2, 3)
88 s2 = slice(1, 2, 3)
89 s3 = slice(1, 2, 4)
90 self.assertEqual(s1, s2)
91 self.assertNotEqual(s1, s3)
Thomas Wouters3e57b522007-08-28 23:07:26 +000092 self.assertNotEqual(s1, None)
93 self.assertNotEqual(s1, (1, 2, 3))
94 self.assertNotEqual(s1, "")
Raymond Hettinger5d2e7772003-09-02 01:53:01 +000095
96 class Exc(Exception):
97 pass
98
99 class BadCmp(object):
100 def __eq__(self, other):
101 raise Exc
102
103 s1 = slice(BadCmp())
104 s2 = slice(BadCmp())
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000105 self.assertEqual(s1, s1)
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000106 self.assertRaises(Exc, lambda: s1 == s2)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000107
108 s1 = slice(1, BadCmp())
109 s2 = slice(1, BadCmp())
110 self.assertEqual(s1, s1)
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000111 self.assertRaises(Exc, lambda: s1 == s2)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000112
113 s1 = slice(1, 2, BadCmp())
114 s2 = slice(1, 2, BadCmp())
115 self.assertEqual(s1, s1)
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000116 self.assertRaises(Exc, lambda: s1 == s2)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000117
118 def test_members(self):
119 s = slice(1)
120 self.assertEqual(s.start, None)
121 self.assertEqual(s.stop, 1)
122 self.assertEqual(s.step, None)
123
124 s = slice(1, 2)
125 self.assertEqual(s.start, 1)
126 self.assertEqual(s.stop, 2)
127 self.assertEqual(s.step, None)
128
129 s = slice(1, 2, 3)
130 self.assertEqual(s.start, 1)
131 self.assertEqual(s.stop, 2)
132 self.assertEqual(s.step, 3)
133
134 class AnyClass:
135 pass
136
137 obj = AnyClass()
138 s = slice(obj)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000139 self.assertTrue(s.stop is obj)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000140
Mark Dickinsonc8a69672012-11-10 14:52:10 +0000141 def check_indices(self, slice, length):
142 try:
143 actual = slice.indices(length)
144 except ValueError:
145 actual = "valueerror"
146 try:
147 expected = slice_indices(slice, length)
148 except ValueError:
149 expected = "valueerror"
150 self.assertEqual(actual, expected)
151
152 if length >= 0 and slice.step != 0:
153 actual = range(*slice.indices(length))
154 expected = range(length)[slice]
155 self.assertEqual(actual, expected)
156
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000157 def test_indices(self):
158 self.assertEqual(slice(None ).indices(10), (0, 10, 1))
159 self.assertEqual(slice(None, None, 2).indices(10), (0, 10, 2))
160 self.assertEqual(slice(1, None, 2).indices(10), (1, 10, 2))
161 self.assertEqual(slice(None, None, -1).indices(10), (9, -1, -1))
162 self.assertEqual(slice(None, None, -2).indices(10), (9, -1, -2))
163 self.assertEqual(slice(3, None, -2).indices(10), (3, -1, -2))
Benjamin Petersonfea6a942008-07-02 16:11:42 +0000164 # issue 3004 tests
165 self.assertEqual(slice(None, -9).indices(10), (0, 1, 1))
166 self.assertEqual(slice(None, -10).indices(10), (0, 0, 1))
167 self.assertEqual(slice(None, -11).indices(10), (0, 0, 1))
168 self.assertEqual(slice(None, -10, -1).indices(10), (9, 0, -1))
169 self.assertEqual(slice(None, -11, -1).indices(10), (9, -1, -1))
170 self.assertEqual(slice(None, -12, -1).indices(10), (9, -1, -1))
171 self.assertEqual(slice(None, 9).indices(10), (0, 9, 1))
172 self.assertEqual(slice(None, 10).indices(10), (0, 10, 1))
173 self.assertEqual(slice(None, 11).indices(10), (0, 10, 1))
174 self.assertEqual(slice(None, 8, -1).indices(10), (9, 8, -1))
175 self.assertEqual(slice(None, 9, -1).indices(10), (9, 9, -1))
176 self.assertEqual(slice(None, 10, -1).indices(10), (9, 9, -1))
177
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000178 self.assertEqual(
179 slice(-100, 100 ).indices(10),
180 slice(None).indices(10)
181 )
182 self.assertEqual(
183 slice(100, -100, -1).indices(10),
184 slice(None, None, -1).indices(10)
185 )
Guido van Rossume2a383d2007-01-15 16:59:06 +0000186 self.assertEqual(slice(-100, 100, 2).indices(10), (0, 10, 2))
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000187
Christian Heimesa37d4c62007-12-04 23:02:19 +0000188 self.assertEqual(list(range(10))[::sys.maxsize - 1], [0])
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000189
Mark Dickinsonc8a69672012-11-10 14:52:10 +0000190 # Check a variety of start, stop, step and length values, including
191 # values exceeding sys.maxsize (see issue #14794).
192 vals = [None, -2**100, -2**30, -53, -7, -1, 0, 1, 7, 53, 2**30, 2**100]
193 lengths = [0, 1, 7, 53, 2**30, 2**100]
194 for slice_args in itertools.product(vals, repeat=3):
195 s = slice(*slice_args)
196 for length in lengths:
197 self.check_indices(s, length)
198 self.check_indices(slice(0, 10, 1), -3)
199
200 # Negative length should raise ValueError
201 with self.assertRaises(ValueError):
202 slice(None).indices(-1)
203
204 # Zero step should raise ValueError
205 with self.assertRaises(ValueError):
206 slice(0, 10, 0).indices(5)
207
208 # Using a start, stop or step or length that can't be interpreted as an
209 # integer should give a TypeError ...
210 with self.assertRaises(TypeError):
211 slice(0.0, 10, 1).indices(5)
212 with self.assertRaises(TypeError):
213 slice(0, 10.0, 1).indices(5)
214 with self.assertRaises(TypeError):
215 slice(0, 10, 1.0).indices(5)
216 with self.assertRaises(TypeError):
217 slice(0, 10, 1).indices(5.0)
218
219 # ... but it should be fine to use a custom class that provides index.
220 self.assertEqual(slice(0, 10, 1).indices(5), (0, 5, 1))
221 self.assertEqual(slice(MyIndexable(0), 10, 1).indices(5), (0, 5, 1))
222 self.assertEqual(slice(0, MyIndexable(10), 1).indices(5), (0, 5, 1))
223 self.assertEqual(slice(0, 10, MyIndexable(1)).indices(5), (0, 5, 1))
224 self.assertEqual(slice(0, 10, 1).indices(MyIndexable(5)), (0, 5, 1))
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000225
Guido van Rossumd8faa362007-04-27 19:54:29 +0000226 def test_setslice_without_getslice(self):
227 tmp = []
228 class X(object):
Thomas Woutersd2cf20e2007-08-30 22:57:53 +0000229 def __setitem__(self, i, k):
230 tmp.append((i, k))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000231
232 x = X()
233 x[1:2] = 42
Ezio Melottib3aedd42010-11-20 19:04:17 +0000234 self.assertEqual(tmp, [(slice(1, 2), 42)])
Guido van Rossumd8faa362007-04-27 19:54:29 +0000235
236 def test_pickle(self):
237 s = slice(10, 20, 3)
238 for protocol in (0,1,2):
239 t = loads(dumps(s, protocol))
240 self.assertEqual(s, t)
241 self.assertEqual(s.indices(15), t.indices(15))
242 self.assertNotEqual(id(s), id(t))
243
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000244def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000245 support.run_unittest(SliceTest)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000246
247if __name__ == "__main__":
248 test_main()