mirror of
				https://github.com/python/cpython.git
				synced 2025-10-26 11:14:33 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			255 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			255 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # tests for slice objects; in particular the indices method.
 | |
| 
 | |
| import itertools
 | |
| import operator
 | |
| import sys
 | |
| import unittest
 | |
| import weakref
 | |
| 
 | |
| from pickle import loads, dumps
 | |
| from test import support
 | |
| 
 | |
| 
 | |
| def evaluate_slice_index(arg):
 | |
|     """
 | |
|     Helper function to convert a slice argument to an integer, and raise
 | |
|     TypeError with a suitable message on failure.
 | |
| 
 | |
|     """
 | |
|     if hasattr(arg, '__index__'):
 | |
|         return operator.index(arg)
 | |
|     else:
 | |
|         raise TypeError(
 | |
|             "slice indices must be integers or "
 | |
|             "None or have an __index__ method")
 | |
| 
 | |
| def slice_indices(slice, length):
 | |
|     """
 | |
|     Reference implementation for the slice.indices method.
 | |
| 
 | |
|     """
 | |
|     # Compute step and length as integers.
 | |
|     length = operator.index(length)
 | |
|     step = 1 if slice.step is None else evaluate_slice_index(slice.step)
 | |
| 
 | |
|     # Raise ValueError for negative length or zero step.
 | |
|     if length < 0:
 | |
|         raise ValueError("length should not be negative")
 | |
|     if step == 0:
 | |
|         raise ValueError("slice step cannot be zero")
 | |
| 
 | |
|     # Find lower and upper bounds for start and stop.
 | |
|     lower = -1 if step < 0 else 0
 | |
|     upper = length - 1 if step < 0 else length
 | |
| 
 | |
|     # Compute start.
 | |
|     if slice.start is None:
 | |
|         start = upper if step < 0 else lower
 | |
|     else:
 | |
|         start = evaluate_slice_index(slice.start)
 | |
|         start = max(start + length, lower) if start < 0 else min(start, upper)
 | |
| 
 | |
|     # Compute stop.
 | |
|     if slice.stop is None:
 | |
|         stop = lower if step < 0 else upper
 | |
|     else:
 | |
|         stop = evaluate_slice_index(slice.stop)
 | |
|         stop = max(stop + length, lower) if stop < 0 else min(stop, upper)
 | |
| 
 | |
|     return start, stop, step
 | |
| 
 | |
| 
 | |
| # Class providing an __index__ method.  Used for testing slice.indices.
 | |
| 
 | |
| class MyIndexable(object):
 | |
|     def __init__(self, value):
 | |
|         self.value = value
 | |
| 
 | |
|     def __index__(self):
 | |
|         return self.value
 | |
| 
 | |
| 
 | |
| class SliceTest(unittest.TestCase):
 | |
| 
 | |
|     def test_constructor(self):
 | |
|         self.assertRaises(TypeError, slice)
 | |
|         self.assertRaises(TypeError, slice, 1, 2, 3, 4)
 | |
| 
 | |
|     def test_repr(self):
 | |
|         self.assertEqual(repr(slice(1, 2, 3)), "slice(1, 2, 3)")
 | |
| 
 | |
|     def test_hash(self):
 | |
|         # Verify clearing of SF bug #800796
 | |
|         self.assertRaises(TypeError, hash, slice(5))
 | |
|         with self.assertRaises(TypeError):
 | |
|             slice(5).__hash__()
 | |
| 
 | |
|     def test_cmp(self):
 | |
|         s1 = slice(1, 2, 3)
 | |
|         s2 = slice(1, 2, 3)
 | |
|         s3 = slice(1, 2, 4)
 | |
|         self.assertEqual(s1, s2)
 | |
|         self.assertNotEqual(s1, s3)
 | |
|         self.assertNotEqual(s1, None)
 | |
|         self.assertNotEqual(s1, (1, 2, 3))
 | |
|         self.assertNotEqual(s1, "")
 | |
| 
 | |
|         class Exc(Exception):
 | |
|             pass
 | |
| 
 | |
|         class BadCmp(object):
 | |
