diff --git a/pandas/_libs/lib.pyi b/pandas/_libs/lib.pyi index 15bd5a7379105..946cbb7dbf6f1 100644 --- a/pandas/_libs/lib.pyi +++ b/pandas/_libs/lib.pyi @@ -44,6 +44,7 @@ def is_iterator(obj: object) -> bool: ... def is_scalar(val: object) -> bool: ... def is_list_like(obj: object, allow_sets: bool = ...) -> bool: ... def is_pyarrow_array(obj: object) -> bool: ... +def is_null_slice(obj: object) -> bool: ... def is_period(val: object) -> TypeGuard[Period]: ... def is_interval(val: object) -> TypeGuard[Interval]: ... def is_decimal(val: object) -> TypeGuard[Decimal]: ... diff --git a/pandas/_libs/lib.pyx b/pandas/_libs/lib.pyx index 0c0610f72044e..a9af158556f3e 100644 --- a/pandas/_libs/lib.pyx +++ b/pandas/_libs/lib.pyx @@ -29,6 +29,7 @@ from cpython.object cimport ( ) from cpython.ref cimport Py_INCREF from cpython.sequence cimport PySequence_Check +from cpython.slice cimport PySlice_Unpack from cpython.tuple cimport ( PyTuple_New, PyTuple_SET_ITEM, @@ -71,6 +72,7 @@ cdef extern from "Python.h": # Note: importing extern-style allows us to declare these as nogil # functions, whereas `from cpython cimport` does not. bint PyObject_TypeCheck(object obj, PyTypeObject* type) nogil + cdef Py_ssize_t PY_SSIZE_T_MAX cdef extern from "numpy/arrayobject.h": # cython's numpy.dtype specification is incorrect, which leads to @@ -1250,6 +1252,20 @@ def is_pyarrow_array(obj): return False +def is_null_slice(obj): + cdef Py_ssize_t start, stop, step + if isinstance(obj, slice): + try: + PySlice_Unpack(obj, &start, &stop, &step) + except TypeError: + return False + + if start == 0 and stop == PY_SSIZE_T_MAX and step == 1: + return True + + return False + + _TYPE_MAP = { "categorical": "categorical", "category": "categorical", diff --git a/pandas/core/common.py b/pandas/core/common.py index 8fd8b10c6fc32..2b243ec21818d 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -307,12 +307,7 @@ def is_null_slice(obj) -> bool: """ We have a null slice. """ - return ( - isinstance(obj, slice) - and obj.start is None - and obj.stop is None - and obj.step is None - ) + return lib.is_null_slice(obj) def is_empty_slice(obj) -> bool: