blob: 4ae4142c60c8a878ba5ae104def96d7f005b947c [file] [log] [blame]
Michael W. Hudsonf0d777c2002-07-19 15:47:06 +00001# tests for slice objects; in particular the indices method.
2
Mark Dickinsonc8a69672012-11-10 14:52:10 +00003import itertools
4import operator
Michael W. Hudson173f11d2002-11-05 15:28:51 +00005import sys
Benjamin Peterson2b601d32016-04-16 14:47:12 -07006import unittest
7import weakref
8
9from pickle import loads, dumps
10from test import support
Michael W. Hudsonf0d777c2002-07-19 15:47:06 +000011
Mark Dickinsonc8a69672012-11-10 14:52:10 +000012
13def evaluate_slice_index(arg):
14 """
15 Helper function to convert a slice argument to an integer, and raise
16 TypeError with a suitable message on failure.
17
18 """
19 if hasattr(arg, '__index__'):
20 return operator.index(arg)
21 else:
22 raise TypeError(
23 "slice indices must be integers or "
24 "None or have an __index__ method")
25
26def slice_indices(slice, length):
27 """
28 Reference implementation for the slice.indices method.
29
30 """
31 # Compute step and length as integers.
32 length = operator.index(length)
33 step = 1 if slice.step is None else evaluate_slice_index(slice.step)
34
35 # Raise ValueError for negative length or zero step.
36 if length < 0:
37 raise ValueError("length should not be negative")
38 if step == 0:
39 raise ValueError("slice step cannot be zero")
40
41 # Find lower and upper bounds for start and stop.
42 lower = -1 if step < 0 else 0
43 upper = length - 1 if step < 0 else length
44
45 # Compute start.
46 if slice.start is None:
47 start = upper if step < 0 else lower
48 else:
49 start = evaluate_slice_index(slice.start)
50 start = max(start + length, lower) if start < 0 else min(start, upper)
51
52 # Compute stop.
53 if slice.stop is None:
54 stop = lower if step < 0 else upper
55 else:
56 stop = evaluate_slice_index(slice.stop)
57 stop = max(stop + length, lower) if stop < 0 else min(stop, upper)
58
59 return start, stop, step
60
61
62# Class providing an __index__ method. Used for testing slice.indices.
63
64class MyIndexable(object):
65 def __init__(self, value):
66 self.value = value
67
68 def __index__(self):
69 return self.value
70
71
Raymond Hettinger5d2e7772003-09-02 01:53:01 +000072class SliceTest(unittest.TestCase):
Michael W. Hudson173f11d2002-11-05 15:28:51 +000073
Raymond Hettinger5d2e7772003-09-02 01:53:01 +000074 def test_constructor(self):
75 self.assertRaises(TypeError, slice)
76 self.assertRaises(TypeError, slice, 1, 2, 3, 4)
77
78 def test_repr(self):
79 self.assertEqual(repr(slice(1, 2, 3)), "slice(1, 2, 3)")
80
Raymond Hettingerb859c072003-09-05 14:27:30 +000081 def test_hash(self):
82 # Verify clearing of SF bug #800796
83 self.assertRaises(TypeError, hash, slice(5))
Serhiy Storchakac0e00222015-05-20 18:37:37 +030084 with self.assertRaises(TypeError):
85 slice(5).__hash__()
Raymond Hettingerb859c072003-09-05 14:27:30 +000086
Raymond Hettinger5d2e7772003-09-02 01:53:01 +000087 def test_cmp(self):
88 s1 = slice(1, 2, 3)
89 s2 = slice(1, 2, 3)
90 s3 = slice(1, 2, 4)
91 self.assertEqual(s1, s2)
92 self.assertNotEqual(s1, s3)
Thomas Wouters3e57b522007-08-28 23:07:26 +000093 self.assertNotEqual(s1, None)
94 self.assertNotEqual(s1, (1, 2, 3))
95 self.assertNotEqual(s1, "")
Raymond Hettinger5d2e7772003-09-02 01:53:01 +000096
97 class Exc(Exception):
98 pass
99
100 class BadCmp(object):
101 def __eq__(self, other):
102 raise Exc
103
104 s1 = slice(BadCmp())
105 s2 = slice(BadCmp())
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000106 self.assertEqual(s1, s1)
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000107 self.assertRaises(Exc, lambda: s1 == s2)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000108
109 s1 = slice(1, BadCmp())
110 s2 = slice(1, BadCmp())
111 self.assertEqual(s1, s1)
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000112 self.assertRaises(Exc, lambda: s1 == s2)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000113
114 s1 = slice(1, 2, BadCmp())
115 s2 = slice(1, 2, BadCmp())
116 self.assertEqual(s1, s1)
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000117 self.assertRaises(Exc, lambda: s1 == s2)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000118
119 def test_members(self):
120 s = slice(1)
121 self.assertEqual(s.start, None)
122 self.assertEqual(s.stop, 1)
123 self.assertEqual(s.step, None)
124
125 s = slice(1, 2)
126 self.assertEqual(s.start, 1)
127 self.assertEqual(s.stop, 2)
128 self.assertEqual(s.step, None)
129
130 s = slice(1, 2, 3)
131 self.assertEqual(s.start, 1)
132 self.assertEqual(s.stop, 2)
133 self.assertEqual(s.step, 3)
134
135 class AnyClass:
136 pass
137
138 obj = AnyClass()
139 s = slice(obj)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000140 self.assertTrue(s.stop is obj)
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000141
Mark Dickinsonc8a69672012-11-10 14:52:10 +0000142 def check_indices(self, slice, length):
143 try:
144 actual = slice.indices(length)
145 except ValueError:
146 actual = "valueerror"
147 try:
148 expected = slice_indices(slice, length)
149 except ValueError:
150 expected = "valueerror"
151 self.assertEqual(actual, expected)
152
153 if length >= 0 and slice.step != 0:
154 actual = range(*slice.indices(length))
155 expected = range(length)[slice]
156 self.assertEqual(actual, expected)
157
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000158 def test_indices(self):
159 self.assertEqual(slice(None ).indices(10), (0, 10, 1))
160 self.assertEqual(slice(None, None, 2).indices(10), (0, 10, 2))
161 self.assertEqual(slice(1, None, 2).indices(10), (1, 10, 2))
162 self.assertEqual(slice(None, None, -1).indices(10), (9, -1, -1))
163 self.assertEqual(slice(None, None, -2).indices(10), (9, -1, -2))
164 self.assertEqual(slice(3, None, -2).indices(10), (3, -1, -2))
Benjamin Petersonfea6a942008-07-02 16:11:42 +0000165 # issue 3004 tests
166 self.assertEqual(slice(None, -9).indices(10), (0, 1, 1))
167 self.assertEqual(slice(None, -10).indices(10), (0, 0, 1))
168 self.assertEqual(slice(None, -11).indices(10), (0, 0, 1))
169 self.assertEqual(slice(None, -10, -1).indices(10), (9, 0, -1))
170 self.assertEqual(slice(None, -11, -1).indices(10), (9, -1, -1))
171 self.assertEqual(slice(None, -12, -1).indices(10), (9, -1, -1))
172 self.assertEqual(slice(None, 9).indices(10), (0, 9, 1))
173 self.assertEqual(slice(None, 10).indices(10), (0, 10, 1))
174 self.assertEqual(slice(None, 11).indices(10), (0, 10, 1))
175 self.assertEqual(slice(None, 8, -1).indices(10), (9, 8, -1))
176 self.assertEqual(slice(None, 9, -1).indices(10), (9, 9, -1))
177 self.assertEqual(slice(None, 10, -1).indices(10), (9, 9, -1))
178
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000179 self.assertEqual(
180 slice(-100, 100 ).indices(10),
181 slice(None).indices(10)
182 )
183 self.assertEqual(
184 slice(100, -100, -1).indices(10),
185 slice(None, None, -1).indices(10)
186 )
Guido van Rossume2a383d2007-01-15 16:59:06 +0000187 self.assertEqual(slice(-100, 100, 2).indices(10), (0, 10, 2))
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000188
Christian Heimesa37d4c62007-12-04 23:02:19 +0000189 self.assertEqual(list(range(10))[::sys.maxsize - 1], [0])
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000190
Mark Dickinsonc8a69672012-11-10 14:52:10 +0000191 # Check a variety of start, stop, step and length values, including
192 # values exceeding sys.maxsize (see issue #14794).
193 vals = [None, -2**100, -2**30, -53, -7, -1, 0, 1, 7, 53, 2**30, 2**100]
194 lengths = [0, 1, 7, 53, 2**30, 2**100]
195 for slice_args in itertools.product(vals, repeat=3):
196 s = slice(*slice_args)
197 for length in lengths:
198 self.check_indices(s, length)
199 self.check_indices(slice(0, 10, 1), -3)
200
201 # Negative length should raise ValueError
202 with self.assertRaises(ValueError):
203 slice(None).indices(-1)
204
205 # Zero step should raise ValueError
206 with self.assertRaises(ValueError):
207 slice(0, 10, 0).indices(5)
208
209 # Using a start, stop or step or length that can't be interpreted as an
210 # integer should give a TypeError ...
211 with self.assertRaises(TypeError):
212 slice(0.0, 10, 1).indices(5)
213 with self.assertRaises(TypeError):
214 slice(0, 10.0, 1).indices(5)
215 with self.assertRaises(TypeError):
216 slice(0, 10, 1.0).indices(5)
217 with self.assertRaises(TypeError):
218 slice(0, 10, 1).indices(5.0)
219
220 # ... but it should be fine to use a custom class that provides index.
221 self.assertEqual(slice(0, 10, 1).indices(5), (0, 5, 1))
222 self.assertEqual(slice(MyIndexable(0), 10, 1).indices(5), (0, 5, 1))
223 self.assertEqual(slice(0, MyIndexable(10), 1).indices(5), (0, 5, 1))
224 self.assertEqual(slice(0, 10, MyIndexable(1)).indices(5), (0, 5, 1))
225 self.assertEqual(slice(0, 10, 1).indices(MyIndexable(5)), (0, 5, 1))
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000226
Guido van Rossumd8faa362007-04-27 19:54:29 +0000227 def test_setslice_without_getslice(self):
228 tmp = []
229 class X(object):
Thomas Woutersd2cf20e2007-08-30 22:57:53 +0000230 def __setitem__(self, i, k):
231 tmp.append((i, k))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000232
233 x = X()
234 x[1:2] = 42
Ezio Melottib3aedd42010-11-20 19:04:17 +0000235 self.assertEqual(tmp, [(slice(1, 2), 42)])
Guido van Rossumd8faa362007-04-27 19:54:29 +0000236
237 def test_pickle(self):
238 s = slice(10, 20, 3)
239 for protocol in (0,1,2):
240 t = loads(dumps(s, protocol))
241 self.assertEqual(s, t)
242 self.assertEqual(s.indices(15), t.indices(15))
243 self.assertNotEqual(id(s), id(t))
244
Benjamin Peterson2b601d32016-04-16 14:47:12 -0700245 def test_cycle(self):
246 class myobj(): pass
247 o = myobj()
248 o.s = slice(o)
249 w = weakref.ref(o)
250 o = None
Benjamin Peterson89172262016-04-16 15:02:23 -0700251 support.gc_collect()
Benjamin Peterson2b601d32016-04-16 14:47:12 -0700252 self.assertIsNone(w())
253
Raymond Hettinger5d2e7772003-09-02 01:53:01 +0000254if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -0500255 unittest.main()