|             def __eq__(self, other):
 | |
|                 raise Exc
 | |
| 
 | |
|         s1 = slice(BadCmp())
 | |
|         s2 = slice(BadCmp())
 | |
|         self.assertEqual(s1, s1)
 | |
|         self.assertRaises(Exc, lambda: s1 == s2)
 | |
| 
 | |
|         s1 = slice(1, BadCmp())
 | |
|         s2 = slice(1, BadCmp())
 | |
|         self.assertEqual(s1, s1)
 | |
|         self.assertRaises(Exc, lambda: s1 == s2)
 | |
| 
 | |
|         s1 = slice(1, 2, BadCmp())
 | |
|         s2 = slice(1, 2, BadCmp())
 | |
|         self.assertEqual(s1, s1)
 | |
|         self.assertRaises(Exc, lambda: s1 == s2)
 | |
| 
 | |
|     def test_members(self):
 | |
|         s = slice(1)
 | |
|         self.assertEqual(s.start, None)
 | |
|         self.assertEqual(s.stop, 1)
 | |
|         self.assertEqual(s.step, None)
 | |
| 
 | |
|         s = slice(1, 2)
 | |
|         self.assertEqual(s.start, 1)
 | |
|         self.assertEqual(s.stop, 2)
 | |
|         self.assertEqual(s.step, None)
 | |
| 
 | |
|         s = slice(1, 2, 3)
 | |
|         self.assertEqual(s.start, 1)
 | |
|         self.assertEqual(s.stop, 2)
 | |
|         self.assertEqual(s.step, 3)
 | |
| 
 | |
|         class AnyClass:
 | |
|             pass
 | |
| 
 | |
|         obj = AnyClass()
 | |
|         s = slice(obj)
 | |
|         self.assertTrue(s.stop is obj)
 | |
| 
 | |
|     def check_indices(self, slice, length):
 | |
|         try:
 | |
|             actual = slice.indices(length)
 | |
|         except ValueError:
 | |
|             actual = "valueerror"
 | |
|         try:
 | |
|             expected = slice_indices(slice, length)
 | |
|         except ValueError:
 | |
|             expected = "valueerror"
 | |
|         self.assertEqual(actual, expected)
 | |
| 
 | |
|         if length >= 0 and slice.step != 0:
 | |
|             actual = range(*slice.indices(length))
 | |
|             expected = range(length)[slice]
 | |
|             self.assertEqual(actual, expected)
 | |
| 
 | |
|     def test_indices(self):
 | |
|         self.assertEqual(slice(None           ).indices(10), (0, 10,  1))
 | |
|         self.assertEqual(slice(None,  None,  2).indices(10), (0, 10,  2))
 | |
|         self.assertEqual(slice(1,     None,  2).indices(10), (1, 10,  2))
 | |
|         self.assertEqual(slice(None,  None, -1).indices(10), (9, -1, -1))
 | |
|         self.assertEqual(slice(None,  None, -2).indices(10), (9, -1, -2))
 | |
|         self.assertEqual(slice(3,     None, -2).indices(10), (3, -1, -2))
 | |
|         # issue 3004 tests
 | |
|         self.assertEqual(slice(None, -9).indices(10), (0, 1, 1))
 | |
|         self.assertEqual(slice(None, -10).indices(10), (0, 0, 1))
 | |
|         self.assertEqual(slice(None, -11).indices(10), (0, 0, 1))
 | |
|         self.assertEqual(slice(None, -10, -1).indices(10), (9, 0, -1))
 | |
|         self.assertEqual(slice(None, -11, -1).indices(10), (9, -1, -1))
 | |
|         self.assertEqual(slice(None, -12, -1).indices(10), (9, -1, -1))
 | |
|         self.assertEqual(slice(None, 9).indices(10), (0, 9, 1))
 | |
|         self.assertEqual(slice(None, 10).indices(10), (0, 10, 1))
 | |
|         self.assertEqual(slice(None, 11).indices(10), (0, 10, 1))
 | |
|         self.assertEqual(slice(None, 8, -1).indices(10), (9, 8, -1))
 | |
