https://github.com/python/cpython/commit/69368ebc33d26d7293149ecbbb9f8c3b0a049c77
commit: 69368ebc33d26d7293149ecbbb9f8c3b0a049c77
branch: 3.13
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-09-29T15:38:46+03:00
summary:

[3.13] gh-158102: Add tests for the slice C API (GH-158120) (GH-158293)

Test PySlice_Check(), PySlice_New(), PySlice_GetIndices(), PySlice_Unpack(),
PySlice_AdjustIndices() and PySlice_GetIndicesEx() -- both the macro and the
deprecated function.

Document the length and step precondition of PySlice_AdjustIndices()
(cherry picked from commit 8c056c33aacea5768f5e2010401f2b98ecd7a305)

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>

files:
A Lib/test/test_capi/test_slice.py
A Modules/_testlimitedcapi/slice.c
M Doc/c-api/slice.rst
M Modules/Setup.stdlib.in
M Modules/_testlimitedcapi.c
M Modules/_testlimitedcapi/parts.h
M PCbuild/_testlimitedcapi.vcxproj
M PCbuild/_testlimitedcapi.vcxproj.filters

diff --git a/Doc/c-api/slice.rst b/Doc/c-api/slice.rst
index c6d761fe7fd1c96..ce2b64a8aca8530 100644
--- a/Doc/c-api/slice.rst
+++ b/Doc/c-api/slice.rst
@@ -53,6 +53,7 @@ Slice Objects
    length *length*, and store the length of the slice in *slicelength*.  Out
    of bounds indices are clipped in a manner consistent with the handling of
    normal slices.
+   *length* must not be negative.
 
    Return ``0`` on success and ``-1`` on error with an exception set.
 
@@ -108,6 +109,10 @@ Slice Objects
    Out of bounds indices are clipped in a manner consistent with the handling
    of normal slices.
 
+   *length* must not be negative.
+   *step* must not be zero and must not be less than ``-PY_SSIZE_T_MAX``,
+   as guaranteed by :c:func:`PySlice_Unpack`.
+
    Return the length of the slice.  Always successful.  Doesn't call Python
    code.
 
