diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-15-12-40-00.gh-issue-87613.Sl1ceV.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-15-12-40-00.gh-issue-87613.Sl1ceV.rst new file mode 100644 index 00000000000000..bd96edc0b9f177 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-15-12-40-00.gh-issue-87613.Sl1ceV.rst @@ -0,0 +1 @@ +Speed up calling :class:`slice` by adding a vectorcall implementation of the constructor generated by Argument Clinic. Patch by Pieter Eendebak. diff --git a/Objects/clinic/sliceobject.c.h b/Objects/clinic/sliceobject.c.h new file mode 100644 index 00000000000000..39fff4b06eb6a3 --- /dev/null +++ b/Objects/clinic/sliceobject.c.h @@ -0,0 +1,78 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +static PyObject * +slice_new_impl(PyTypeObject *type, PyObject *start, PyObject *stop, + PyObject *step); + +static PyObject * +slice_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = &PySlice_Type; + PyObject *start; + PyObject *stop = NULL; + PyObject *step = NULL; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("slice", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("slice", PyTuple_GET_SIZE(args), 1, 3)) { + goto exit; + } + start = PyTuple_GET_ITEM(args, 0); + if (PyTuple_GET_SIZE(args) < 2) { + goto skip_optional; + } + stop = PyTuple_GET_ITEM(args, 1); + if (PyTuple_GET_SIZE(args) < 3) { + goto skip_optional; + } + step = PyTuple_GET_ITEM(args, 2); +skip_optional: + return_value = slice_new_impl(type, start, stop, step); + +exit: + return return_value; +} + +static PyObject * +slice_vectorcall(PyObject *type, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ + PyObject *return_value = NULL; + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + PyObject *start; + PyObject *stop = NULL; + PyObject *step = NULL; + + assert(Py_Is(_PyType_CAST(type), &PySlice_Type)); + /* Make sure the type object is immutable: the generated + * vectorcall doesn't deal e.g. with users reassigning __init__. */ + assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE)); + if (!_PyArg_NoKwnames("slice", kwnames)) { + goto exit; + } + if (!_PyArg_CheckPositional("slice", nargs, 1, 3)) { + goto exit; + } + start = args[0]; + if (nargs < 2) { + goto skip_optional; + } + stop = args[1]; + if (nargs < 3) { + goto skip_optional; + } + step = args[2]; +skip_optional: + return_value = slice_new_impl(_PyType_CAST(type), start, stop, step); + +exit: + return return_value; +} +/*[clinic end generated code: output=3547c23a3ee3cd86 input=a9049054013a1b77]*/ diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 91d1753b822fc1..5ab7e42d9e7b76 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -21,6 +21,13 @@ this type and there is exactly one in existence. #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs +#include "clinic/sliceobject.c.h" + +/*[clinic input] +class slice "PySliceObject *" "&PySlice_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=857b514967456ef4]*/ + #define _PySlice_CAST(op) _Py_CAST(PySliceObject*, (op)) @@ -308,19 +315,22 @@ PySlice_GetIndicesEx(PyObject *_r, Py_ssize_t length, return 0; } -static PyObject * -slice_new(PyTypeObject *type, PyObject *args, PyObject *kw) -{ - PyObject *start, *stop, *step; +/*[clinic input] +@classmethod +@vectorcall +slice.__new__ as slice_new - start = stop = step = NULL; - - if (!_PyArg_NoKeywords("slice", kw)) - return NULL; - - if (!PyArg_UnpackTuple(args, "slice", 1, 3, &start, &stop, &step)) - return NULL; + start: object + stop: object(c_default="NULL") = None + step: object(c_default="NULL") = None + / +[clinic start generated code]*/ +static PyObject * +slice_new_impl(PyTypeObject *type, PyObject *start, PyObject *stop, + PyObject *step) +/*[clinic end generated code: output=35a6e86397efb677 input=cf5c572a0d2caa22]*/ +{ /* This swapping of stop and start is to maintain similarity with range(). */ if (stop == NULL) { @@ -702,4 +712,5 @@ PyTypeObject PySlice_Type = { 0, /* tp_init */ 0, /* tp_alloc */ slice_new, /* tp_new */ + .tp_vectorcall = slice_vectorcall, };