From 2d5d63bd93dde23c712e2f5d8c7d32d0f2764cd6 Mon Sep 17 00:00:00 2001 From: Vyas Ramasubramani Date: Tue, 4 Feb 2025 01:03:08 +0000 Subject: [PATCH 1/8] Remove dataframe protocol --- python/cudf/cudf/__init__.py | 4 +- python/cudf/cudf/core/dataframe.py | 45 +- python/cudf/cudf/core/df_protocol.py | 900 --------------------- python/cudf/cudf/tests/test_df_protocol.py | 286 ------- 4 files changed, 3 insertions(+), 1232 deletions(-) delete mode 100644 python/cudf/cudf/core/df_protocol.py delete mode 100644 python/cudf/cudf/tests/test_df_protocol.py diff --git a/python/cudf/cudf/__init__.py b/python/cudf/cudf/__init__.py index 843f2670b4d2..5f99522a0ae3 100644 --- a/python/cudf/cudf/__init__.py +++ b/python/cudf/cudf/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018-2024, NVIDIA CORPORATION. +# Copyright (c) 2018-2025, NVIDIA CORPORATION. # If libcudf was installed as a wheel, we must request it to load the library symbols. # Otherwise, we assume that the library was installed in a system path that ld can find. @@ -36,7 +36,7 @@ from cudf.api.types import dtype from cudf.core.algorithms import factorize, unique from cudf.core.cut import cut -from cudf.core.dataframe import DataFrame, from_dataframe, from_pandas, merge +from cudf.core.dataframe import DataFrame, from_pandas, merge from cudf.core.dtypes import ( CategoricalDtype, Decimal32Dtype, diff --git a/python/cudf/cudf/core/dataframe.py b/python/cudf/cudf/core/dataframe.py index bbd20aa05fcf..8ef0663a4e44 100644 --- a/python/cudf/cudf/core/dataframe.py +++ b/python/cudf/cudf/core/dataframe.py @@ -27,7 +27,6 @@ import pandas as pd import pyarrow as pa from nvtx import annotate -from packaging import version from pandas.io.formats import console from pandas.io.formats.printing import pprint_thing from typing_extensions import Self, assert_never @@ -48,7 +47,7 @@ is_scalar, is_string_dtype, ) -from cudf.core import column, df_protocol, indexing_utils, reshape +from cudf.core import column, indexing_utils, reshape from cudf.core._compat import PANDAS_LT_300 from cudf.core.buffer import acquire_spill_lock, as_buffer from cudf.core.column import ( @@ -5564,16 +5563,6 @@ def from_pandas(cls, dataframe, nan_as_null=no_default): # Checks duplicate columns and sets column metadata df.columns = dataframe.columns return df - elif hasattr(dataframe, "__dataframe__"): - # TODO: Probably should be handled in the constructor as - # this isn't pandas specific - assert version.parse(cudf.__version__) < version.parse("25.04.00") - warnings.warn( - "Support for loading dataframes via the `__dataframe__` interchange " - "protocol is deprecated", - FutureWarning, - ) - return from_dataframe(dataframe, allow_copy=True) else: raise TypeError( f"Could not construct DataFrame from {type(dataframe)}" @@ -7730,15 +7719,6 @@ def pct_change( periods=periods, freq=freq, **kwargs ) - def __dataframe__( - self, nan_as_null: bool = False, allow_copy: bool = True - ): - assert version.parse(cudf.__version__) < version.parse("25.04.00") - warnings.warn("Using `__dataframe__` is deprecated", FutureWarning) - return df_protocol.__dataframe__( - self, nan_as_null=nan_as_null, allow_copy=allow_copy - ) - def nunique(self, axis=0, dropna: bool = True) -> Series: """ Count number of distinct elements in specified axis. @@ -8183,29 +8163,6 @@ def from_pylibcudf(cls, table: plc.Table, metadata: dict) -> Self: return df -def from_dataframe(df, allow_copy: bool = False) -> DataFrame: - """ - Build a :class:`DataFrame` from an object supporting the dataframe interchange protocol. - - .. note:: - - If you have a ``pandas.DataFrame``, use :func:`from_pandas` instead. - - Parameters - ---------- - df : DataFrameXchg - Object supporting the interchange protocol, i.e. ``__dataframe__`` method. - allow_copy : bool, default: True - Whether to allow copying the memory to perform the conversion - (if false then zero-copy approach is requested). - - Returns - ------- - :class:`DataFrame` - """ - return df_protocol.from_dataframe(df, allow_copy=allow_copy) - - def make_binop_func(op, postprocess=None): # This function is used to wrap binary operations in Frame with an # appropriate API for DataFrame as required for pandas compatibility. The diff --git a/python/cudf/cudf/core/df_protocol.py b/python/cudf/cudf/core/df_protocol.py deleted file mode 100644 index cc9f39d70eff..000000000000 --- a/python/cudf/cudf/core/df_protocol.py +++ /dev/null @@ -1,900 +0,0 @@ -# Copyright (c) 2021-2025, NVIDIA CORPORATION. -from __future__ import annotations - -import enum -from collections import abc -from typing import TYPE_CHECKING, Any, cast - -import cupy as cp -import numpy as np -from numba.cuda import as_cuda_array - -import rmm - -import cudf -from cudf.core.buffer import Buffer, as_buffer -from cudf.core.column import ( - CategoricalColumn, - NumericalColumn, - as_column, - build_column, -) - -if TYPE_CHECKING: - from collections.abc import Iterable, Mapping, Sequence - -# Implementation of interchange protocol classes -# ---------------------------------------------- - - -class _DtypeKind(enum.IntEnum): - INT = 0 - UINT = 1 - FLOAT = 2 - BOOL = 20 - STRING = 21 # UTF-8 - DATETIME = 22 - CATEGORICAL = 23 - - -class _Device(enum.IntEnum): - CPU = 1 - CUDA = 2 - CPU_PINNED = 3 - OPENCL = 4 - VULKAN = 7 - METAL = 8 - VPI = 9 - ROCM = 10 - - -class _MaskKind(enum.IntEnum): - NON_NULLABLE = 0 - NAN = 1 - SENTINEL = 2 - BITMASK = 3 - BYTEMASK = 4 - - -_SUPPORTED_KINDS = { - _DtypeKind.INT, - _DtypeKind.UINT, - _DtypeKind.FLOAT, - _DtypeKind.CATEGORICAL, - _DtypeKind.BOOL, - _DtypeKind.STRING, -} -ProtoDtype = tuple[_DtypeKind, int, str, str] - - -class _CuDFBuffer: - """ - Data in the buffer is guaranteed to be contiguous in memory. - """ - - def __init__( - self, - buf: Buffer, - dtype: np.dtype, - allow_copy: bool = True, - ) -> None: - """ - Use Buffer object. - """ - # Store the cudf buffer where the data resides as a private - # attribute, so we can use it to retrieve the public attributes - self._buf = buf - self._dtype = dtype - self._allow_copy = allow_copy - - @property - def bufsize(self) -> int: - """ - The Buffer size in bytes. - """ - return self._buf.size - - @property - def ptr(self) -> int: - """ - Pointer to start of the buffer as an integer. - """ - return self._buf.get_ptr(mode="write") - - def __dlpack__(self): - # DLPack not implemented in NumPy yet, so leave it out here. - try: - cuda_array = as_cuda_array(self._buf).view(self._dtype) - return cp.asarray(cuda_array).toDlpack() - except ValueError: - raise TypeError(f"dtype {self._dtype} unsupported by `dlpack`") - - def __dlpack_device__(self) -> tuple[_Device, int]: - """ - _Device type and _Device ID for where the data in the buffer resides. - """ - return (_Device.CUDA, cp.asarray(self._buf).device.id) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}(" + str( - { - "bufsize": self.bufsize, - "ptr": self.ptr, - "device": self.__dlpack_device__()[0].name, - } - ) - +")" - - -class _CuDFColumn: - """ - A column object, with only the methods and properties required by the - interchange protocol defined. - - A column can contain one or more chunks. Each chunk can contain up to three - buffers - a data buffer, a mask buffer (depending on null representation), - and an offsets buffer (if variable-size binary; e.g., variable-length - strings). - - Note: this Column object can only be produced by ``__dataframe__``, so - doesn't need its own version or ``__column__`` protocol. - - """ - - def __init__( - self, - column: cudf.core.column.ColumnBase, - nan_as_null: bool = True, - allow_copy: bool = True, - ) -> None: - """ - Note: doesn't deal with extension arrays yet, just assume a regular - Series/ndarray for now. - """ - if not isinstance(column, cudf.core.column.ColumnBase): - raise TypeError( - "column must be a subtype of df.core.column.ColumnBase," - f"got {type(column)}" - ) - self._col = column - self._nan_as_null = nan_as_null - self._allow_copy = allow_copy - - def size(self) -> int: - """ - Size of the column, in elements. - """ - return self._col.size - - @property - def offset(self) -> int: - """ - Offset of first element. Always zero. - """ - return 0 - - @property - def dtype(self) -> ProtoDtype: - """ - Dtype description as a tuple - ``(kind, bit-width, format string, endianness)`` - - Kind : - - - INT = 0 - - UINT = 1 - - FLOAT = 2 - - BOOL = 20 - - STRING = 21 # UTF-8 - - DATETIME = 22 - - CATEGORICAL = 23 - - Bit-width : the number of bits as an integer - Format string : data type description format string in Apache Arrow C - Data Interface format. - Endianness : current only native endianness (``=``) is supported - - Notes - ----- - - Kind specifiers are aligned with DLPack where possible - (hence the jump to 20, leave enough room for future extension) - - Masks must be specified as boolean with either bit width 1 - (for bit masks) or 8 (for byte masks). - - Dtype width in bits was preferred over bytes - - Endianness isn't too useful, but included now in case - in the future we need to support non-native endianness - - Went with Apache Arrow format strings over NumPy format strings - because they're more complete from a dataframe perspective - - Format strings are mostly useful for datetime specification, - and for categoricals. - - For categoricals, the format string describes the type of the - categorical in the data buffer. In case of a separate encoding - of the categorical (e.g. an integer to string mapping), - this can be derived from ``self.describe_categorical``. - - Data types not included: complex, Arrow-style null, - binary, decimal, and nested (list, struct, map, union) dtypes. - """ - dtype = self._col.dtype - - # For now, assume that, if the column dtype is 'O' (i.e., `object`), - # then we have an array of strings - if not isinstance(dtype, cudf.CategoricalDtype) and dtype.kind == "O": - return (_DtypeKind.STRING, 8, "u", "=") - - return self._dtype_from_cudfdtype(dtype) - - def _dtype_from_cudfdtype(self, dtype) -> ProtoDtype: - """ - See `self.dtype` for details. - """ - # Note: 'c' (complex) not handled yet (not in array spec v1). - # 'b', 'B' (bytes), 'S', 'a', (old-style string) 'V' (void) - # not handled datetime and timedelta both map to datetime - # (is timedelta handled?) - _np_kinds = { - "i": _DtypeKind.INT, - "u": _DtypeKind.UINT, - "f": _DtypeKind.FLOAT, - "b": _DtypeKind.BOOL, - "U": _DtypeKind.STRING, - "M": _DtypeKind.DATETIME, - "m": _DtypeKind.DATETIME, - } - kind = _np_kinds.get(dtype.kind, None) - if kind is None: - # Not a NumPy/CuPy dtype. Check if it's a categorical maybe - if isinstance(dtype, cudf.CategoricalDtype): - kind = _DtypeKind.CATEGORICAL - # Codes and categories' dtypes are different. - # We use codes' dtype as these are stored in the buffer. - codes = cast( - cudf.core.column.CategoricalColumn, self._col - ).codes - dtype = codes.dtype - else: - raise ValueError( - f"Data type {dtype} not supported by exchange protocol" - ) - - if kind not in _SUPPORTED_KINDS: - raise NotImplementedError(f"Data type {dtype} not handled yet") - - bitwidth = dtype.itemsize * 8 - format_str = dtype.str - endianness = dtype.byteorder if kind != _DtypeKind.CATEGORICAL else "=" - return (kind, bitwidth, format_str, endianness) - - @property - def describe_categorical(self) -> tuple[bool, bool, dict[int, Any]]: - """ - If the dtype is categorical, there are two options: - - - There are only values in the data buffer. - - There is a separate dictionary-style encoding for categorical values. - - Raises TypeError if the dtype is not categorical - - Content of returned dict: - - - "is_ordered" : bool, whether the ordering of dictionary - indices is semantically meaningful. - - "is_dictionary" : bool, whether a dictionary-style mapping of - categorical values to other objects exists - - "mapping" : dict, Python-level only (e.g. ``{int: str}``). - None if not a dictionary-style categorical. - """ - if not self.dtype[0] == _DtypeKind.CATEGORICAL: - raise TypeError( - "`describe_categorical only works on " - "a column with categorical dtype!" - ) - categ_col = cast(cudf.core.column.CategoricalColumn, self._col) - ordered = bool(categ_col.dtype.ordered) - is_dictionary = True - # NOTE: this shows the children approach is better, transforming - # `categories` to a "mapping" dict is inefficient - categories = categ_col.categories - mapping = {ix: val for ix, val in enumerate(categories.values_host)} - return ordered, is_dictionary, mapping - - @property - def describe_null(self) -> tuple[int, Any]: - """ - Return the missing value (or "null") representation the column dtype - uses, as a tuple ``(kind, value)``. - - Kind: - - - 0 : non-nullable - - 1 : NaN/NaT - - 2 : sentinel value - - 3 : bit mask - - 4 : byte mask - - Value : if kind is "sentinel value", the actual value. - If kind is a bit mask or a byte mask, the value (0 or 1) - indicating a missing value. - None otherwise. - """ - kind = self.dtype[0] - if self.null_count == 0: - # there is no validity mask so it is non-nullable - return _MaskKind.NON_NULLABLE, None - - elif kind in _SUPPORTED_KINDS: - # currently, we return a bit mask - return _MaskKind.BITMASK, 0 - - else: - raise NotImplementedError( - f"Data type {self.dtype} not yet supported" - ) - - @property - def null_count(self) -> int: - """ - Number of null elements. Should always be known. - """ - return self._col.null_count - - @property - def metadata(self) -> dict[str, Any]: - """ - Store specific metadata of the column. - """ - return {} - - def num_chunks(self) -> int: - """ - Return the number of chunks the column consists of. - """ - return 1 - - def get_chunks( - self, n_chunks: int | None = None - ) -> Iterable["_CuDFColumn"]: - """ - Return an iterable yielding the chunks. - - See `DataFrame.get_chunks` for details on ``n_chunks``. - """ - return (self,) - - def get_buffers( - self, - ) -> Mapping[str, tuple[_CuDFBuffer, ProtoDtype] | None]: - """ - Return a dictionary containing the underlying buffers. - - The returned dictionary has the following contents: - - - "data": a two-element tuple whose first element is a buffer - containing the data and whose second element is the data - buffer's associated dtype. - - "validity": a two-element tuple whose first element is a buffer - containing mask values indicating missing data and - whose second element is the mask value buffer's - associated dtype. None if the null representation is - not a bit or byte mask. - - "offsets": a two-element tuple whose first element is a buffer - containing the offset values for variable-size binary - data (e.g., variable-length strings) and whose second - element is the offsets buffer's associated dtype. None - if the data buffer does not have an associated offsets - buffer. - """ - buffers = {} - try: - buffers["validity"] = self._get_validity_buffer() - except RuntimeError: - buffers["validity"] = None - - try: - buffers["offsets"] = self._get_offsets_buffer() - except RuntimeError: - buffers["offsets"] = None - - buffers["data"] = self._get_data_buffer() - - return buffers - - def _get_validity_buffer( - self, - ) -> tuple[_CuDFBuffer, ProtoDtype] | None: - """ - Return the buffer containing the mask values - indicating missing data and the buffer's associated dtype. - - Raises RuntimeError if null representation is not a bit or byte mask. - """ - null, invalid = self.describe_null - - if null == _MaskKind.BITMASK: - assert self._col.mask is not None - buffer = _CuDFBuffer( - self._col.mask, cp.uint8, allow_copy=self._allow_copy - ) - dtype = (_DtypeKind.UINT, 8, "C", "=") - return buffer, dtype - - elif null == _MaskKind.NAN: - raise RuntimeError( - "This column uses NaN as null so does not have a separate mask" - ) - elif null == _MaskKind.NON_NULLABLE: - raise RuntimeError( - "This column is non-nullable so does not have a mask" - ) - else: - raise NotImplementedError( - f"See {self.__class__.__name__}.describe_null method." - ) - - def _get_offsets_buffer( - self, - ) -> tuple[_CuDFBuffer, ProtoDtype] | None: - """ - Return the buffer containing the offset values for - variable-size binary data (e.g., variable-length strings) - and the buffer's associated dtype. - - Raises RuntimeError if the data buffer does not have an associated - offsets buffer. - """ - if self.dtype[0] == _DtypeKind.STRING: - offsets = self._col.children[0] - assert (offsets is not None) and (offsets.data is not None), " " - "offsets(.data) should not be None for string column" - - buffer = _CuDFBuffer( - offsets.data, offsets.dtype, allow_copy=self._allow_copy - ) - dtype = self._dtype_from_cudfdtype(offsets.dtype) - else: - raise RuntimeError( - "This column has a fixed-length dtype " - "so does not have an offsets buffer" - ) - - return buffer, dtype - - def _get_data_buffer( - self, - ) -> tuple[_CuDFBuffer, ProtoDtype]: - """ - Return the buffer containing the data and - the buffer's associated dtype. - """ - if self.dtype[0] in ( - _DtypeKind.INT, - _DtypeKind.UINT, - _DtypeKind.FLOAT, - _DtypeKind.BOOL, - ): - col_data = self._col - dtype = self.dtype - - elif self.dtype[0] == _DtypeKind.CATEGORICAL: - col_data = cast( - cudf.core.column.CategoricalColumn, self._col - ).codes - dtype = self._dtype_from_cudfdtype(col_data.dtype) - - elif self.dtype[0] == _DtypeKind.STRING: - col_data = build_column( - data=self._col.data, dtype=np.dtype("int8") - ) - dtype = self._dtype_from_cudfdtype(col_data.dtype) - - else: - raise NotImplementedError( - f"Data type {self._col.dtype} not handled yet" - ) - assert (col_data is not None) and (col_data.data is not None), " " - f"col_data(.data) should not be None when dtype = {dtype}" - buffer = _CuDFBuffer( - col_data.data, col_data.dtype, allow_copy=self._allow_copy - ) - - return buffer, dtype - - -class _CuDFDataFrame: - """ - A data frame class, with only the methods required by the interchange - protocol defined. - - Instances of this (private) class are returned from - ``cudf.DataFrame.__dataframe__`` as objects with the methods and - attributes defined on this class. - """ - - def __init__( - self, - df: "cudf.core.dataframe.DataFrame", - nan_as_null: bool = True, - allow_copy: bool = True, - ) -> None: - """ - Constructor - an instance of this (private) class is returned from - `cudf.DataFrame.__dataframe__`. - """ - self._df = df - # ``nan_as_null`` is a keyword intended for the consumer to tell the - # producer to overwrite null values in the data with - # ``NaN`` (or ``NaT``). - # This currently has no effect; once support for nullable extension - # dtypes is added, this value should be propagated to columns. - self._nan_as_null = nan_as_null - self._allow_copy = allow_copy - - def __dataframe__( - self, nan_as_null: bool = False, allow_copy: bool = True - ) -> "_CuDFDataFrame": - """ - See the docstring of the `cudf.DataFrame.__dataframe__` for details - """ - return _CuDFDataFrame( - self._df, nan_as_null=nan_as_null, allow_copy=allow_copy - ) - - @property - def metadata(self): - # `index` isn't a regular column, and the protocol doesn't support row - # labels - so we export it as cuDF-specific metadata here. - return {"cudf.index": self._df.index} - - def num_columns(self) -> int: - return len(self._df._column_names) - - def num_rows(self) -> int: - return len(self._df) - - def num_chunks(self) -> int: - return 1 - - def column_names(self) -> Iterable[str]: - return self._df._column_names - - def get_column(self, i: int) -> _CuDFColumn: - return _CuDFColumn( - as_column(self._df.iloc[:, i]), allow_copy=self._allow_copy - ) - - def get_column_by_name(self, name: str) -> _CuDFColumn: - return _CuDFColumn( - as_column(self._df[name]), allow_copy=self._allow_copy - ) - - def get_columns(self) -> Iterable[_CuDFColumn]: - return [ - _CuDFColumn(as_column(self._df[name]), allow_copy=self._allow_copy) - for name in self._df.columns - ] - - def select_columns(self, indices: Sequence[int]) -> "_CuDFDataFrame": - if not isinstance(indices, abc.Sequence): - raise ValueError("`indices` is not a sequence") - - return _CuDFDataFrame(self._df.iloc[:, indices]) - - def select_columns_by_name(self, names: Sequence[str]) -> "_CuDFDataFrame": - if not isinstance(names, abc.Sequence): - raise ValueError("`names` is not a sequence") - - return _CuDFDataFrame( - self._df.loc[:, names], self._nan_as_null, self._allow_copy - ) - - def get_chunks( - self, n_chunks: int | None = None - ) -> Iterable["_CuDFDataFrame"]: - """ - Return an iterator yielding the chunks. - """ - return (self,) - - -def __dataframe__( - self, nan_as_null: bool = False, allow_copy: bool = True -) -> _CuDFDataFrame: - """ - The public method to attach to cudf.DataFrame. - - ``nan_as_null`` is a keyword intended for the consumer to tell the - producer to overwrite null values in the data with ``NaN`` (or ``NaT``). - This currently has no effect; once support for nullable extension - dtypes is added, this value should be propagated to columns. - - ``allow_copy`` is a keyword that defines whether or not the library is - allowed to make a copy of the data. For example, copying data would be - necessary if a library supports strided buffers, given that this protocol - specifies contiguous buffers. - """ - return _CuDFDataFrame(self, nan_as_null=nan_as_null, allow_copy=allow_copy) - - -""" -Implementation of the dataframe exchange protocol. - -Public API ----------- - -from_dataframe : construct a cudf.DataFrame from an input data frame which - implements the exchange protocol - -Notes ------ - -- Interpreting a raw pointer (as in ``Buffer.ptr``) is annoying and - unsafe to do in pure Python. It's more general but definitely less friendly - than having ``to_arrow`` and ``to_numpy`` methods. So for the buffers which - lack ``__dlpack__`` (e.g., because the column dtype isn't supported by - DLPack), this is worth looking at again. - -""" - - -# A typing protocol could be added later to let Mypy validate code using -# `from_dataframe` better. -DataFrameObject = Any -ColumnObject = Any - - -_INTS = {8: cp.int8, 16: cp.int16, 32: cp.int32, 64: cp.int64} -_UINTS = {8: cp.uint8, 16: cp.uint16, 32: cp.uint32, 64: cp.uint64} -_FLOATS = {32: cp.float32, 64: cp.float64} -_CP_DTYPES = { - 0: _INTS, - 1: _UINTS, - 2: _FLOATS, - 20: {8: bool}, - 21: {8: cp.uint8}, -} - - -def from_dataframe( - df: DataFrameObject, allow_copy: bool = False -) -> cudf.DataFrame: - """ - Construct a ``DataFrame`` from ``df`` if it supports the - dataframe interchange protocol (``__dataframe__``). - - Parameters - ---------- - df : DataFrameObject - Object supporting dataframe interchange protocol - allow_copy : bool - If ``True``, allow copying of the data. If ``False``, a - ``TypeError`` is raised if data copying is required to - construct the ``DataFrame`` (e.g., if ``df`` lives in CPU - memory). - - Returns - ------- - DataFrame - - Examples - -------- - >>> import pandas as pd - >>> pdf = pd.DataFrame({'a': [1, 2, 3], 'b': ['x', 'y', 'z']}) - >>> df = cudf.from_dataframe(pdf, allow_copy=True) - >>> type(df) - cudf.core.dataframe.DataFrame - >>> df - a b - 0 1 x - 1 2 y - 2 3 z - - Notes - ----- - See https://data-apis.org/dataframe-protocol/latest/index.html - for the dataframe interchange protocol spec and API - """ - if isinstance(df, cudf.DataFrame): - return df - - if not hasattr(df, "__dataframe__"): - raise ValueError("`df` does not support __dataframe__") - - df = df.__dataframe__(allow_copy=allow_copy) - - # Check number of chunks, if there's more than one we need to iterate - if df.num_chunks() > 1: - raise NotImplementedError("More than one chunk not handled yet") - - # We need a dict of columns here, with each column being a cudf column. - columns = dict() - _buffers = [] # hold on to buffers, keeps memory alive - for name in df.column_names(): - col = df.get_column_by_name(name) - - if col.dtype[0] in ( - _DtypeKind.INT, - _DtypeKind.UINT, - _DtypeKind.FLOAT, - _DtypeKind.BOOL, - ): - columns[name], _buf = _protocol_to_cudf_column_numeric( - col, allow_copy - ) - - elif col.dtype[0] == _DtypeKind.CATEGORICAL: - columns[name], _buf = _protocol_to_cudf_column_categorical( - col, allow_copy - ) - - elif col.dtype[0] == _DtypeKind.STRING: - columns[name], _buf = _protocol_to_cudf_column_string( - col, allow_copy - ) - - else: - raise NotImplementedError( - f"Data type {col.dtype[0]} not handled yet" - ) - - _buffers.append(_buf) - - df_new = cudf.DataFrame._from_data(columns) - df_new._buffers = _buffers - return df_new - - -def _protocol_to_cudf_column_numeric( - col, allow_copy: bool -) -> tuple[ - cudf.core.column.ColumnBase, - Mapping[str, tuple[_CuDFBuffer, ProtoDtype] | None], -]: - """ - Convert an int, uint, float or bool protocol column - to the corresponding cudf column - """ - if col.offset != 0: - raise NotImplementedError("column.offset > 0 not handled yet") - - buffers = col.get_buffers() - assert buffers["data"] is not None, "data buffer should not be None" - _dbuffer, _ddtype = buffers["data"] - _dbuffer = _ensure_gpu_buffer(_dbuffer, _ddtype, allow_copy) - cudfcol_num = build_column( - _dbuffer._buf, - protocol_dtype_to_cupy_dtype(_ddtype), - ) - return _set_missing_values(col, cudfcol_num, allow_copy), buffers - - -def _ensure_gpu_buffer(buf, data_type, allow_copy: bool) -> _CuDFBuffer: - # if `buf` is a (protocol) buffer that lives on the GPU already, - # return it as is. Otherwise, copy it to the device and return - # the resulting buffer. - if buf.__dlpack_device__()[0] != _Device.CUDA: - if allow_copy: - dbuf = rmm.DeviceBuffer(ptr=buf.ptr, size=buf.bufsize) - return _CuDFBuffer( - as_buffer(dbuf, exposed=True), - protocol_dtype_to_cupy_dtype(data_type), - allow_copy, - ) - else: - raise TypeError( - "This operation must copy data from CPU to GPU. " - "Set `allow_copy=True` to allow it." - ) - return buf - - -def _set_missing_values( - protocol_col, - cudf_col: cudf.core.column.ColumnBase, - allow_copy: bool, -) -> cudf.core.column.ColumnBase: - valid_mask = protocol_col.get_buffers()["validity"] - if valid_mask is not None: - null, invalid = protocol_col.describe_null - if null == _MaskKind.BYTEMASK: - valid_mask = _ensure_gpu_buffer( - valid_mask[0], valid_mask[1], allow_copy - ) - bitmask = as_column(valid_mask._buf, dtype="bool").as_mask() - return cudf_col.set_mask(bitmask) - elif null == _MaskKind.BITMASK: - valid_mask = _ensure_gpu_buffer( - valid_mask[0], valid_mask[1], allow_copy - ) - bitmask = valid_mask._buf - return cudf_col.set_mask(bitmask) - return cudf_col - - -def protocol_dtype_to_cupy_dtype(_dtype: ProtoDtype) -> cp.dtype: - kind = _dtype[0] - bitwidth = _dtype[1] - if _dtype[0] not in _SUPPORTED_KINDS: - raise RuntimeError(f"Data type {_dtype[0]} not handled yet") - - return _CP_DTYPES[kind][bitwidth] - - -def _protocol_to_cudf_column_categorical( - col, allow_copy: bool -) -> tuple[ - cudf.core.column.ColumnBase, - Mapping[str, tuple[_CuDFBuffer, ProtoDtype] | None], -]: - """ - Convert a categorical column to a Series instance - """ - ordered, is_dict, categories = col.describe_categorical - if not is_dict: - raise NotImplementedError( - "Non-dictionary categoricals not supported yet" - ) - buffers = col.get_buffers() - assert buffers["data"] is not None, "data buffer should not be None" - codes_buffer, codes_dtype = buffers["data"] - codes_buffer = _ensure_gpu_buffer(codes_buffer, codes_dtype, allow_copy) - cdtype = np.dtype(protocol_dtype_to_cupy_dtype(codes_dtype)) - codes = NumericalColumn( - data=codes_buffer._buf, - size=None, - dtype=cdtype, - ) - cudfcol = CategoricalColumn( - data=None, - size=codes.size, - dtype=cudf.CategoricalDtype(categories=categories, ordered=ordered), - mask=codes.base_mask, - offset=codes.offset, - children=(codes,), - ) - - return _set_missing_values(col, cudfcol, allow_copy), buffers - - -def _protocol_to_cudf_column_string( - col, allow_copy: bool -) -> tuple[ - cudf.core.column.ColumnBase, - Mapping[str, tuple[_CuDFBuffer, ProtoDtype] | None], -]: - """ - Convert a string ColumnObject to cudf Column object. - """ - # Retrieve the data buffers - buffers = col.get_buffers() - - # Retrieve the data buffer containing the UTF-8 code units - assert buffers["data"] is not None, "data buffer should never be None" - data_buffer, data_dtype = buffers["data"] - data_buffer = _ensure_gpu_buffer(data_buffer, data_dtype, allow_copy) - encoded_string = build_column( - data_buffer._buf, - protocol_dtype_to_cupy_dtype(data_dtype), - ) - - # Retrieve the offsets buffer containing the index offsets demarcating - # the beginning and end of each string - assert buffers["offsets"] is not None, "not possible for string column" - offset_buffer, offset_dtype = buffers["offsets"] - offset_buffer = _ensure_gpu_buffer(offset_buffer, offset_dtype, allow_copy) - offsets = build_column( - offset_buffer._buf, - protocol_dtype_to_cupy_dtype(offset_dtype), - ) - offsets = offsets.astype("int32") - cudfcol_str = build_column( - None, dtype=cp.dtype("O"), children=(offsets, encoded_string) - ) - return _set_missing_values(col, cudfcol_str, allow_copy), buffers - - -def _protocol_buffer_to_cudf_buffer(protocol_buffer): - return as_buffer( - rmm.DeviceBuffer( - ptr=protocol_buffer.ptr, size=protocol_buffer.bufsize - ), - exposed=True, - ) diff --git a/python/cudf/cudf/tests/test_df_protocol.py b/python/cudf/cudf/tests/test_df_protocol.py deleted file mode 100644 index 371db773805c..000000000000 --- a/python/cudf/cudf/tests/test_df_protocol.py +++ /dev/null @@ -1,286 +0,0 @@ -# Copyright (c) 2021-2025, NVIDIA CORPORATION. -from __future__ import annotations - -from typing import Any - -import cupy as cp -import pandas as pd -import pytest - -import cudf -from cudf.core.buffer import as_buffer -from cudf.core.column import as_column, build_column -from cudf.core.df_protocol import ( - DataFrameObject, - _CuDFBuffer, - _CuDFColumn, - _DtypeKind, - _MaskKind, - _protocol_buffer_to_cudf_buffer, - from_dataframe, - protocol_dtype_to_cupy_dtype, -) -from cudf.testing import assert_eq - -pytestmark = pytest.mark.filterwarnings( - "ignore:Using `__dataframe__` is deprecated:FutureWarning" -) - - -@pytest.fixture( - params=[ - {"a": [1, 2, 3], "b": ["x", "y", "z"]}, - {"a": [1, 2, None], "b": ["x", "y", "z"]}, - {"a": [1, 2, 3], "b": pd.Categorical(["x", "y", None])}, - ] -) -def pandas_df(request): - data = request.param - return pd.DataFrame(data) - - -def assert_validity_equal(protocol_buffer, cudf_buffer, size, null, valid): - if null == _MaskKind.BYTEMASK: - protocol_mask = _protocol_buffer_to_cudf_buffer(protocol_buffer) - assert_eq( - as_column(protocol_mask, dtype="bool"), - as_column(cudf_buffer, dtype="bool"), - ) - elif null == _MaskKind.BITMASK: - protocol_mask = _protocol_buffer_to_cudf_buffer(protocol_buffer) - cudf_mask = cudf_buffer - assert_eq( - build_column( - as_buffer(cp.zeros(10, dtype="int8")), - "int8", - size=size, - mask=protocol_mask, - children=(), - ), - build_column( - as_buffer(cp.zeros(10, dtype="int8")), - "int8", - size=size, - mask=cudf_mask, - children=(), - ), - ) - else: - raise NotImplementedError() - - -def assert_buffer_equal(buffer_and_dtype: tuple[_CuDFBuffer, Any], cudfcol): - buf, dtype = buffer_and_dtype - device_id = cp.asarray(cudfcol.data).device.id - assert buf.__dlpack_device__() == (2, device_id) - col_from_buf = build_column( - _protocol_buffer_to_cudf_buffer(buf), - protocol_dtype_to_cupy_dtype(dtype), - ) - # check that non null values are the equals as nulls are represented - # by sentinel values in the buffer. - # FIXME: In gh-10202 some minimal fixes were added to unblock CI. But - # currently only non-null values are compared, null positions are - # unchecked. - non_null_idxs = cudfcol.notnull() - assert_eq( - col_from_buf.apply_boolean_mask(non_null_idxs), - cudfcol.apply_boolean_mask(non_null_idxs), - ) - array_from_dlpack = cp.from_dlpack(buf.__dlpack__()).get() - col_array = cp.asarray(cudfcol.data_array_view(mode="read")).get() - assert_eq( - array_from_dlpack[non_null_idxs.values_host].flatten(), - col_array[non_null_idxs.values_host].flatten(), - ) - - -def assert_column_equal(col: _CuDFColumn, cudfcol): - assert col.size() == cudfcol.size - assert col.offset == 0 - assert col.null_count == cudfcol.null_count - assert col.num_chunks() == 1 - if col.null_count == 0: - pytest.raises(RuntimeError, col._get_validity_buffer) - assert col.get_buffers()["validity"] is None - else: - assert_validity_equal( - col.get_buffers()["validity"][0], - cudfcol.mask, - cudfcol.size, - *col.describe_null, - ) - - if col.dtype[0] == _DtypeKind.CATEGORICAL: - assert_buffer_equal(col.get_buffers()["data"], cudfcol.codes) - assert col.get_buffers()["offsets"] is None - - elif col.dtype[0] == _DtypeKind.STRING: - chars_col = build_column(data=cudfcol.data, dtype="int8") - assert_buffer_equal(col.get_buffers()["data"], chars_col) - assert_buffer_equal(col.get_buffers()["offsets"], cudfcol.children[0]) - - else: - assert_buffer_equal(col.get_buffers()["data"], cudfcol) - assert col.get_buffers()["offsets"] is None - - if col.null_count == 0: - assert col.describe_null == (0, None) - else: - assert col.describe_null == (3, 0) - - -def assert_dataframe_equal(dfo: DataFrameObject, df: cudf.DataFrame): - assert dfo.num_columns() == len(df.columns) - assert dfo.num_rows() == len(df) - assert dfo.num_chunks() == 1 - assert dfo.column_names() == tuple(df.columns) - for col in df.columns: - assert_column_equal(dfo.get_column_by_name(col), df[col]._column) - - -def assert_from_dataframe_equals(dfobj, allow_copy): - df2 = from_dataframe(dfobj, allow_copy=allow_copy) - - assert_dataframe_equal(dfobj.__dataframe__(allow_copy), df2) - if isinstance(dfobj, cudf.DataFrame): - assert_eq(dfobj, df2) - - elif isinstance(dfobj, pd.DataFrame): - assert_eq(cudf.DataFrame(dfobj), df2) - - else: - raise TypeError(f"{type(dfobj)} not supported yet.") - - -def test_from_dataframe_exception(pandas_df): - exception_msg = "This operation must copy data from CPU to GPU." - " Set `allow_copy=True` to allow it." - with pytest.raises(TypeError, match=exception_msg): - from_dataframe(pandas_df) - - -def assert_df_unique_dtype_cols(data): - cdf = cudf.DataFrame(data=data) - assert_from_dataframe_equals(cdf, allow_copy=False) - assert_from_dataframe_equals(cdf, allow_copy=True) - - -def test_from_dataframe(): - data = dict(a=[1, 2, 3], b=[9, 10, 11]) - df1 = cudf.DataFrame(data=data) - df2 = cudf.from_dataframe(df1) - assert_eq(df1, df2) - - df3 = cudf.from_dataframe(df2) - assert_eq(df1, df3) - - -def test_int_dtype(): - data_int = dict(a=[1, 2, 3], b=[9, 10, 11]) - assert_df_unique_dtype_cols(data_int) - - -def test_float_dtype(): - data_float = dict(a=[1.5, 2.5, 3.5], b=[9.2, 10.5, 11.8]) - assert_df_unique_dtype_cols(data_float) - - -def test_categorical_dtype(): - cdf = cudf.DataFrame({"A": [1, 2, 5, 1]}) - cdf["A"] = cdf["A"].astype("category") - col = cdf.__dataframe__().get_column_by_name("A") - assert col.dtype[0] == _DtypeKind.CATEGORICAL - assert col.describe_categorical == (False, True, {0: 1, 1: 2, 2: 5}) - assert_from_dataframe_equals(cdf, allow_copy=False) - assert_from_dataframe_equals(cdf, allow_copy=True) - - -def test_bool_dtype(): - data_bool = dict(a=[True, True, False], b=[False, True, False]) - assert_df_unique_dtype_cols(data_bool) - - -def test_string_dtype(): - data_string = dict(a=["a", "b", "cdef", "", "g"]) - assert_df_unique_dtype_cols(data_string) - - -def test_mixed_dtype(): - data_mixed = dict( - int=[1, 2, 3], - float=[1.5, 2.5, 3.5], - bool=[True, False, True], - categorical=[5, 1, 5], - string=["rapidsai-cudf ", "", "df protocol"], - ) - assert_df_unique_dtype_cols(data_mixed) - - -def test_NA_int_dtype(): - data_int = dict( - a=[1, None, 3, None, 5], - b=[9, 10, None, 7, 8], - c=[6, 19, 20, 100, 1000], - ) - assert_df_unique_dtype_cols(data_int) - - -def test_NA_float_dtype(): - data_float = dict( - a=[1.4, None, 3.6, None, 5.2], - b=[9.7, 10.9, None, 7.8, 8.2], - c=[6.1, 19.2, 20.3, 100.4, 1000.5], - ) - assert_df_unique_dtype_cols(data_float) - - -def test_NA_categorical_dtype(): - df = cudf.DataFrame({"A": [1, 2, 5, 1]}) - df["B"] = df["A"].astype("category") - df.at[[1, 3], "B"] = None # Set two items to null - - # Some detailed testing for correctness of dtype and null handling: - col = df.__dataframe__().get_column_by_name("B") - assert col.dtype[0] == _DtypeKind.CATEGORICAL - assert col.null_count == 2 - assert col.describe_null == (3, 0) - assert col.num_chunks() == 1 - assert col.describe_categorical == (False, True, {0: 1, 1: 2, 2: 5}) - assert_from_dataframe_equals(df, allow_copy=False) - assert_from_dataframe_equals(df, allow_copy=True) - - -def test_NA_bool_dtype(): - data_bool = dict(a=[None, True, False], b=[False, None, None]) - assert_df_unique_dtype_cols(data_bool) - - -def test_NA_string_dtype(): - df = cudf.DataFrame({"A": ["a", "b", "cdef", "", "g"]}) - df["B"] = df["A"].astype("object") - df.at[1, "B"] = cudf.NA # Set one item to null - - # Test for correctness and null handling: - col = df.__dataframe__().get_column_by_name("B") - assert col.dtype[0] == _DtypeKind.STRING - assert col.null_count == 1 - assert col.describe_null == (3, 0) - assert col.num_chunks() == 1 - assert_from_dataframe_equals(df, allow_copy=False) - assert_from_dataframe_equals(df, allow_copy=True) - - -def test_NA_mixed_dtype(): - data_mixed = dict( - int=[1, None, 2, 3, 1000], - float=[None, 1.5, 2.5, 3.5, None], - bool=[True, None, False, None, None], - categorical=[5, 1, 5, 3, None], - string=[None, None, None, "df protocol", None], - ) - assert_df_unique_dtype_cols(data_mixed) - - -def test_from_cpu_df(pandas_df): - cudf.from_dataframe(pandas_df, allow_copy=True) From 0e1dadb4b3436bbf41d84cc8aba6b01fb7afe79c Mon Sep 17 00:00:00 2001 From: Vyas Ramasubramani Date: Tue, 4 Feb 2025 02:36:09 +0000 Subject: [PATCH 2/8] Remove test --- .../cudf_pandas_tests/test_cudf_pandas.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/python/cudf/cudf_pandas_tests/test_cudf_pandas.py b/python/cudf/cudf_pandas_tests/test_cudf_pandas.py index 3e8b6d5786c2..81e0f09f7956 100644 --- a/python/cudf/cudf_pandas_tests/test_cudf_pandas.py +++ b/python/cudf/cudf_pandas_tests/test_cudf_pandas.py @@ -1221,25 +1221,6 @@ def test_intermediates_are_proxied(): assert isinstance(grouper, xpd.core.groupby.generic.DataFrameGroupBy) -def test_from_dataframe(): - cudf = pytest.importorskip("cudf") - from cudf.testing import assert_eq - - data = {"foo": [1, 2, 3], "bar": [4, 5, 6]} - - cudf_pandas_df = xpd.DataFrame(data) - cudf_df = cudf.DataFrame(data) - - # test construction of a cuDF DataFrame from an cudf_pandas DataFrame - assert_eq(cudf_df, cudf.DataFrame.from_pandas(cudf_pandas_df)) - assert_eq(cudf_df, cudf.from_dataframe(cudf_pandas_df)) - - # ideally the below would work as well, but currently segfaults - - # pd_df = pd.DataFrame(data) - # assert_eq(pd_df, pd.api.interchange.from_dataframe(cudf_pandas_df)) - - def test_multiindex_values_returns_1d_tuples(): mi = xpd.MultiIndex.from_tuples([(1, 2), (3, 4)]) result = mi.values From 9f4c9de84fb0dcf5ab4b594c4148bdcde49f5128 Mon Sep 17 00:00:00 2001 From: Vyas Ramasubramani Date: Tue, 4 Feb 2025 04:20:08 +0000 Subject: [PATCH 3/8] Remove doc reference --- docs/cudf/source/user_guide/api_docs/general_functions.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/cudf/source/user_guide/api_docs/general_functions.rst b/docs/cudf/source/user_guide/api_docs/general_functions.rst index 5c5b5cb3b041..350fc372130d 100644 --- a/docs/cudf/source/user_guide/api_docs/general_functions.rst +++ b/docs/cudf/source/user_guide/api_docs/general_functions.rst @@ -26,7 +26,6 @@ Top-level conversions :toctree: api/ to_numeric - from_dataframe from_dlpack from_pandas From 188542d6d54f36785c323154dedb7fac31e8cfcd Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Tue, 4 Feb 2025 17:51:14 -0600 Subject: [PATCH 4/8] Update run.sh --- ci/cudf_pandas_scripts/pandas-tests/run.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/cudf_pandas_scripts/pandas-tests/run.sh b/ci/cudf_pandas_scripts/pandas-tests/run.sh index 15970a4185c8..81ee292b67d2 100755 --- a/ci/cudf_pandas_scripts/pandas-tests/run.sh +++ b/ci/cudf_pandas_scripts/pandas-tests/run.sh @@ -30,6 +30,7 @@ mkdir -p "${RAPIDS_TESTS_DIR}" bash python/cudf/cudf/pandas/scripts/run-pandas-tests.sh \ -n 5 \ --tb=no \ + --durations=0 \ -m "not slow" \ --max-worker-restart=3 \ --junitxml="${RAPIDS_TESTS_DIR}/junit-cudf-pandas.xml" \ From 2ecc11458e911aca33b3b32fe01a94b381d560d2 Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Tue, 4 Feb 2025 17:51:42 -0600 Subject: [PATCH 5/8] Update run-pandas-tests.sh --- python/cudf/cudf/pandas/scripts/run-pandas-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh b/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh index 9b9ce0265712..09f98f20ef10 100755 --- a/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh +++ b/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh @@ -135,7 +135,7 @@ and not test_eof_states \ and not test_array_tz" # TODO: Remove "not db" once a postgres & mysql container is set up on the CI -PANDAS_CI="1" timeout 90m python -m pytest -p cudf.pandas \ +PANDAS_CI="1" timeout 900m python -m pytest -p cudf.pandas \ -v -m "not single_cpu and not db" \ -k "$TEST_THAT_NEED_MOTO_SERVER and $TEST_THAT_CRASH_PYTEST_WORKERS and not test_groupby_raises_category_on_category and not test_constructor_no_pandas_array and not test_is_monotonic_na and not test_index_contains and not test_index_contains and not test_frame_op_subclass_nonclass_constructor and not test_round_trip_current" \ --import-mode=importlib \ From 0c592b533d848618df2b88dfbfb33069e9e776a7 Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Tue, 4 Feb 2025 18:06:00 -0600 Subject: [PATCH 6/8] Update run-pandas-tests.sh --- python/cudf/cudf/pandas/scripts/run-pandas-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh b/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh index 09f98f20ef10..fdb57aa969b7 100755 --- a/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh +++ b/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# SPDX-FileCopyrightText: Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. +# SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. # All rights reserved. # SPDX-License-Identifier: Apache-2.0 From f3886ce10d391b4e7256574ac0b823ef70ecbf24 Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Wed, 5 Feb 2025 20:04:43 -0600 Subject: [PATCH 7/8] Update run-pandas-tests.sh --- python/cudf/cudf/pandas/scripts/run-pandas-tests.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh b/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh index fdb57aa969b7..fe8a0ef24f39 100755 --- a/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh +++ b/python/cudf/cudf/pandas/scripts/run-pandas-tests.sh @@ -24,7 +24,8 @@ PANDAS_VERSION=$(python -c "import pandas; print(pandas.__version__)") # tests/io/test_clipboard.py::TestClipboard crashes pytest workers (possibly due to fixture patching clipboard functionality) PYTEST_IGNORES="--ignore=tests/io/parser/common/test_read_errors.py \ ---ignore=tests/io/test_clipboard.py" +--ignore=tests/io/test_clipboard.py \ +--ignore=tests/io/test_pickle.py" mkdir -p pandas-testing cd pandas-testing @@ -135,7 +136,7 @@ and not test_eof_states \ and not test_array_tz" # TODO: Remove "not db" once a postgres & mysql container is set up on the CI -PANDAS_CI="1" timeout 900m python -m pytest -p cudf.pandas \ +PANDAS_CI="1" timeout 90m python -m pytest -p cudf.pandas \ -v -m "not single_cpu and not db" \ -k "$TEST_THAT_NEED_MOTO_SERVER and $TEST_THAT_CRASH_PYTEST_WORKERS and not test_groupby_raises_category_on_category and not test_constructor_no_pandas_array and not test_is_monotonic_na and not test_index_contains and not test_index_contains and not test_frame_op_subclass_nonclass_constructor and not test_round_trip_current" \ --import-mode=importlib \ From 5def6dab520d652c7eadbf8988e7f03b6718a702 Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Wed, 5 Feb 2025 20:05:07 -0600 Subject: [PATCH 8/8] Update ci/cudf_pandas_scripts/pandas-tests/run.sh --- ci/cudf_pandas_scripts/pandas-tests/run.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ci/cudf_pandas_scripts/pandas-tests/run.sh b/ci/cudf_pandas_scripts/pandas-tests/run.sh index 81ee292b67d2..15970a4185c8 100755 --- a/ci/cudf_pandas_scripts/pandas-tests/run.sh +++ b/ci/cudf_pandas_scripts/pandas-tests/run.sh @@ -30,7 +30,6 @@ mkdir -p "${RAPIDS_TESTS_DIR}" bash python/cudf/cudf/pandas/scripts/run-pandas-tests.sh \ -n 5 \ --tb=no \ - --durations=0 \ -m "not slow" \ --max-worker-restart=3 \ --junitxml="${RAPIDS_TESTS_DIR}/junit-cudf-pandas.xml" \