diff --git a/Lib/test/test_capi/test_slice.py b/Lib/test/test_capi/test_slice.py
new file mode 100644
index 000000000000000..3f41b55a609f48f
--- /dev/null
+++ b/Lib/test/test_capi/test_slice.py
@@ -0,0 +1,307 @@
+import sys
+import unittest
+from test.support import import_helper
+
+_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
+
+NULL = None
+SSIZE_MAX = sys.maxsize
+SSIZE_MIN = -sys.maxsize - 1
+
+VALUES = [None, 0, 1, 3, 7, -1, -3, -7]
+STEPS = [None, 1, 3, 5, -1, -3, -5]
+LENGTHS = [0, 1, 3, 10]
+
+
+class Index:
+    def __init__(self, value):
+        self.value = value
+
+    def __index__(self):
+        return self.value
+
+
+class BadIndex:
+    def __index__(self):
+        raise RuntimeError('bad index')
+
+
+class PopIndex:
+    # __index__() removes the last item of the list.
+    def __init__(self, value, seq):
+        self.value = value
+        self.seq = seq
+
+    def __index__(self):
+        self.seq.pop()
+        return self.value
+
+
+class SliceTest(unittest.TestCase):
+
+    def test_check(self):
+        # Test PySlice_Check()
+        check = _testlimitedcapi.slice_check
+        self.assertTrue(check(slice(1, 7, 2)))
+        self.assertFalse(check(object()))
+
+        # CRASHES check(NULL)
+
+    def test_new(self):
+        # Test PySlice_New()
+        new = _testlimitedcapi.slice_new
+        self.assertEqual(new(1, 7, 2), slice(1, 7, 2))
+        self.assertEqual(new(7, 1, -2), slice(7, 1, -2))
+        self.assertEqual(new('a', 'b', 'c'), slice('a', 'b', 'c'))
+        self.assertEqual(new(NULL, NULL, NULL), slice(None, None, None))
+
+    def test_getindices(self):
+        # Test PySlice_GetIndices()
+        getindices = _testlimitedcapi.slice_getindices
+        self.assertEqual(getindices(slice(1, 7, 2), 10), (1, 7, 2))
+        self.assertEqual(getindices(slice(None), 10), (0, 10, 1))
+        self.assertEqual(getindices(slice(None, None, -1), 10), (9, -1, -1))
+        self.assertEqual(getindices(slice(-3, -1), 10), (7, 9, 1))
+        self.assertEqual(getindices(slice(-1, -3, -1), 10), (9, 7, -1))
+        self.assertEqual(getindices(slice(None, None, -2), 0), (-1, -1, -2))
+        self.assertEqual(getindices(slice(-3, -5, 1), 0), (-3, -5, 1))
+
+        # It fails without setting an exception for out of bounds indices,
+        # a zero step and non-integer indices.
+        self.assertIsNone(getindices(slice(1, 11), 10))
+        self.assertIsNone(getindices(slice(10, 1), 10))
+        self.assertIsNone(getindices(slice(1, 7, 0), 10))
+        self.assertIsNone(getindices(slice(Index(1)), 10))
+        self.assertIsNone(getindices(slice('a'), 10))
+        self.assertIsNone(getindices(slice(1, 'a'), 10))
+        self.assertIsNone(getindices(slice(1, 7, 'a'), 10))
+
+        # Negative length is not supported, but does not fail.
+        self.assertIsNone(getindices(slice(None), -3))
+        self.assertIsNone(getindices(slice(1, 7, 2), -3))
+        self.assertEqual(getindices(slice(-10, -5, 1), -3), (-13, -8, 1))
+        self.assertEqual(getindices(slice(-5, -10, -2), -3), (-8, -13, -2))
+
+        # CRASHES getindices(NULL, 10)
+        # CRASHES getindices(object(), 10)
+
+    def test_unpack(self):
+        # Test PySlice_Unpack()
+        unpack = _testlimitedcapi.slice_unpack
+        self.assertEqual(unpack(slice(1, 7)), (1, 7, 1))
+        self.assertEqual(unpack(slice(1, 7, 2)), (1, 7, 2))
+        self.assertEqual(unpack(slice(7, 1, -2)), (7, 1, -2))
+        self.assertEqual(unpack(slice(None, 7, 2)), (0, 7, 2))
+        self.assertEqual(unpack(slice(None, 7, -2)), (SSIZE_MAX, 7, -2))
+        self.assertEqual(unpack(slice(1, None, 2)), (1, SSIZE_MAX, 2))
+        self.assertEqual(unpack(slice(1, None, -2)), (1, SSIZE_MIN, -2))
+        self.assertEqual(unpack(slice(None)), (0, SSIZE_MAX, 1))
+        self.assertEqual(unpack(slice(None, None, -1)),
+                         (SSIZE_MAX, SSIZE_MIN, -1))
+        # Negative indices are not adjusted.
+        self.assertEqual(unpack(slice(-3, -1)), (-3, -1, 1))
+        self.assertEqual(unpack(slice(Index(1), Index(7), Index(2))),
+                         (1, 7, 2))
+
+        # Values which do not fit in Py_ssize_t are silently clipped.
+        self.assertEqual(unpack(slice(1, 2**1000)), (1, SSIZE_MAX, 1))
+        self.assertEqual(unpack(slice(1, -2**1000)), (1, SSIZE_MIN, 1))
+        self.assertEqual(unpack(slice(2**1000, 7)), (SSIZE_MAX, 7, 1))
+        self.assertEqual(unpack(slice(-2**1000, 7)), (SSIZE_MIN, 7, 1))
+        self.assertEqual(unpack(slice(1, 7, 2**1000)), (1, 7, SSIZE_MAX))
+        # The step is boosted to -PY_SSIZE_T_MAX, not PY_SSIZE_T_MIN, so
+        # that negating it is safe.
+        self.assertEqual(unpack(slice(7, 1, -2**1000)), (7, 1, -SSIZE_MAX))
+        self.assertEqual(unpack(slice(7, 1, SSIZE_MIN)), (7, 1, -SSIZE_MAX))
+
+        with self.assertRaisesRegex(ValueError, 'slice step cannot be zero'):
+            unpack(slice(1, 1, 0))
+        with self.assertRaisesRegex(TypeError,
+                                    'slice indices must be integers'):
+            unpack(slice('a', 7))
+        with self.assertRaisesRegex(TypeError,
+                                    'slice indices must be integers'):
+            unpack(slice(1, 'a'))
+        with self.assertRaisesRegex(TypeError,
+                                    'slice indices must be integers'):
+            unpack(slice(1, 7, 'a'))
+        with self.assertRaisesRegex(RuntimeError, 'bad index'):
+            unpack(slice(BadIndex(), 7))
+        with self.assertRaisesRegex(RuntimeError, 'bad index'):
+            unpack(slice(1, BadIndex()))
+        with self.assertRaisesRegex(RuntimeError, 'bad index'):
+            unpack(slice(1, 7, BadIndex()))
+
+        # CRASHES unpack(NULL)
+        # CRASHES unpack(object())
+
+    def test_adjustindices(self):
+        # Test PySlice_AdjustIndices()
+        adjust = _testlimitedcapi.slice_adjustindices
+        self.assertEqual(adjust(10, 1, 7, 1), (6, 1, 7))
+        self.assertEqual(adjust(10, 1, 7, 2), (3, 1, 7))
+        self.assertEqual(adjust(10, 7, 1, -1), (6, 7, 1))
+        self.assertEqual(adjust(10, 7, 1, -2), (3, 7, 1))
+        # An empty slice keeps the adjusted indices.
+        self.assertEqual(adjust(10, 7, 1, 1), (0, 7, 1))
+        self.assertEqual(adjust(10, 1, 7, -1), (0, 1, 7))
+
+        # Negative indices are added to the length.
+        self.assertEqual(adjust(10, -9, -3, 1), (6, 1, 7))
+        self.assertEqual(adjust(10, -3, -9, -1), (6, 7, 1))
+
+        # Out of bounds indices are clipped.
+        self.assertEqual(adjust(10, -100, 100, 1), (10, 0, 10))
+        self.assertEqual(adjust(10, 100, -100, -1), (10, 9, -1))
+        self.assertEqual(adjust(10, SSIZE_MIN, SSIZE_MAX, 1), (10, 0, 10))
+        self.assertEqual(adjust(10, SSIZE_MAX, SSIZE_MIN, -1), (10, 9, -1))
+        self.assertEqual(adjust(0, 1, 7, 1), (0, 0, 0))
+        self.assertEqual(adjust(0, 7, 1, -1), (0, -1, -1))
+
+        # The returned length is the length of the corresponding range.
+        for length in LENGTHS:
+            for start in VALUES[1:]:
+                for stop in VALUES[1:]:
+                    for step in STEPS[1:]:
+                        with self.subTest(length=length, start=start,
+                                          stop=stop, step=step):
+                            slicelength, start2, stop2 = adjust(length, start,
+                                                                stop, step)
+                            self.assertEqual(slicelength,
+                                             len(range(start2, stop2, step)))
+
+        # Negative length is not supported, but does not fail.
+        self.assertEqual(adjust(-3, 1, 7, 1), (0, -3, -3))
+        self.assertEqual(adjust(-3, 7, 1, -1), (0, -4, -4))
+        self.assertEqual(adjust(-3, -10, -5, 1), (0, 0, 0))
+
+        # The step is asserted to be neither zero nor less than
+        # -PY_SSIZE_T_MAX.
+        # CRASHES adjust(10, 0, 10, 0)
+        # CRASHES adjust(10, 0, 10, SSIZE_MIN)
+
+
+class GetIndicesExMacroTest(unittest.TestCase):
+    # PySlice_GetIndicesEx() is a macro using PySlice_Unpack() and
+    # PySlice_AdjustIndices().  It is also a deprecated function, exported
+    # for the stable ABI.
+    getindicesex = staticmethod(_testlimitedcapi.slice_getindicesex_macro)
+    getindicesex_seq = staticmethod(
+        _testlimitedcapi.slice_getindicesex_seq_macro)
+    # The macro evaluates the length after calling PySlice_Unpack(), so the
+    # size of the list after removing an item is used.
+    resized = (6, 8, 1, 2)
+
+    def test_getindicesex(self):
+        # Test PySlice_GetIndicesEx()
+        getindicesex = self.getindicesex
+        self.assertEqual(getindicesex(slice(1, 7, 2), 10), (1, 7, 2, 3))
+        self.assertEqual(getindicesex(slice(7, 1, -2), 10), (7, 1, -2, 3))
+        self.assertEqual(getindicesex(slice(Index(1), Index(7), Index(2)), 10),
+                         (1, 7, 2, 3))
+
+        # The result agrees with slice.indices() and the slice length is
+        # the length of the corresponding range.
+        for length in LENGTHS:
+            for start in VALUES:
+                for stop in VALUES:
+                    for step in STEPS:
+                        s = slice(start, stop, step)
+                        with self.subTest(slice=s, length=length):
+                            indices = s.indices(length)
+                            self.assertEqual(getindicesex(s, length),
+                                             indices + (len(range(*indices)),))
+
+        # Negative indices are added to the length.
+        self.assertEqual(getindicesex(slice(-9, -3), 10), (1, 7, 1, 6))
+        self.assertEqual(getindicesex(slice(-3, -9, -1), 10), (7, 1, -1, 6))
+
+        # Out of bounds indices are clipped.
+        self.assertEqual(getindicesex(slice(-100, 100), 10), (0, 10, 1, 10))
+        self.assertEqual(getindicesex(slice(100, -100, -1), 10),
+                         (9, -1, -1, 10))
+        self.assertEqual(getindicesex(slice(None), 0), (0, 0, 1, 0))
+        self.assertEqual(getindicesex(slice(1, 7, 2), 0), (0, 0, 2, 0))
+        self.assertEqual(getindicesex(slice(None, None, -1), 0),
+                         (-1, -1, -1, 0))
+
+        # Indices which do not fit in Py_ssize_t are clipped, not rejected.
+        # Note that slice.indices() does not clip the step.
+        self.assertEqual(getindicesex(slice(1, 2**1000), 10), (1, 10, 1, 9))
+        self.assertEqual(getindicesex(slice(2**1000, 7), 10), (10, 7, 1, 0))
+        self.assertEqual(getindicesex(slice(1, 7, 2**1000), 10),
+                         (1, 7, SSIZE_MAX, 1))
+        # -PY_SSIZE_T_MAX-1 is replaced with -PY_SSIZE_T_MAX.
+        self.assertEqual(getindicesex(slice(7, 1, -2**1000), 10),
+                         (7, 1, -SSIZE_MAX, 1))
+        self.assertEqual(getindicesex(slice(7, 1, SSIZE_MIN), 10),
+                         (7, 1, -SSIZE_MAX, 1))
+
+        with self.assertRaisesRegex(ValueError, 'slice step cannot be zero'):
+            getindicesex(slice(1, 7, 0), 10)
+        with self.assertRaisesRegex(TypeError,
+                                    'slice indices must be integers'):
+            getindicesex(slice('a', 7), 10)
+        with self.assertRaisesRegex(TypeError,
+                                    'slice indices must be integers'):
+            getindicesex(slice(1, 'a'), 10)
+        with self.assertRaisesRegex(TypeError,
+                                    'slice indices must be integers'):
+            getindicesex(slice(1, 7, 'a'), 10)
+        with self.assertRaisesRegex(RuntimeError, 'bad index'):
+            getindicesex(slice(BadIndex(), 7), 10)
+        with self.assertRaisesRegex(RuntimeError, 'bad index'):
+            getindicesex(slice(1, BadIndex()), 10)
+        with self.assertRaisesRegex(RuntimeError, 'bad index'):
+            getindicesex(slice(1, 7, BadIndex()), 10)
+
+        # Negative length is not supported, but does not fail.
+        self.assertEqual(getindicesex(slice(None), -3), (-3, -3, 1, 0))
+        self.assertEqual(getindicesex(slice(1, 7, 2), -3), (-3, -3, 2, 0))
+        self.assertEqual(getindicesex(slice(7, 1, -2), -3), (-4, -4, -2, 0))
+
+        # CRASHES getindicesex(NULL, 10)
+        # CRASHES getindicesex(object(), 10)
+
+    def test_getindicesex_seq(self):
+        # The length is the size of a sequence.
+        getindicesex_seq = self.getindicesex_seq
+        seq = list(range(10))
+        self.assertEqual(getindicesex_seq(slice(-3, -1), seq), (7, 9, 1, 2))
+        self.assertEqual(getindicesex_seq(slice(-3, -1), []), (0, 0, 1, 0))
+
+        # gh-72054: __index__() can resize the sequence.  Negative indices
+        # are adjusted by the length, so the result depends on when it is
+        # evaluated.
+        seq = list(range(10))
+        self.assertEqual(getindicesex_seq(slice(PopIndex(-3, seq), -1), seq),
+                         self.resized)
+        self.assertEqual(len(seq), 9, seq)
+
+        seq = list(range(10))
+        self.assertEqual(getindicesex_seq(slice(-3, PopIndex(-1, seq)), seq),
+                         self.resized)
+        self.assertEqual(len(seq), 9, seq)
+
+        seq = list(range(10))
+        self.assertEqual(
+            getindicesex_seq(slice(-3, -1, PopIndex(1, seq)), seq),
+            self.resized)
+        self.assertEqual(len(seq), 9, seq)
+
+        # CRASHES getindicesex_seq(slice(None), NULL)
+        # CRASHES getindicesex_seq(slice(None), object())
+
+
+class GetIndicesExFuncTest(GetIndicesExMacroTest):
+    # The deprecated function is equivalent to the macro, except that the
+    # length is evaluated before the call.
+    getindicesex = staticmethod(_testlimitedcapi.slice_getindicesex_func)
+    getindicesex_seq = staticmethod(
+        _testlimitedcapi.slice_getindicesex_seq_func)
+    # The size of the list before removing an item is used.
+    resized = (7, 9, 1, 2)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index e1bbb4010f1e95b..951eb96fe684e66 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -164,7 +164,7 @@
 @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
 @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c 
