Skip to content

gh-158102: Add tests for the slice C API - #158120

Merged
serhiy-storchaka merged 4 commits into
python:mainfrom
serhiy-storchaka:slice-capi-tests
Sep 25, 2026
Merged

serhiy-storchaka merged 4 commits into
python:mainfrom
serhiy-storchaka:slice-capi-tests

Conversation

@serhiy-storchaka

Copy link
Copy Markdown
Member

Test PySlice_Check(), PySlice_New(), PySlice_GetIndices(), PySlice_Unpack(), PySlice_AdjustIndices() and PySlice_GetIndicesEx().

PySlice_GetIndicesEx() is tested both as a macro and as a deprecated function, including the case when __index__() resizes the sequence whose size is passed as the length: the macro evaluates it after PySlice_Unpack(), the function before the call.

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

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for writing an exhaustive test suite for the PySlice C API. It's more complicated than what I expected! Here is a first review.

Comment thread Lib/test/test_capi/test_slice.py
Comment thread Lib/test/test_capi/test_slice.py Outdated
Comment on lines +8 to +9
SIZE_MAX = sys.maxsize
SIZE_MIN = -sys.maxsize - 1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In C, SIZE_MAX is a different constant: size_t maximum (_testcapi.SIZE_MAX). Since this file tests C functions, it's confusing.

Can you rename the constants to SSIZE_MAX and SSIZE_MIN?

The _testcapi uses different names: PY_SSIZE_T_MAX and PY_SSIZE_T_MIN.

Comment thread Lib/test/test_capi/test_slice.py
Comment thread Lib/test/test_capi/test_slice.py Outdated
Comment on lines +11 to +13
VALUES = [None, 0, 1, 2, 3, 5, 7, -1, -2, -3, -5, -7]
STEPS = [None, 1, 2, 3, 5, -1, -2, -3, -5]
LENGTHS = [0, 1, 2, 5, 10]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

./python -m test test_capi.test_slice -v -m test_adjustindices takes 700 ms. I'm not sure that it's worth it to test so many combinations:

Suggested change
VALUES = [None, 0, 1, 2, 3, 5, 7, -1, -2, -3, -5, -7]
STEPS = [None, 1, 2, 3, 5, -1, -2, -3, -5]
LENGTHS = [0, 1, 2, 5, 10]
VALUES = [None, 0, 1, 3, 7, -1, -3, -7]
STEPS = [None, 1, 3, 5, -1, -3, -5]
LENGTHS = [0, 1, 3, 10]

Comment thread Lib/test/test_capi/test_slice.py Outdated
# 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, SIZE_MIN)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PySlice_Unpack() replaces step=PY_SSIZE_T_MIN with step=PY_SSIZE_T_MAX. Would it be acceptable do the same here? Well, if we change the behavior, I would prefer to do it in a separated PR. It's better to test the current behavior without changing code first, as you do :-)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally, PySlice_AdjustIndices() is always called after PySlice_Unpack(), which guarantees that step is never 0 or less than -SSIZE_MAX. An additional check here would be redundant.


# The step is asserted to be neither zero nor less than
# -PY_SSIZE_T_MAX.
# CRASHES adjust(10, 0, 10, 0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unfortunate that the function cannot fail. Can you update PySlice_AdjustIndices() document to mention that step must not be equal to zero and must not be equal to PY_SSIZE_T_MIN?

@serhiy-storchaka serhiy-storchaka Sep 25, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update the docs if they already do not mentioned this.

self.assertEqual(adjust(10, SIZE_MIN, SIZE_MAX, 1), (10, 0, 10))
self.assertEqual(adjust(10, SIZE_MAX, SIZE_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))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add tests on negative length?

Comment thread Modules/_testlimitedcapi/slice.c Outdated

// Need limited C API 3.6.1 for PySlice_Unpack() and PySlice_AdjustIndices()
// and for PySlice_GetIndicesEx() implemented as a macro.
#if !defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The limited C API can now be tested on Free Threading. You can also define Py_LIMITED_API if Py_GIL_DISABLED is defined, Py_TARGET_ABI3T is set to Py_LIMITED_API in this case by Include/pyabi.h.

Comment thread Modules/_testlimitedcapi/slice.c Outdated
/* Test PySlice_GetIndicesEx() implemented as a macro using PySlice_Unpack()
* and PySlice_AdjustIndices(). */
static PyObject *
slice_getindicesex(PyObject *Py_UNUSED(module), PyObject *args)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to rename this function to "slice_getindicesex_macro()", or rename "slice_getindicesex_deprecated()" to "slice_getindicesex_func()". Same remark for the test case class name.

Comment thread Lib/test/test_capi/test_slice.py Outdated
self.assertEqual(getindicesex(slice(7, 1, -2**1000), 10),
(7, 1, -SIZE_MAX, 1))
self.assertEqual(getindicesex(slice(7, 1, SIZE_MIN), 10),
(7, 1, -SIZE_MAX, 1))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly, PySlice_GetIndicesEx() also accepts negative length. You may also add tests on negative length.

serhiy-storchaka and others added 3 commits September 25, 2026 12:37
Co-authored-by: Victor Stinner <vstinner@python.org>
Also remove trailing whitespace added by a suggestion.
Rename SIZE_MAX and SIZE_MIN to SSIZE_MAX and SSIZE_MIN, and the
PySlice_GetIndicesEx() wrappers to slice_getindicesex_macro() and
slice_getindicesex_func().  Test the limited C API also on free threading.
Reduce the number of tested combinations.  Add tests for zero and negative
length, and document that the length must not be negative.

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the update. Tests should now cover all code paths.

@serhiy-storchaka
serhiy-storchaka enabled auto-merge (squash) September 25, 2026 11:02
@serhiy-storchaka
serhiy-storchaka merged commit 8c056c3 into python:main Sep 25, 2026
103 of 106 checks passed
@miss-islington-app

Copy link
Copy Markdown

Thanks @serhiy-storchaka for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13, 3.14, 3.15.
🐍🍒⛏🤖

@miss-islington-app

Copy link
Copy Markdown

Sorry, @serhiy-storchaka, I could not cleanly backport this to 3.15 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker 8c056c33aacea5768f5e2010401f2b98ecd7a305 3.15

@miss-islington-app

Copy link
Copy Markdown

Sorry, @serhiy-storchaka, I could not cleanly backport this to 3.14 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker 8c056c33aacea5768f5e2010401f2b98ecd7a305 3.14

@miss-islington-app

Copy link
Copy Markdown

Sorry, @serhiy-storchaka, I could not cleanly backport this to 3.13 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker 8c056c33aacea5768f5e2010401f2b98ecd7a305 3.13

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes needs backport to 3.15 pre-release feature fixes, bugs and security fixes skip news tests Tests in the Lib/test dir

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants