mirror of
				https://github.com/python/cpython.git
				synced 2025-11-02 22:51:25 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			299 lines
		
	
	
	
		
			9.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			299 lines
		
	
	
	
		
			9.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# tests for slice objects; in particular the indices method.
 | 
						|
 | 
						|
import itertools
 | 
						|
import operator
 | 
						|
import sys
 | 
						|
import unittest
 | 
						|
import weakref
 | 
						|
import copy
 | 
						|
 | 
						|
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):
 | 
						|
        self.assertEqual(hash(slice(5)), slice(5).__hash__())
 | 
						|
        self.assertEqual(hash(slice(1, 2)), slice(1, 2).__hash__())
 | 
						|
        self.assertEqual(hash(slice(1, 2, 3)), slice(1, 2, 3).__hash__())
 | 
						|
        self.assertNotEqual(slice(5), slice(6))
 | 
						|
 | 
						|
        with self.assertRaises(TypeError):
 | 
						|
            hash(slice(1, 2, []))
 | 
						|
 | 
						|
        with self.assertRaises(TypeError):
 | 
						|
            hash(slice(4, {}))
 | 
						|
 | 
						|
    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):
 | 
						|
        import pickle
 | 
						|
 | 
						|
        s = slice(10, 20, 3)
 | 
						|
        for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
 | 
						|
            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_copy(self):
 | 
						|
        s = slice(1, 10)
 | 
						|
        c = copy.copy(s)
 | 
						|
        self.assertIs(s, c)
 | 
						|
 | 
						|
        s = slice(1, 10, 2)
 | 
						|
        c = copy.copy(s)
 | 
						|
        self.assertIs(s, c)
 | 
						|
 | 
						|
        # Corner case for mutable indices:
 | 
						|
        s = slice([1, 2], [3, 4], [5, 6])
 | 
						|
        c = copy.copy(s)
 | 
						|
        self.assertIs(s, c)
 | 
						|
        self.assertIs(s.start, c.start)
 | 
						|
        self.assertIs(s.stop, c.stop)
 | 
						|
        self.assertIs(s.step, c.step)
 | 
						|
 | 
						|
    def test_deepcopy(self):
 | 
						|
        s = slice(1, 10)
 | 
						|
        c = copy.deepcopy(s)
 | 
						|
        self.assertEqual(s, c)
 | 
						|
 | 
						|
        s = slice(1, 10, 2)
 | 
						|
        c = copy.deepcopy(s)
 | 
						|
        self.assertEqual(s, c)
 | 
						|
 | 
						|
        # Corner case for mutable indices:
 | 
						|
        s = slice([1, 2], [3, 4], [5, 6])
 | 
						|
        c = copy.deepcopy(s)
 | 
						|
        self.assertIsNot(s, c)
 | 
						|
        self.assertEqual(s, c)
 | 
						|
        self.assertIsNot(s.start, c.start)
 | 
						|
        self.assertIsNot(s.stop, c.stop)
 | 
						|
        self.assertIsNot(s.step, c.step)
 | 
						|
 | 
						|
    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()
 |