_testinternalcapi/test_lock.c _testinternalcapi/pytime.c 
_testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
 @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c 
_testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c 
_testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c 
_testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c 
_testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c 
_testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c 
_testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c 
_testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c 
_testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c 
_testcapi/weakref.c
-@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c 
_testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c 
_testlimitedcapi/bytes.c _testlimitedcapi/capsule.c _testlimitedcapi/complex.c 
_testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c 
_testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c 
_testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c 
_testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c 
_testlimitedcapi/tuple.c _testlimitedcapi/unicode.c 
_testlimitedcapi/vectorcall_limited.c _testlimitedcapi/file.c 
_testlimitedcapi/weakref.c _testlimitedcapi/run.c
+@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c 
_testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c 
_testlimitedcapi/bytes.c _testlimitedcapi/capsule.c _testlimitedcapi/complex.c 
_testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c 
_testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c 
_testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c 
_testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slice.c 
_testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c 
_testlimitedcapi/vectorcall_limited.c _testlimitedcapi/file.c 
_testlimitedcapi/weakref.c _testlimitedcapi/run.c
 @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
 @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
 
diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c
index e9136282104a11f..e4a84d2311ae58a 100644
--- a/Modules/_testlimitedcapi.c
+++ b/Modules/_testlimitedcapi.c
@@ -74,6 +74,9 @@ PyInit__testlimitedcapi(void)
     if (_PyTestLimitedCAPI_Init_Set(mod) < 0) {
         return NULL;
     }
