Skip to main content

Objects/sliceobject.c

Source:

cpython 3.14 @ ab2d84fe1023/Objects/sliceobject.c

Map

LinesSymbolPurpose
1–30includes, Py_Ellipsismodule-level singleton setup
31–60ellipsis_new, ellipsis_reprEllipsis type methods
61–120PySlice_New, slice_newconstructor, argument unpacking
121–180_PySlice_Unpackextract (start, stop, step) as C Py_ssize_t
181–260_PySlice_AdjustIndicesclamp indices to sequence length
261–320PySlice_GetIndicesExpublic wrapper, overflow-safe
321–360slice_indicesPython-level slice.indices() method
361–400PySlice_Type, slice_repr, slice_richcomparetype object and comparison

Reading

PySliceObject layout

A slice carries three PyObject* fields. All three are always set; None is stored when the caller omits start, stop, or step.

// CPython: Objects/sliceobject.c:1 PySliceObject
typedef struct {
PyObject_HEAD
PyObject *start;
PyObject *stop;
PyObject *step;
} PySliceObject;

slice_new (called via PySlice_New) validates step is not zero and then calls PyObject_GC_New. The three fields are incref'd individually, so the object owns references to all three.

Unpacking and index clamping

_PySlice_Unpack converts the three PyObject* fields to Py_ssize_t values without any knowledge of the sequence length. It handles None defaults (start defaults to 0 for positive step and PY_SSIZE_T_MAX for negative), and clips each raw integer to [PY_SSIZE_T_MIN+1, PY_SSIZE_T_MAX] before returning.

// CPython: Objects/sliceobject.c:121 _PySlice_Unpack
int
_PySlice_Unpack(PyObject *_r,
Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step)
{
PySliceObject *r = (PySliceObject*)_r;
/* ... step cannot be zero ... */
if (r->step == Py_None) {
*step = 1;
} else {
if (!_PyEval_SliceIndex(r->step, step)) return -1;
if (*step == 0) { PyErr_SetString(...); return -1; }
}
/* start/stop default logic follows */
}

_PySlice_AdjustIndices then takes the length of the concrete sequence and clamps the unpacked values:

// CPython: Objects/sliceobject.c:181 _PySlice_AdjustIndices
Py_ssize_t
_PySlice_AdjustIndices(Py_ssize_t length, Py_ssize_t *start,
Py_ssize_t *stop, Py_ssize_t step)

The function returns the number of items the slice selects, which is max(0, (stop - start + step - sign(step)) / step).

PySlice_GetIndicesEx and the Ellipsis singleton

PySlice_GetIndicesEx is the older public API. It calls _PySlice_Unpack followed by _PySlice_AdjustIndices, collapsing the two-step protocol into one call. It is kept for C extension compatibility; new code should call the two primitives directly.

The Ellipsis object is a singleton defined in the same file. ellipsis_new always returns the pre-allocated _Py_EllipsisObject and increments its reference count. PyEllipsis_Type marks it as not instantiable by users.

// CPython: Objects/sliceobject.c:31 ellipsis_new
static PyObject *
ellipsis_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
if (...) { /* disallow subclassing */ }
Py_INCREF(Py_Ellipsis);
return Py_Ellipsis;
}

gopy notes

Status: not yet ported.

Planned package path: objects/ (file slice.go alongside int.go, list.go, etc.).

Priority items for porting:

  • PySliceObject struct with Start, Stop, Step as *Object fields.
  • SliceNew mirroring PySlice_New argument order and None-defaulting behaviour.
  • SliceUnpack and SliceAdjustIndices as the two-step index protocol used by the VM's BUILD_SLICE and sequence subscription opcodes.
  • PySlice_GetIndicesEx as a convenience wrapper for C-extension compatibility shims.
  • The Ellipsis singleton as a package-level var Ellipsis *Object initialised once at startup.