|         self.assertEqual(slice(None, 9, -1).indices(10), (9, 9, -1))
 | |
|         self.assertEqual(slice(None, 10, -1).indices(10), (9, 9, -1))
 | |
| 
 | |
|         self.assertEqual(
 | |
|             slice(-100,  100     ).indices(10),
 | |
|             slice(None).indices(10)
 | |
|         )
 | |
|         self.assertEqual(
 | |
|             slice(100,  -100,  -1).indices(10),
 | |
|             slice(None, None, -1).indices(10)
 | |
|         )
 | |
|         self.assertEqual(slice(-100, 100, 2).indices(10), (0, 10,  2))
 | |
| 
 | |
|         self.assertEqual(list(range(10))[::sys.maxsize - 1], [0])
 | |
| 
 | |
|         # Check a variety of start, stop, step and length values, including
 | |
|         # values exceeding sys.maxsize (see issue #14794).
 | |
|         vals = [None, -2**100, -2**30, -53, -7, -1, 0, 1, 7, 53, 2**30, 2**100]
 | |
|         lengths = [0, 1, 7, 53, 2**30, 2**100]
 | |
|         for slice_args in itertools.product(vals, repeat=3):
 | |
|             s = slice(*slice_args)
 | |
|             for length in lengths:
 | |
|                 self.check_indices(s, length)
 | |
|         self.check_indices(slice(0, 10, 1), -3)
 | |
| 
 | |
|         # Negative length should raise ValueError
 | |
|         with self.assertRaises(ValueError):
 | |
|             slice(None).indices(-1)
 | |
| 
 | |
|         # Zero step should raise ValueError
 | |
|         with self.assertRaises(ValueError):
 | |
|             slice(0, 10, 0).indices(5)
 | |
| 
 | |
|         # Using a start, stop or step or length that can't be interpreted as an
 | |
|         # integer should give a TypeError ...
 | |
|         with self.assertRaises(TypeError):
 | |
|             slice(0.0, 10, 1).indices(5)
 | |
|         with self.assertRaises(TypeError):
 | |
|             slice(0, 10.0, 1).indices(5)
 | |
|         with self.assertRaises(TypeError):
 | |
|             slice(0, 10, 1.0).indices(5)
 | |
|         with self.assertRaises(TypeError):
 | |
|             slice(0, 10, 1).indices(5.0)
 | |
| 
 | |
|         # ... but it should be fine to use a custom class that provides index.
 | |
|         self.assertEqual(slice(0, 10, 1).indices(5), (0, 5, 1))
 | |
|         self.assertEqual(slice(MyIndexable(0), 10, 1).indices(5), (0, 5, 1))
 | |
|         self.assertEqual(slice(0, MyIndexable(10), 1).indices(5), (0, 5, 1))
 | |
|         self.assertEqual(slice(0, 10, MyIndexable(1)).indices(5), (0, 5, 1))
 | |
|         self.assertEqual(slice(0, 10, 1).indices(MyIndexable(5)), (0, 5, 1))
 | |
| 
 | |
|     def test_setslice_without_getslice(self):
 | |
|         tmp = []
 | |
|         class X(object):
 | |
|             def __setitem__(self, i, k):
 | |
|                 tmp.append((i, k))
 | |
| 
 | |
|         x = X()
 | |
|         x[1:2] = 42
 | |
|         self.assertEqual(tmp, [(slice(1, 2), 42)])
 | |
| 
 | |
|     def test_pickle(self):
 | |
|         s = slice(10, 20, 3)
 | |
|         for protocol in (0,1,2):
 | |
|             t = loads(dumps(s, protocol))
 | |
|             self.assertEqual(s, t)
 | |
|             self.assertEqual(s.indices(15), t.indices(15))
 | |
|             self.assertNotEqual(id(s), id(t))
 | |
| 
 | |
|     def test_cycle(self):
 | |
|         class myobj(): pass
 | |
|         o = myobj()
 | |
|         o.s = slice(o)
 | |
|         w = weakref.ref(o)
 | |
|         o = None
 | |
|         support.gc_collect()
 | |
|         self.assertIsNone(w())
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     unittest.main()
 | 