+    if (_PyTestLimitedCAPI_Init_Slice(mod) < 0) {
+        return NULL;
+    }
     if (_PyTestLimitedCAPI_Init_Sys(mod) < 0) {
         return NULL;
     }
diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h
index 8f8136c4cd10194..c8db086d03bc097 100644
--- a/Modules/_testlimitedcapi/parts.h
+++ b/Modules/_testlimitedcapi/parts.h
@@ -37,6 +37,7 @@ int _PyTestLimitedCAPI_Init_List(PyObject *module);
 int _PyTestLimitedCAPI_Init_Long(PyObject *module);
 int _PyTestLimitedCAPI_Init_PyOS(PyObject *module);
 int _PyTestLimitedCAPI_Init_Set(PyObject *module);
+int _PyTestLimitedCAPI_Init_Slice(PyObject *module);
 int _PyTestLimitedCAPI_Init_Sys(PyObject *module);
 int _PyTestLimitedCAPI_Init_Tuple(PyObject *module);
 int _PyTestLimitedCAPI_Init_Unicode(PyObject *module);
diff --git a/Modules/_testlimitedcapi/slice.c b/Modules/_testlimitedcapi/slice.c
new file mode 100644
index 000000000000000..82ebf1cb6e58eeb
--- /dev/null
+++ b/Modules/_testlimitedcapi/slice.c
@@ -0,0 +1,238 @@
+#include "pyconfig.h"   // Py_GIL_DISABLED
+#ifndef Py_GIL_DISABLED
+   // Need limited C API 3.6.1 for PySlice_Unpack() and PySlice_AdjustIndices()
+   // and for PySlice_GetIndicesEx() implemented as a macro.
+#  define Py_LIMITED_API 0x03060100
+#endif
+
+#include "parts.h"
+#include "util.h"
+
+
+static PyObject *
+slice_check(PyObject *Py_UNUSED(module), PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PySlice_Check(obj));
+}
+
+static PyObject *
+slice_new(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    PyObject *start, *stop, *step;
+
+    if (!PyArg_ParseTuple(args, "OOO", &start, &stop, &step)) {
+        return NULL;
+    }
+    NULLABLE(start);
+    NULLABLE(stop);
+    NULLABLE(step);
+    return PySlice_New(start, stop, step);
+}
+
+/* Returns the (start, stop, step) triple on success.  If PySlice_GetIndices()
+ * fails without setting an exception, returns None. */
+static PyObject *
+slice_getindices(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    PyObject *slice;
+    Py_ssize_t length;
+    Py_ssize_t start = UNINITIALIZED_SIZE;
+    Py_ssize_t stop = UNINITIALIZED_SIZE;
+    Py_ssize_t step = UNINITIALIZED_SIZE;
+
+    if (!PyArg_ParseTuple(args, "On", &slice, &length)) {
+        return NULL;
+    }
+    NULLABLE(slice);
+    if (PySlice_GetIndices(slice, length, &start, &stop, &step) < 0) {
+        if (PyErr_Occurred()) {
+            return NULL;
+        }
+        Py_RETURN_NONE;
+    }
+    assert(!PyErr_Occurred());
+    assert(start != UNINITIALIZED_SIZE);
+    assert(stop != UNINITIALIZED_SIZE);
+    assert(step != UNINITIALIZED_SIZE);
+    return Py_BuildValue("nnn", start, stop, step);
+}
+
+/* Test PySlice_GetIndicesEx() implemented as a macro using PySlice_Unpack()
+ * and PySlice_AdjustIndices(). */
+static PyObject *
+slice_getindicesex_macro(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    PyObject *slice;
+    Py_ssize_t length = UNINITIALIZED_SIZE;
+    Py_ssize_t start = UNINITIALIZED_SIZE;
+    Py_ssize_t stop = UNINITIALIZED_SIZE;
+    Py_ssize_t step = UNINITIALIZED_SIZE;
+    Py_ssize_t slicelength = UNINITIALIZED_SIZE;
+
+    if (!PyArg_ParseTuple(args, "On", &slice, &length)) {
+        return NULL;
+    }
+    NULLABLE(slice);
+    if (PySlice_GetIndicesEx(slice, length,
+                             &start, &stop, &step, &slicelength) < 0) {
+        assert(PyErr_Occurred());
+        /* The macro sets the slice length to 0 on error. */
+        assert(slicelength == 0);
+        return NULL;
+    }
+    assert(!PyErr_Occurred());
+    assert(start != UNINITIALIZED_SIZE);
+    assert(stop != UNINITIALIZED_SIZE);
+    assert(step != UNINITIALIZED_SIZE);
+    assert(slicelength != UNINITIALIZED_SIZE);
+    return Py_BuildValue("nnnn", start, stop, step, slicelength);
+}
+
+/* Same as slice_getindicesex_macro(), but the length is the size of a 
sequence.
+ * The macro evaluates it after calling PySlice_Unpack(), which can execute
+ * arbitrary Python code and resize the sequence. */
+static PyObject *
+slice_getindicesex_seq_macro(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    PyObject *slice, *seq;
+    Py_ssize_t start = UNINITIALIZED_SIZE;
+    Py_ssize_t stop = UNINITIALIZED_SIZE;
+    Py_ssize_t step = UNINITIALIZED_SIZE;
+    Py_ssize_t slicelength = UNINITIALIZED_SIZE;
+
+    if (!PyArg_ParseTuple(args, "OO", &slice, &seq)) {
+        return NULL;
+    }
+    NULLABLE(slice);
+    NULLABLE(seq);
+    if (PySlice_GetIndicesEx(slice, Py_SIZE(seq),
+                             &start, &stop, &step, &slicelength) < 0) {
+        assert(PyErr_Occurred());
+        assert(slicelength == 0);
+        return NULL;
+    }
+    assert(!PyErr_Occurred());
+    return Py_BuildValue("nnnn", start, stop, step, slicelength);
+}
+
+static PyObject *
+slice_unpack(PyObject *Py_UNUSED(module), PyObject *slice)
+{
+    Py_ssize_t start = UNINITIALIZED_SIZE;
+    Py_ssize_t stop = UNINITIALIZED_SIZE;
+    Py_ssize_t step = UNINITIALIZED_SIZE;
+
+    NULLABLE(slice);
+    if (PySlice_Unpack(slice, &start, &stop, &step) < 0) {
+        assert(PyErr_Occurred());
+        return NULL;
+    }
+    assert(!PyErr_Occurred());
+    assert(start != UNINITIALIZED_SIZE);
+    assert(stop != UNINITIALIZED_SIZE);
+    assert(step != UNINITIALIZED_SIZE);
+    return Py_BuildValue("nnn", start, stop, step);
+}
+
+static PyObject *
+slice_adjustindices(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    Py_ssize_t length, start, stop, step;
+
+    if (!PyArg_ParseTuple(args, "nnnn", &length, &start, &stop, &step)) {
+        return NULL;
+    }
+    Py_ssize_t slicelength = PySlice_AdjustIndices(length, &start, &stop, 
step);
+    assert(!PyErr_Occurred());
+    return Py_BuildValue("nnn", slicelength, start, stop);
+}
+
+#undef PySlice_GetIndicesEx
+
+/* Test the deprecated PySlice_GetIndicesEx() function.  It is still exported
+ * for the stable ABI and used if Py_LIMITED_API is older than 3.5.4. */
+static PyObject *
+slice_getindicesex_func(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    PyObject *slice;
+    Py_ssize_t length;
+    Py_ssize_t start = UNINITIALIZED_SIZE;
+    Py_ssize_t stop = UNINITIALIZED_SIZE;
+    Py_ssize_t step = UNINITIALIZED_SIZE;
+    Py_ssize_t slicelength = UNINITIALIZED_SIZE;
+
+    if (!PyArg_ParseTuple(args, "On", &slice, &length)) {
+        return NULL;
+    }
+    NULLABLE(slice);
+// Ignore deprecation warnings
+_Py_COMP_DIAG_PUSH
+_Py_COMP_DIAG_IGNORE_DEPR_DECLS
+    int res = PySlice_GetIndicesEx(slice, length,
+                                   &start, &stop, &step, &slicelength);
+_Py_COMP_DIAG_POP
+    if (res < 0) {
+        assert(PyErr_Occurred());
+        return NULL;
+    }
+    assert(!PyErr_Occurred());
+    assert(start != UNINITIALIZED_SIZE);
+    assert(stop != UNINITIALIZED_SIZE);
+    assert(step != UNINITIALIZED_SIZE);
+    assert(slicelength != UNINITIALIZED_SIZE);
+    return Py_BuildValue("nnnn", start, stop, step, slicelength);
+}
+
+
+/* Same as slice_getindicesex_seq_macro(), but using the deprecated function.
+ * The length is evaluated before the call. */
+static PyObject *
+slice_getindicesex_seq_func(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    PyObject *slice, *seq;
+    Py_ssize_t start = UNINITIALIZED_SIZE;
+    Py_ssize_t stop = UNINITIALIZED_SIZE;
+    Py_ssize_t step = UNINITIALIZED_SIZE;
+    Py_ssize_t slicelength = UNINITIALIZED_SIZE;
+
+    if (!PyArg_ParseTuple(args, "OO", &slice, &seq)) {
+        return NULL;
+    }
+    NULLABLE(slice);
+    NULLABLE(seq);
+// Ignore deprecation warnings
+_Py_COMP_DIAG_PUSH
+_Py_COMP_DIAG_IGNORE_DEPR_DECLS
+    int res = PySlice_GetIndicesEx(slice, Py_SIZE(seq),
+                                   &start, &stop, &step, &slicelength);
+_Py_COMP_DIAG_POP
+    if (res < 0) {
+        assert(PyErr_Occurred());
+        return NULL;
+    }
+    assert(!PyErr_Occurred());
+    return Py_BuildValue("nnnn", start, stop, step, slicelength);
+}
+
+
+static PyMethodDef test_methods[] = {
+    {"slice_check", slice_check, METH_O},
+    {"slice_new", slice_new, METH_VARARGS},
+    {"slice_getindices", slice_getindices, METH_VARARGS},
+    {"slice_getindicesex_macro", slice_getindicesex_macro, METH_VARARGS},
+    {"slice_getindicesex_seq_macro", slice_getindicesex_seq_macro, 
METH_VARARGS},
+    {"slice_getindicesex_func", slice_getindicesex_func,
+     METH_VARARGS},
+    {"slice_getindicesex_seq_func", slice_getindicesex_seq_func,
+     METH_VARARGS},
+    {"slice_unpack", slice_unpack, METH_O},
+    {"slice_adjustindices", slice_adjustindices, METH_VARARGS},
+    {NULL},
+};
+
+int
+_PyTestLimitedCAPI_Init_Slice(PyObject *m)
+{
+    return PyModule_AddFunctions(m, test_methods);
+}
diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj
index 6baf75f5dff28ad..bc187cfeb93af80 100644
--- a/PCbuild/_testlimitedcapi.vcxproj
+++ b/PCbuild/_testlimitedcapi.vcxproj
@@ -109,6 +109,7 @@
     <ClCompile Include="..\Modules\_testlimitedcapi\object.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\set.c" />
+    <ClCompile Include="..\Modules\_testlimitedcapi\slice.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\sys.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\tuple.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\unicode.c" />
diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters 
b/PCbuild/_testlimitedcapi.vcxproj.filters
index 4fee09823c554c5..22c9ceadd19054b 100644
--- a/PCbuild/_testlimitedcapi.vcxproj.filters
+++ b/PCbuild/_testlimitedcapi.vcxproj.filters
@@ -24,6 +24,7 @@
     <ClCompile Include="..\Modules\_testlimitedcapi\object.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\set.c" />
+    <ClCompile Include="..\Modules\_testlimitedcapi\slice.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\sys.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\testcapi_long.h" />
     <ClCompile Include="..\Modules\_testlimitedcapi\tuple.c" />

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to