diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6d29a2153cc3..6d122ef7030d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,7 @@ repos: hooks: - id: mypy additional_dependencies: [ - "polars>=1.30,<1.39", + "polars>=1.35,<1.39", "numpy", pyarrow-stubs, "pyarrow<24.0.0", # https://github.com/rapidsai/cudf/issues/22229 diff --git a/ci/test_wheel_cudf_polars.sh b/ci/test_wheel_cudf_polars.sh index 1ae3544e642e..da8a1f1c7070 100755 --- a/ci/test_wheel_cudf_polars.sh +++ b/ci/test_wheel_cudf_polars.sh @@ -71,7 +71,7 @@ for version in "${VERSIONS[@]}"; do COVERAGE_ARGS=(--no-cov) fi - timeout 1h ./ci/run_cudf_polars_pytests.sh \ + timeout 15m ./ci/run_cudf_polars_pytests.sh \ "${COVERAGE_ARGS[@]}" \ --numprocesses=8 \ --dist=worksteal \ diff --git a/conda/environments/all_cuda-129_arch-aarch64.yaml b/conda/environments/all_cuda-129_arch-aarch64.yaml index 4a9e6266c5a1..f461f45f7f6c 100644 --- a/conda/environments/all_cuda-129_arch-aarch64.yaml +++ b/conda/environments/all_cuda-129_arch-aarch64.yaml @@ -67,7 +67,7 @@ dependencies: - packaging - pandas>=2.0,<2.4.0 - pandoc -- polars>=1.30,<1.39 +- polars>=1.35,<1.39 - pre-commit - pyarrow>=19.0.0,<24 - pydata-sphinx-theme>=0.15.4 diff --git a/conda/environments/all_cuda-129_arch-x86_64.yaml b/conda/environments/all_cuda-129_arch-x86_64.yaml index e299e625122e..b59154ad037b 100644 --- a/conda/environments/all_cuda-129_arch-x86_64.yaml +++ b/conda/environments/all_cuda-129_arch-x86_64.yaml @@ -67,7 +67,7 @@ dependencies: - packaging - pandas>=2.0,<2.4.0 - pandoc -- polars>=1.30,<1.39 +- polars>=1.35,<1.39 - pre-commit - pyarrow>=19.0.0,<24 - pydata-sphinx-theme>=0.15.4 diff --git a/conda/environments/all_cuda-131_arch-aarch64.yaml b/conda/environments/all_cuda-131_arch-aarch64.yaml index 2a306e5db37d..ebf288106be8 100644 --- a/conda/environments/all_cuda-131_arch-aarch64.yaml +++ b/conda/environments/all_cuda-131_arch-aarch64.yaml @@ -67,7 +67,7 @@ dependencies: - packaging - pandas>=2.0,<2.4.0 - pandoc -- polars>=1.30,<1.39 +- polars>=1.35,<1.39 - pre-commit - pyarrow>=19.0.0,<24 - pydata-sphinx-theme>=0.15.4 diff --git a/conda/environments/all_cuda-131_arch-x86_64.yaml b/conda/environments/all_cuda-131_arch-x86_64.yaml index 18091e36e91b..c648a4f40535 100644 --- a/conda/environments/all_cuda-131_arch-x86_64.yaml +++ b/conda/environments/all_cuda-131_arch-x86_64.yaml @@ -67,7 +67,7 @@ dependencies: - packaging - pandas>=2.0,<2.4.0 - pandoc -- polars>=1.30,<1.39 +- polars>=1.35,<1.39 - pre-commit - pyarrow>=19.0.0,<24 - pydata-sphinx-theme>=0.15.4 diff --git a/conda/recipes/cudf-polars/recipe.yaml b/conda/recipes/cudf-polars/recipe.yaml index a4a9441187c1..3a48d7fc4af6 100644 --- a/conda/recipes/cudf-polars/recipe.yaml +++ b/conda/recipes/cudf-polars/recipe.yaml @@ -37,7 +37,7 @@ requirements: - python - pylibcudf =${{ version }} - rapidsmpf =${{ minor_version }} - - polars>=1.30,<1.39 + - polars>=1.35,<1.39 - packaging - ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="x") }} - if: cuda_major == "12" diff --git a/dependencies.yaml b/dependencies.yaml index 68947256aba8..5cb3cf194057 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -821,7 +821,7 @@ dependencies: # 'nvidia-ml-py' provides the 'pynvml' module - &nvidia_ml_py nvidia-ml-py>=12 - packaging - - polars>=1.30,<1.39 + - polars>=1.35,<1.39 - typing_extensions>=4.0.0 run_cudf_polars_experimental: common: diff --git a/python/cudf_polars/cudf_polars/containers/datatype.py b/python/cudf_polars/cudf_polars/containers/datatype.py index 23795ff3be93..c9da8a977ad2 100644 --- a/python/cudf_polars/cudf_polars/containers/datatype.py +++ b/python/cudf_polars/cudf_polars/containers/datatype.py @@ -45,13 +45,9 @@ def _dtype_to_header(dtype: pl.DataType) -> DataTypeHeader: if name in SCALAR_NAME_TO_POLARS_TYPE_MAP: return {"kind": "scalar", "name": name} if isinstance(dtype, pl.Decimal): - # TODO: Add version guard once we support polars 1.34 - # Also keep in mind the typing change in polars: - # https://github.com/pola-rs/polars/pull/25227 - precision = dtype.precision if dtype.precision is not None else 38 return { "kind": "decimal", - "precision": precision, + "precision": dtype.precision, "scale": dtype.scale, } if isinstance(dtype, pl.Datetime): diff --git a/python/cudf_polars/cudf_polars/dsl/expressions/string.py b/python/cudf_polars/cudf_polars/dsl/expressions/string.py index 9c5214dc57cb..48973153f56b 100644 --- a/python/cudf_polars/cudf_polars/dsl/expressions/string.py +++ b/python/cudf_polars/cudf_polars/dsl/expressions/string.py @@ -12,7 +12,7 @@ from enum import IntEnum, auto from typing import TYPE_CHECKING, Any, ClassVar, cast -from polars import Struct as pl_Struct, polars # type: ignore[attr-defined] +from polars import Struct as pl_Struct from polars.exceptions import InvalidOperationError import pylibcudf as plc @@ -23,7 +23,6 @@ from cudf_polars.dsl.utils.reshape import broadcast from cudf_polars.utils.dtypes import make_empty_column from cudf_polars.utils.versions import ( - POLARS_VERSION_LT_132, POLARS_VERSION_LT_136, POLARS_VERSION_LT_138, ) @@ -31,6 +30,8 @@ if TYPE_CHECKING: from typing import Self + from polars import polars # type: ignore[attr-defined] + from cudf_polars.containers import DataFrame, DataType __all__ = ["StringFunction"] @@ -292,21 +293,6 @@ def _validate_input(self) -> None: raise NotImplementedError( "strip operations only support scalar patterns" ) - elif self.name is StringFunction.Name.ZFill: - if isinstance(self.children[1], Literal): - _, width = self.children - assert isinstance(width, Literal) - if ( - POLARS_VERSION_LT_132 - and width.value is not None - and width.value < 0 - ): # pragma: no cover - dtypestr = polars.dtype_str_repr(width.dtype.polars_type) - raise InvalidOperationError( - f"conversion from `{dtypestr}` to `u64` " - f"failed in column 'literal' for 1 out of " - f"1 values: [{width.value}]" - ) from None @staticmethod def _create_regex_program( @@ -437,24 +423,6 @@ def do_evaluate( else: col_width = self.children[1].evaluate(df, context=context) assert isinstance(col_width, Column) - all_gt_0 = plc.binaryop.binary_operation( - col_width.obj, - plc.Scalar.from_py( - 0, plc.DataType(plc.TypeId.INT64), stream=df.stream - ), - plc.binaryop.BinaryOperator.GREATER_EQUAL, - plc.DataType(plc.TypeId.BOOL8), - stream=df.stream, - ) - - if POLARS_VERSION_LT_132 and not plc.reduce.reduce( - all_gt_0, - plc.aggregation.all(), - plc.DataType(plc.TypeId.BOOL8), - stream=df.stream, - ).to_py(stream=df.stream): # pragma: no cover - raise InvalidOperationError("fill conversion failed.") - return Column( plc.strings.padding.zfill_by_widths( column.obj, col_width.obj, stream=df.stream @@ -981,21 +949,14 @@ def do_evaluate( dtype=self.dtype, ) elif self.name is StringFunction.Name.PadStart: - if POLARS_VERSION_LT_132: # pragma: no cover - (column,) = columns - width_arg, char = self.options - pad_width = cast(int, width_arg) - else: - (column, width_col) = columns - (char,) = self.options - # TODO: Maybe accept a string scalar in - # cudf::strings::pad to avoid DtoH transfer - # See https://github.com/rapidsai/cudf/issues/20202 - width_py = width_col.obj.to_scalar(stream=df.stream).to_py( - stream=df.stream - ) - assert width_py is not None - pad_width = int(width_py) + (column, width_col) = columns + (char,) = self.options + # TODO: Maybe accept a string scalar in + # cudf::strings::pad to avoid DtoH transfer + # See https://github.com/rapidsai/cudf/issues/20202 + width_py = width_col.obj.to_scalar(stream=df.stream).to_py(stream=df.stream) + assert width_py is not None + pad_width = int(width_py) return Column( plc.strings.padding.pad( @@ -1008,20 +969,13 @@ def do_evaluate( dtype=self.dtype, ) elif self.name is StringFunction.Name.PadEnd: - if POLARS_VERSION_LT_132: # pragma: no cover - (column,) = columns - width_arg, char = self.options - pad_width = cast(int, width_arg) - else: - (column, width_col) = columns - (char,) = self.options - # TODO: Maybe accept a string scalar in - # cudf::strings::pad to avoid DtoH transfer - width_py = width_col.obj.to_scalar(stream=df.stream).to_py( - stream=df.stream - ) - assert width_py is not None - pad_width = int(width_py) + (column, width_col) = columns + (char,) = self.options + # TODO: Maybe accept a string scalar in + # cudf::strings::pad to avoid DtoH transfer + width_py = width_col.obj.to_scalar(stream=df.stream).to_py(stream=df.stream) + assert width_py is not None + pad_width = int(width_py) return Column( plc.strings.padding.pad( diff --git a/python/cudf_polars/cudf_polars/dsl/ir.py b/python/cudf_polars/cudf_polars/dsl/ir.py index 1c48f70bb114..8537fea9faed 100644 --- a/python/cudf_polars/cudf_polars/dsl/ir.py +++ b/python/cudf_polars/cudf_polars/dsl/ir.py @@ -49,8 +49,6 @@ join_cuda_streams, ) from cudf_polars.utils.versions import ( - POLARS_VERSION_LT_131, - POLARS_VERSION_LT_134, POLARS_VERSION_LT_136, POLARS_VERSION_LT_137, POLARS_VERSION_LT_138, @@ -505,16 +503,7 @@ def __init__( raise NotImplementedError( "Read from cloud storage" ) # pragma: no cover; no test yet - if ( - any(str(p).startswith("https:/") for p in self.paths) - and POLARS_VERSION_LT_131 - ): # pragma: no cover; polars passed us the wrong URI - # https://github.com/pola-rs/polars/issues/22766 - raise NotImplementedError("Read from https") - if any( - str(p).startswith("file:/" if POLARS_VERSION_LT_131 else "file://") - for p in self.paths - ): + if any(str(p).startswith("file://") for p in self.paths): raise NotImplementedError("Read from file URI") if self.typ == "csv": if any( @@ -1980,19 +1969,15 @@ def _strip_predicate_casts(node: expr.Expr) -> expr.Expr: ): return child - if ( - not POLARS_VERSION_LT_134 - and isinstance(child, expr.ColRef) - and ( - ( - plc.traits.is_floating_point(src.plc_type) - and plc.traits.is_floating_point(dst.plc_type) - ) - or ( - plc.traits.is_integral(src.plc_type) - and plc.traits.is_integral(dst.plc_type) - and src.plc_type.id() == dst.plc_type.id() - ) + if isinstance(child, expr.ColRef) and ( + ( + plc.traits.is_floating_point(src.plc_type) + and plc.traits.is_floating_point(dst.plc_type) + ) + or ( + plc.traits.is_integral(src.plc_type) + and plc.traits.is_integral(dst.plc_type) + and src.plc_type.id() == dst.plc_type.id() ) ): return child @@ -3040,16 +3025,6 @@ def __init__(self, schema: Schema, name: str, options: Any, df: IR): # same sub-shapes raise NotImplementedError("Explode with more than one column") self.options = (tuple(to_explode),) - elif POLARS_VERSION_LT_131 and self.name == "rename": # pragma: no cover - # As of 1.31, polars validates renaming in the IR - old, new, strict = self.options - if len(new) != len(set(new)) or ( - set(new) & (set(df.schema.keys()) - set(old)) - ): - raise NotImplementedError( - "Duplicate new names in rename." - ) # pragma: no cover - self.options = (tuple(old), tuple(new), strict) elif self.name == "unpivot": indices, pivotees, variable_name, value_name = self.options value_name = "value" if value_name is None else value_name @@ -3123,11 +3098,6 @@ def do_evaluate( # No-op in our data model # Don't think this appears in a plan tree from python return df # pragma: no cover - elif POLARS_VERSION_LT_131 and name == "rename": # pragma: no cover - # final tag is "swapping" which is useful for the - # optimiser (it blocks some pushdown operations) - old, new, _ = options - return df.rename_columns(dict(zip(old, new, strict=True))) elif name == "explode": ((to_explode,),) = options index = df.column_names.index(to_explode) diff --git a/python/cudf_polars/cudf_polars/dsl/translate.py b/python/cudf_polars/cudf_polars/dsl/translate.py index 760d0541615d..47afd9b7e58e 100644 --- a/python/cudf_polars/cudf_polars/dsl/translate.py +++ b/python/cudf_polars/cudf_polars/dsl/translate.py @@ -33,13 +33,8 @@ from cudf_polars.typing import Schema from cudf_polars.utils import config, sorting from cudf_polars.utils.versions import ( - POLARS_VERSION_LT_131, - POLARS_VERSION_LT_132, - POLARS_VERSION_LT_133, - POLARS_VERSION_LT_134, POLARS_VERSION_LT_136, POLARS_VERSION_LT_138, - POLARS_VERSION_LT_1323, ) if TYPE_CHECKING: @@ -290,12 +285,11 @@ def _(node: plrs._ir_nodes.Scan, translator: Translator, schema: Schema) -> ir.I with_columns = file_options.with_columns row_index = file_options.row_index include_file_paths = file_options.include_file_paths - if not POLARS_VERSION_LT_131: - deletion_files = file_options.deletion_files # pragma: no cover - if deletion_files: # pragma: no cover - raise NotImplementedError( - "Iceberg format is not supported in cudf-polars. Furthermore, row-level deletions are not supported." - ) # pragma: no cover + deletion_files = file_options.deletion_files + if deletion_files: # pragma: no cover + raise NotImplementedError( + "Iceberg format is not supported in cudf-polars. Furthermore, row-level deletions are not supported." + ) # pragma: no cover config_options = translator.config_options parquet_options = config_options.parquet_options @@ -341,18 +335,13 @@ def _(node: plrs._ir_nodes.Scan, translator: Translator, schema: Schema) -> ir.I @_translate_ir.register def _(node: plrs._ir_nodes.Cache, translator: Translator, schema: Schema) -> ir.IR: - if POLARS_VERSION_LT_1323: # pragma: no cover - refcount = node.cache_hits - else: - refcount = None - # Make sure Cache nodes with the same id_ # are actually the same object. if node.id_ not in translator._cache_nodes: translator._cache_nodes[node.id_] = ir.Cache( schema, node.id_, - refcount, + None, translator.translate_ir(n=node.input), ) return translator._cache_nodes[node.id_] @@ -643,9 +632,7 @@ def _(node: plrs._ir_nodes.Sink, translator: Translator, schema: Schema) -> ir.I f"{sink_kind} compression ('{compression}') is not supported." ) - if POLARS_VERSION_LT_132: # pragma: no cover - path = file["target"] - elif POLARS_VERSION_LT_138: # pragma: no cover + if POLARS_VERSION_LT_138: # pragma: no cover path = file["target"]["Local"] else: path = file["target"]["inner"] @@ -796,9 +783,7 @@ def _( if name in needs_cast: return expr.Cast(dtype, True, result_expr) # noqa: FBT003 return result_expr - elif not POLARS_VERSION_LT_131 and isinstance( - name, plrs._expr_nodes.StructFunction - ): + elif isinstance(name, plrs._expr_nodes.StructFunction): return expr.StructFunction( dtype, expr.StructFunction.Name.from_polars(name), @@ -808,37 +793,18 @@ def _( elif isinstance(name, str): children = (translator.translate_expr(n=n, schema=schema) for n in node.input) if name == "log" or ( - not POLARS_VERSION_LT_133 - and name == "l" + name == "l" and isinstance(options[0], str) and "".join((name, *options)) == "log" ): - if POLARS_VERSION_LT_133: # pragma: no cover - (base,) = options - (child,) = children - return expr.BinOp( - dtype, - plc.binaryop.BinaryOperator.LOG_BASE, - child, - expr.Literal(dtype, base), - ) - else: - (child, base) = children - res = expr.BinOp( - dtype, - plc.binaryop.BinaryOperator.LOG_BASE, - child, - expr.Literal(dtype, base.value), - ) - return ( - res - if not POLARS_VERSION_LT_134 - else expr.Cast( - DataType(pl.Float64()), - True, # noqa: FBT003 - res, - ) - ) + (child, base) = children + assert isinstance(base, expr.Literal) + return expr.BinOp( + dtype, + plc.binaryop.BinaryOperator.LOG_BASE, + child, + expr.Literal(dtype, base.value), + ) elif name == "pow": return expr.BinOp(dtype, plc.binaryop.BinaryOperator.POW, *children) return expr.UnaryFunction(dtype, name, options, *children) @@ -1143,12 +1109,6 @@ def _( ) -> expr.Expr: left = translator.translate_expr(n=node.left, schema=schema) right = translator.translate_expr(n=node.right, schema=schema) - if ( - POLARS_VERSION_LT_133 - and plc.traits.is_boolean(dtype.plc_type) - and node.op == plrs._expr_nodes.Operator.TrueDivide - ): - dtype = DataType(pl.Float64()) # pragma: no cover if node.op == plrs._expr_nodes.Operator.TrueDivide and ( plc.traits.is_fixed_point(left.dtype.plc_type) or plc.traits.is_fixed_point(right.dtype.plc_type) @@ -1166,8 +1126,7 @@ def _( ) if ( - not POLARS_VERSION_LT_134 - and node.op == plrs._expr_nodes.Operator.Multiply + node.op == plrs._expr_nodes.Operator.Multiply and plc.traits.is_fixed_point(left.dtype.plc_type) and plc.traits.is_fixed_point(right.dtype.plc_type) ): diff --git a/python/cudf_polars/cudf_polars/dsl/utils/aggregations.py b/python/cudf_polars/cudf_polars/dsl/utils/aggregations.py index 0aab66e0acab..0ba59e4828a1 100644 --- a/python/cudf_polars/cudf_polars/dsl/utils/aggregations.py +++ b/python/cudf_polars/cudf_polars/dsl/utils/aggregations.py @@ -17,7 +17,6 @@ from cudf_polars.containers import DataType from cudf_polars.dsl import expr, ir from cudf_polars.dsl.expressions.base import ExecutionContext -from cudf_polars.utils.versions import POLARS_VERSION_LT_134, POLARS_VERSION_LT_1323 if TYPE_CHECKING: from collections.abc import Callable, Generator, Iterable, Sequence @@ -201,7 +200,7 @@ def decompose_single_agg( # mean/median on decimal: Polars returns float -> pre-cast decimal_unsupported = False if plc.traits.is_fixed_point(child_dtype): - cast_for_quantile = is_quantile and not POLARS_VERSION_LT_134 + cast_for_quantile = is_quantile cast_for_mean_or_median = ( agg.name in {"mean", "median"} ) and plc.traits.is_floating_point(agg.dtype.plc_type) @@ -215,9 +214,6 @@ def decompose_single_agg( child, ) child_dtype = child.dtype.plc_type - elif is_quantile and POLARS_VERSION_LT_134: # pragma: no cover - decimal_unsupported = True - is_group_quantile_supported = plc.traits.is_integral( child_dtype ) or plc.traits.is_floating_point(child_dtype) @@ -256,41 +252,9 @@ def decompose_single_agg( # - ROLLING: sum(all-null window) => null; sum(empty window) => 0 (fill only if empty) # # Must post-process because libcudf returns null for both empty and all-null windows/groups - if not POLARS_VERSION_LT_1323 or context in { - ExecutionContext.GROUPBY, - ExecutionContext.WINDOW, - }: - # GROUPBY: always fill top-level nulls with 0 - return [(named_expr, True)], expr.NamedExpr( - name, replace_nulls(col, 0, is_top=is_top) - ) - else: # pragma: no cover - # ROLLING: - # Add a second rolling agg to compute the window size, then only - # replace nulls with 0 when the window size is 0 (ie. empty window). - win_len_name = next(name_generator) - win_len = expr.NamedExpr( - win_len_name, - expr.Len(DataType(pl.Int32())), - ) - - win_len_col = expr.Col(DataType(pl.Int32()), win_len_name) - win_len_filled = replace_nulls(win_len_col, 0, is_top=True) - - is_empty = expr.BinOp( - DataType(pl.Boolean()), - plc.binaryop.BinaryOperator.EQUAL, - win_len_filled, - expr.Literal(DataType(pl.Int32()), 0), - ) - - # If empty -> fill 0; else keep libcudf's semantics for all-null windows. - filled = replace_nulls(col, 0, is_top=is_top) - post_ternary_expr = expr.Ternary(agg.dtype, is_empty, filled, col) - - return [(named_expr, True), (win_len, True)], expr.NamedExpr( - name, post_ternary_expr - ) + return [(named_expr, True)], expr.NamedExpr( + name, replace_nulls(col, 0, is_top=is_top) + ) elif agg.name in {"mean", "median", "quantile", "std", "var"}: post_agg_col: expr.Expr = expr.Col( DataType(pl.Float64()), name diff --git a/python/cudf_polars/cudf_polars/experimental/benchmarks/asserts.py b/python/cudf_polars/cudf_polars/experimental/benchmarks/asserts.py index e8c80e480cd5..fe96c9ac04e8 100644 --- a/python/cudf_polars/cudf_polars/experimental/benchmarks/asserts.py +++ b/python/cudf_polars/cudf_polars/experimental/benchmarks/asserts.py @@ -10,8 +10,6 @@ import polars as pl import polars.testing -from cudf_polars.utils.versions import POLARS_VERSION_LT_1323 - if TYPE_CHECKING: from typing import Any @@ -163,10 +161,7 @@ def assert_tpch_result_equal( "categorical_as_str": categorical_as_str, } - if POLARS_VERSION_LT_1323: # pragma: no cover - tol_kwargs = {"rtol": rel_tol, "atol": abs_tol} - else: - tol_kwargs = {"rel_tol": rel_tol, "abs_tol": abs_tol} + tol_kwargs = {"rel_tol": rel_tol, "abs_tol": abs_tol} polars_kwargs.update(tol_kwargs) if left.columns != right.columns: diff --git a/python/cudf_polars/cudf_polars/testing/asserts.py b/python/cudf_polars/cudf_polars/testing/asserts.py index 5611f8c3e704..f37b7f646f7e 100644 --- a/python/cudf_polars/cudf_polars/testing/asserts.py +++ b/python/cudf_polars/cudf_polars/testing/asserts.py @@ -14,7 +14,6 @@ from cudf_polars.dsl.translate import Translator from cudf_polars.utils.config import ConfigOptions -from cudf_polars.utils.versions import POLARS_VERSION_LT_1323 if TYPE_CHECKING: from cudf_polars.typing import CollectKwargs @@ -127,11 +126,7 @@ def assert_gpu_result_equal( "categorical_as_str": categorical_as_str, } - tol_kwargs: dict[str, float] - if POLARS_VERSION_LT_1323: # pragma: no cover - tol_kwargs = {"rtol": rtol, "atol": atol} - else: - tol_kwargs = {"rel_tol": rtol, "abs_tol": atol} + tol_kwargs: dict[str, float] = {"rel_tol": rtol, "abs_tol": atol} # the type checker errors with: # Argument 4 to "assert_frame_equal" has incompatible type "**dict[str, float]"; expected "bool" [arg-type] diff --git a/python/cudf_polars/cudf_polars/utils/versions.py b/python/cudf_polars/cudf_polars/utils/versions.py index e6be1027b0ca..4b8e94f479bc 100644 --- a/python/cudf_polars/cudf_polars/utils/versions.py +++ b/python/cudf_polars/cudf_polars/utils/versions.py @@ -11,14 +11,7 @@ from polars import __version__ POLARS_VERSION = parse(__version__) -POLARS_LOWER_BOUND = parse("1.30") -POLARS_VERSION_LT_131 = POLARS_VERSION < parse("1.31") -POLARS_VERSION_LT_132 = POLARS_VERSION < parse("1.32") -POLARS_VERSION_LT_1321 = POLARS_VERSION < parse("1.32.1") -POLARS_VERSION_LT_1323 = POLARS_VERSION < parse("1.32.3") -POLARS_VERSION_LT_133 = POLARS_VERSION < parse("1.33.0") -POLARS_VERSION_LT_134 = POLARS_VERSION < parse("1.34.0") -POLARS_VERSION_LT_135 = POLARS_VERSION < parse("1.35.0") +POLARS_LOWER_BOUND = parse("1.35") POLARS_VERSION_LT_136 = POLARS_VERSION < parse("1.36.0") POLARS_VERSION_LT_137 = POLARS_VERSION < parse("1.37.0") POLARS_VERSION_LT_138 = POLARS_VERSION < parse("1.38.0") diff --git a/python/cudf_polars/pyproject.toml b/python/cudf_polars/pyproject.toml index 7703cad7dad4..7c21055d0b4c 100644 --- a/python/cudf_polars/pyproject.toml +++ b/python/cudf_polars/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ "cuda-python>=13.0.1,<14.0", "nvidia-ml-py>=12", "packaging", - "polars>=1.30,<1.39", + "polars>=1.35,<1.39", "pylibcudf==26.6.*,>=0.0.0a0", "rapidsmpf==26.6.*,>=0.0.0a0", "typing_extensions>=4.0.0", diff --git a/python/cudf_polars/tests/dsl/test_serialization.py b/python/cudf_polars/tests/dsl/test_serialization.py index a496a1e6a870..cea9771fca1c 100644 --- a/python/cudf_polars/tests/dsl/test_serialization.py +++ b/python/cudf_polars/tests/dsl/test_serialization.py @@ -12,21 +12,14 @@ from cudf_polars.dsl.expressions.boolean import BooleanFunction from cudf_polars.dsl.expressions.datetime import TemporalFunction from cudf_polars.dsl.expressions.string import StringFunction +from cudf_polars.dsl.expressions.struct import StructFunction from cudf_polars.utils.versions import ( - POLARS_VERSION_LT_131, - POLARS_VERSION_LT_132, POLARS_VERSION_LT_138, - POLARS_VERSION_LT_1321, ) -if not POLARS_VERSION_LT_131: - from cudf_polars.dsl.expressions.struct import StructFunction - @pytest.fixture( - params=[BooleanFunction, StringFunction, TemporalFunction] - if POLARS_VERSION_LT_131 - else [BooleanFunction, StringFunction, TemporalFunction, StructFunction] + params=[BooleanFunction, StringFunction, TemporalFunction, StructFunction] ) def function(request): return request.param @@ -53,30 +46,20 @@ def test_from_polars_all_names(function): # Check names advertised by polars are the same as we advertise polars_names_set = set(polars_names) cudf_polars_names_set = set(function.Name.__members__) - if not POLARS_VERSION_LT_132 and function == StructFunction: + if function == StructFunction: cudf_polars_names_set = cudf_polars_names_set - { "FieldByIndex", "MultipleFields", } - if POLARS_VERSION_LT_1321 and function == TemporalFunction: - cudf_polars_names_set = cudf_polars_names_set - { - "DaysInMonth", - } - if POLARS_VERSION_LT_132 and function == BooleanFunction: - cudf_polars_names_set = cudf_polars_names_set - {"IsClose"} if POLARS_VERSION_LT_138 and function == StringFunction: cudf_polars_names_set = cudf_polars_names_set - {"SplitRegex"} assert polars_names_set == cudf_polars_names_set names = function.Name - if not POLARS_VERSION_LT_132 and function == StructFunction: + if function == StructFunction: names = set(names) - { StructFunction.Name.FieldByIndex, StructFunction.Name.MultipleFields, } - if POLARS_VERSION_LT_1321 and function == TemporalFunction: - names = set(names) - {TemporalFunction.Name.DaysInMonth} - if POLARS_VERSION_LT_132 and function == BooleanFunction: - names = set(names) - {BooleanFunction.Name.IsClose} if POLARS_VERSION_LT_138 and function == StringFunction: names = set(names) - {StringFunction.Name.SplitRegex} for name in names: diff --git a/python/cudf_polars/tests/experimental/test_explain.py b/python/cudf_polars/tests/experimental/test_explain.py index 7f19e3187783..1d2abb72c8cc 100644 --- a/python/cudf_polars/tests/experimental/test_explain.py +++ b/python/cudf_polars/tests/experimental/test_explain.py @@ -445,10 +445,6 @@ def _gb(df): def test_serialize_query(): - # this test is sensitive to the polars version. - # we get a different query plan for polars < 1.35.0. - pytest.importorskip("polars", minversion="1.35.0") - left = pl.LazyFrame({"a": ["a", "b", "a"], "b": [1, 2, 3]}) right = pl.LazyFrame({"a": ["a", "b", "c"], "c": [4, 5, 6]}) diff --git a/python/cudf_polars/tests/experimental/test_select.py b/python/cudf_polars/tests/experimental/test_select.py index cef9f0f66cf0..188e9e392d02 100644 --- a/python/cudf_polars/tests/experimental/test_select.py +++ b/python/cudf_polars/tests/experimental/test_select.py @@ -20,13 +20,8 @@ from cudf_polars.experimental.select import _inline_hstack_false, _sub_expr from cudf_polars.testing.asserts import ( assert_gpu_result_equal, - assert_ir_translation_raises, ) from cudf_polars.testing.engine_utils import warns_on_spmd -from cudf_polars.utils.versions import ( - POLARS_VERSION_LT_132, - POLARS_VERSION_LT_134, -) @pytest.fixture @@ -91,15 +86,12 @@ def test_select_fill_null_with_strategy(df, streaming_engine_factory): ) q = df.select(pl.col("a").forward_fill()) - if POLARS_VERSION_LT_132: - assert_ir_translation_raises(q, NotImplementedError) - else: - with warns_on_spmd( - engine, - UserWarning, - match="fill_null with strategy other than 'zero' or 'one' is not supported for multiple partitions", - ): - assert_gpu_result_equal(q, engine=engine) + with warns_on_spmd( + engine, + UserWarning, + match="fill_null with strategy other than 'zero' or 'one' is not supported for multiple partitions", + ): + assert_gpu_result_equal(q, engine=engine) @pytest.mark.parametrize( @@ -179,13 +171,13 @@ def test_select_with_empty_partitions(engine): q = df.select(pl.col("b").sum() / Decimal("7.00")) # Polars pre their decimal overhaul: https://github.com/pola-rs/polars/issues/19784 # returned a different precision and scale, so we skip dtype check - assert_gpu_result_equal(q, engine=engine, check_dtypes=not POLARS_VERSION_LT_134) + assert_gpu_result_equal(q, engine=engine) def test_select_mean_with_decimals(engine): df = pl.LazyFrame({"d": [Decimal("1.23")] * 4}) q = df.select(pl.mean("d")) - assert_gpu_result_equal(q, engine=engine, check_dtypes=not POLARS_VERSION_LT_134) + assert_gpu_result_equal(q, engine=engine) def test_select_with_len(streaming_engine_factory): diff --git a/python/cudf_polars/tests/expressions/test_agg.py b/python/cudf_polars/tests/expressions/test_agg.py index e465a87f0aa0..436c1c8b03f1 100644 --- a/python/cudf_polars/tests/expressions/test_agg.py +++ b/python/cudf_polars/tests/expressions/test_agg.py @@ -15,8 +15,6 @@ assert_ir_translation_raises, ) from cudf_polars.utils.versions import ( - POLARS_VERSION_LT_134, - POLARS_VERSION_LT_135, POLARS_VERSION_LT_136, ) @@ -57,9 +55,9 @@ def is_sorted(request): @pytest.fixture -def xfail_if_sorted_gt_135(is_sorted, request): +def xfail_if_sorted(is_sorted, request): # See https://github.com/rapidsai/cudf/pull/20791#issuecomment-3750528419 - if is_sorted and not POLARS_VERSION_LT_135: + if is_sorted: request.applymarker( pytest.mark.xfail(reason="See https://github.com/pola-rs/polars/pull/24981") ) @@ -98,7 +96,7 @@ def decimal_df() -> pl.LazyFrame: ) -def test_agg(engine: pl.GPUEngine, df, agg, xfail_if_sorted_gt_135): +def test_agg(engine: pl.GPUEngine, df, agg, xfail_if_sorted): expr = getattr(pl.col("a"), agg)() q = df.select(expr) assert_gpu_result_equal(q, engine=engine, check_exact=False) @@ -125,7 +123,7 @@ def test_cum_agg_reverse_unsupported(cum_agg): @pytest.mark.parametrize("q", [0.5, pl.lit(0.5)]) @pytest.mark.parametrize("interp", ["nearest", "higher", "lower", "midpoint", "linear"]) -def test_quantile(engine: pl.GPUEngine, df, q, interp, xfail_if_sorted_gt_135): +def test_quantile(engine: pl.GPUEngine, df, q, interp, xfail_if_sorted): expr = pl.col("a").quantile(q, interp) q = df.select(expr) assert_gpu_result_equal(q, engine=engine, check_exact=False) @@ -213,10 +211,6 @@ def test_decimal_quantile(engine: pl.GPUEngine, decimal_df, interp): assert_gpu_result_equal(q, engine=engine) -@pytest.mark.skipif( - POLARS_VERSION_LT_134, - reason="std/var on decimal not supported before polars 1.34", -) def test_decimal_std_var(engine: pl.GPUEngine, decimal_df): q = decimal_df.select( std=pl.col("a").std(), diff --git a/python/cudf_polars/tests/expressions/test_booleanfunction.py b/python/cudf_polars/tests/expressions/test_booleanfunction.py index 4f49a44de219..94463c62ca77 100644 --- a/python/cudf_polars/tests/expressions/test_booleanfunction.py +++ b/python/cudf_polars/tests/expressions/test_booleanfunction.py @@ -13,7 +13,6 @@ assert_ir_translation_raises, ) from cudf_polars.testing.engine_utils import is_streaming_engine -from cudf_polars.utils.versions import POLARS_VERSION_LT_132 if TYPE_CHECKING: from collections.abc import Callable @@ -269,12 +268,7 @@ def test_is_in_shape_mismatch_raises(needles, haystack): assert_ir_translation_raises(q, NotImplementedError) -def test_boolean_is_close(request): - request.applymarker( - pytest.mark.xfail( - condition=POLARS_VERSION_LT_132, reason="Not supported until polars 1.32" - ) - ) +def test_boolean_is_close(): ldf = pl.LazyFrame({"a": [1.0, 1.2, 1.4, 1.45, 1.6]}) q = ldf.select(pl.col("a").is_close(1.4, abs_tol=0.1)) diff --git a/python/cudf_polars/tests/expressions/test_casting.py b/python/cudf_polars/tests/expressions/test_casting.py index e8cd8c1fcddd..6a0c472f7f20 100644 --- a/python/cudf_polars/tests/expressions/test_casting.py +++ b/python/cudf_polars/tests/expressions/test_casting.py @@ -13,7 +13,6 @@ assert_gpu_result_equal, assert_ir_translation_raises, ) -from cudf_polars.utils.versions import POLARS_VERSION_LT_132 _supported_dtypes = [(pl.Int8(), pl.Int64())] @@ -101,4 +100,4 @@ def test_float_to_decimal_rounding(engine: pl.GPUEngine): } ) q = df.select(pl.col("foo") / pl.col("bar")) - assert_gpu_result_equal(q, engine=engine, check_dtypes=not POLARS_VERSION_LT_132) + assert_gpu_result_equal(q, engine=engine) diff --git a/python/cudf_polars/tests/expressions/test_numeric_binops.py b/python/cudf_polars/tests/expressions/test_numeric_binops.py index 32c203423dc3..54f47d63e13c 100644 --- a/python/cudf_polars/tests/expressions/test_numeric_binops.py +++ b/python/cudf_polars/tests/expressions/test_numeric_binops.py @@ -11,7 +11,6 @@ from cudf_polars.testing.asserts import ( assert_gpu_result_equal, ) -from cudf_polars.utils.versions import POLARS_VERSION_LT_132 dtypes = [ pl.Int8, @@ -120,7 +119,7 @@ def test_true_div_with_decimals(engine: pl.GPUEngine): schema={"foo": pl.Decimal(15, 2), "bar": pl.Decimal(15, 2)}, ) q = df.select(pl.col("bar") / pl.col("foo")) - assert_gpu_result_equal(q, engine=engine, check_dtypes=not POLARS_VERSION_LT_132) + assert_gpu_result_equal(q, engine=engine) def test_multiply_with_decimals(engine: pl.GPUEngine): @@ -133,7 +132,7 @@ def test_multiply_with_decimals(engine: pl.GPUEngine): ) q = df.select(pl.col("x") * pl.col("y")) - assert_gpu_result_equal(q, engine=engine, check_dtypes=not POLARS_VERSION_LT_132) + assert_gpu_result_equal(q, engine=engine) def test_sum_decimal_widens_precision(request) -> None: diff --git a/python/cudf_polars/tests/expressions/test_numeric_unaryops.py b/python/cudf_polars/tests/expressions/test_numeric_unaryops.py index 8286c5340fef..0e7f8db194cd 100644 --- a/python/cudf_polars/tests/expressions/test_numeric_unaryops.py +++ b/python/cudf_polars/tests/expressions/test_numeric_unaryops.py @@ -13,7 +13,6 @@ assert_gpu_result_equal, assert_ir_translation_raises, ) -from cudf_polars.utils.versions import POLARS_VERSION_LT_132 if TYPE_CHECKING: from cudf_polars.typing import RankMethod, RoundMethod @@ -126,15 +125,11 @@ def test_null_count(engine: pl.GPUEngine): @pytest.mark.parametrize("descending", [False, True]) def test_rank_supported( engine: pl.GPUEngine, - request, ldf: pl.LazyFrame, method: RankMethod, *, descending: bool, ): - request.applymarker( - pytest.mark.xfail(condition=POLARS_VERSION_LT_132, reason="rank unsupported") - ) expr = pl.col("a").rank(method=method, descending=descending) q = ldf.select(expr) assert_gpu_result_equal(q, engine=engine) @@ -145,17 +140,12 @@ def test_rank_supported( @pytest.mark.parametrize("test", ["with_nulls", "with_ties"]) def test_rank_methods_with_nulls_or_ties( engine: pl.GPUEngine, - request, ldf: pl.LazyFrame, method: RankMethod, *, descending: bool, test: str, ) -> None: - request.applymarker( - pytest.mark.xfail(condition=POLARS_VERSION_LT_132, reason="rank unsupported") - ) - base = pl.col("a") if test == "with_nulls": expr = pl.when((base % 2) == 0).then(None).otherwise(base) diff --git a/python/cudf_polars/tests/expressions/test_rolling.py b/python/cudf_polars/tests/expressions/test_rolling.py index 776196753b68..6c00f66347df 100644 --- a/python/cudf_polars/tests/expressions/test_rolling.py +++ b/python/cudf_polars/tests/expressions/test_rolling.py @@ -13,7 +13,7 @@ assert_gpu_result_equal, assert_ir_translation_raises, ) -from cudf_polars.utils.versions import POLARS_VERSION_LT_132, POLARS_VERSION_LT_136 +from cudf_polars.utils.versions import POLARS_VERSION_LT_136 if TYPE_CHECKING: from cudf_polars.typing import RankMethod @@ -320,16 +320,12 @@ def test_over_broadcast_input_row_group_indices_aligned(engine: pl.GPUEngine): @pytest.mark.parametrize("order_by", [None, ["g2", pl.col("x2") * 2]]) def test_rank_over( engine: pl.GPUEngine, - request, df: pl.LazyFrame, method: RankMethod, *, descending: bool, order_by: None | list[str | pl.Expr], ) -> None: - request.applymarker( - pytest.mark.xfail(condition=POLARS_VERSION_LT_132, reason="rank unsupported") - ) q = df.select( pl.col("x") .rank(method=method, descending=descending) @@ -343,16 +339,12 @@ def test_rank_over( @pytest.mark.parametrize("order_by", [None, ["g2", pl.col("x2") * 2]]) def test_rank_over_with_ties( engine: pl.GPUEngine, - request, df: pl.LazyFrame, method: RankMethod, *, descending: bool, order_by: None | list[str | pl.Expr], ) -> None: - request.applymarker( - pytest.mark.xfail(condition=POLARS_VERSION_LT_132, reason="rank unsupported") - ) q = df.select( pl.when(pl.col("g") == 2) .then(pl.lit(4)) @@ -368,16 +360,12 @@ def test_rank_over_with_ties( @pytest.mark.parametrize("order_by", [None, ["g2", pl.col("x2") * 2]]) def test_rank_over_with_null_values( engine: pl.GPUEngine, - request, df: pl.LazyFrame, method: RankMethod, *, descending: bool, order_by: None | list[str | pl.Expr], ) -> None: - request.applymarker( - pytest.mark.xfail(condition=POLARS_VERSION_LT_132, reason="rank unsupported") - ) q = df.select( pl.when((pl.col("x") % 2) == 0) .then(None) @@ -393,16 +381,12 @@ def test_rank_over_with_null_values( @pytest.mark.parametrize("order_by", [None, ["g2", pl.col("x2") * 2]]) def test_rank_over_with_null_group_keys( engine: pl.GPUEngine, - request, df: pl.LazyFrame, method: RankMethod, *, descending: bool, order_by: None | list[str | pl.Expr], ) -> None: - request.applymarker( - pytest.mark.xfail(condition=POLARS_VERSION_LT_132, reason="rank unsupported") - ) q = df.select( pl.col("x") .rank(method=method, descending=descending) @@ -441,10 +425,7 @@ def test_fill_over( group_key, order_by=order_by ) ) - if POLARS_VERSION_LT_132: - assert_ir_translation_raises(q, NotImplementedError) - else: - assert_gpu_result_equal(q, engine=engine) + assert_gpu_result_equal(q, engine=engine) def test_fill_null_with_mean_over_unsupported(df: pl.LazyFrame) -> None: diff --git a/python/cudf_polars/tests/expressions/test_sort.py b/python/cudf_polars/tests/expressions/test_sort.py index df66b324db52..3f2f0dccc66d 100644 --- a/python/cudf_polars/tests/expressions/test_sort.py +++ b/python/cudf_polars/tests/expressions/test_sort.py @@ -8,12 +8,8 @@ import polars as pl -import pylibcudf as plc - -from cudf_polars import Translator -from cudf_polars.dsl.ir import IRExecutionContext from cudf_polars.testing.asserts import assert_gpu_result_equal -from cudf_polars.utils.versions import POLARS_VERSION_LT_135, POLARS_VERSION_LT_136 +from cudf_polars.utils.versions import POLARS_VERSION_LT_136 @pytest.mark.parametrize("descending", [False, True]) @@ -64,7 +60,7 @@ def test_sort_by_expression( @pytest.mark.parametrize("nulls_last", [False, True]) @pytest.mark.parametrize("with_nulls", ["no_nulls", "nulls"]) def test_setsorted(engine: pl.GPUEngine, request, descending, nulls_last, with_nulls): - if not POLARS_VERSION_LT_135 and POLARS_VERSION_LT_136: + if POLARS_VERSION_LT_136: request.applymarker( pytest.mark.xfail( reason="See https://github.com/pola-rs/polars/pull/24981, " @@ -81,28 +77,6 @@ def test_setsorted(engine: pl.GPUEngine, request, descending, nulls_last, with_n assert_gpu_result_equal(q, engine=engine) - if POLARS_VERSION_LT_135: - translator = Translator(q._ldf.visit(), pl.GPUEngine()) - - df = translator.translate_ir().evaluate( - cache={}, - timer=None, - context=IRExecutionContext(), - ) - - a = df.column_map["a"] - - assert a.is_sorted == plc.types.Sorted.YES - null_order = ( - plc.types.NullOrder.AFTER - if (descending ^ nulls_last) and with_nulls == "nulls" - else plc.types.NullOrder.BEFORE - ) - assert a.null_order == null_order - assert a.order == ( - plc.types.Order.DESCENDING if descending else plc.types.Order.ASCENDING - ) - def test_sort_concat_filtered_to_empty(engine: pl.GPUEngine): df = pl.LazyFrame({"a": [1, 2, 3]}) diff --git a/python/cudf_polars/tests/expressions/test_stringfunction.py b/python/cudf_polars/tests/expressions/test_stringfunction.py index f363015b028d..f834ba62acb6 100644 --- a/python/cudf_polars/tests/expressions/test_stringfunction.py +++ b/python/cudf_polars/tests/expressions/test_stringfunction.py @@ -16,9 +16,6 @@ ) from cudf_polars.testing.engine_utils import is_streaming_engine from cudf_polars.utils.versions import ( - POLARS_VERSION_LT_131, - POLARS_VERSION_LT_132, - POLARS_VERSION_LT_133, POLARS_VERSION_LT_136, POLARS_VERSION_LT_138, ) @@ -352,14 +349,7 @@ def test_replace_re(ldf): ) def test_replace_many(engine: pl.GPUEngine, ldf, target, repl): q = ldf.select(pl.col("a").str.replace_many(target, repl)) - _need_support_for_implode_agg = isinstance(repl, list) - if _need_support_for_implode_agg: - assert_gpu_result_equal(q, engine=engine) - elif POLARS_VERSION_LT_131: - assert_ir_translation_raises(q, NotImplementedError) - else: - # Polars 1.31 now gives us replacement argument as a list - assert_gpu_result_equal(q, engine=engine) + assert_gpu_result_equal(q, engine=engine) @pytest.mark.parametrize( @@ -558,15 +548,10 @@ def test_string_zfill(engine: pl.GPUEngine, fill, input_strings): q = ldf.select(pl.col("a").str.zfill(fill)) if fill is not None and fill < 0: - cudf_except = ( - pl.exceptions.InvalidOperationError - if not POLARS_VERSION_LT_132 - else pl.exceptions.ComputeError - ) assert_collect_raises( q, polars_except=pl.exceptions.InvalidOperationError, - cudf_except=cudf_except, + cudf_except=pl.exceptions.InvalidOperationError, ) else: assert_gpu_result_equal(q, engine=engine) @@ -574,14 +559,7 @@ def test_string_zfill(engine: pl.GPUEngine, fill, input_strings): @pytest.mark.parametrize( "fill", - [ - 5 - if not POLARS_VERSION_LT_131 - else pytest.param(5, marks=pytest.mark.xfail(reason="fixed in Polars 1.30")), - 999 - if not POLARS_VERSION_LT_131 - else pytest.param(999, marks=pytest.mark.xfail(reason="fixed in Polars 1.30")), - ], + [5, 999], ) def test_string_zfill_pl_129(engine: pl.GPUEngine, fill): ldf = pl.LazyFrame({"a": ["-1", "+2"]}) @@ -595,12 +573,8 @@ def test_string_zfill_pl_129(engine: pl.GPUEngine, fill): 0, 1, 2, - 5 - if not POLARS_VERSION_LT_131 - else pytest.param(5, marks=pytest.mark.xfail(reason="fixed in Polars 1.30")), - 999 - if not POLARS_VERSION_LT_131 - else pytest.param(999, marks=pytest.mark.xfail(reason="fixed in Polars 1.30")), + 5, + 999, -1, pytest.param(None, marks=pytest.mark.xfail(reason="None dtype")), ], @@ -614,13 +588,10 @@ def test_string_zfill_column(engine: pl.GPUEngine, fill): ).lazy() q = ldf.select(pl.col("input_strings").str.zfill(pl.col("fill"))) if fill is not None and fill < 0: - cudf_except = ( - pl.exceptions.InvalidOperationError if POLARS_VERSION_LT_132 else () - ) assert_collect_raises( q, polars_except=pl.exceptions.InvalidOperationError, - cudf_except=cudf_except, + cudf_except=(), ) else: assert_gpu_result_equal(q, engine=engine) @@ -820,10 +791,6 @@ def test_json_decode(engine: pl.GPUEngine, ldf_jsonlike): q = ldf_jsonlike.select(pl.col("a").str.json_decode(pl.Struct({"a": pl.String()}))) assert_gpu_result_equal(q, engine=engine) - if POLARS_VERSION_LT_133: - q = ldf_jsonlike.select(pl.col("a").str.json_decode(None)) # type: ignore[arg-type] - assert_ir_translation_raises(q, NotImplementedError) - def test_json_decode_empty(engine: pl.GPUEngine): # libcudf's ``read_json_from_string_column`` raises diff --git a/python/cudf_polars/tests/expressions/test_struct.py b/python/cudf_polars/tests/expressions/test_struct.py index 5fa58bf9e842..2e20824586a3 100644 --- a/python/cudf_polars/tests/expressions/test_struct.py +++ b/python/cudf_polars/tests/expressions/test_struct.py @@ -10,7 +10,6 @@ assert_gpu_result_equal, assert_ir_translation_raises, ) -from cudf_polars.utils.versions import POLARS_VERSION_LT_131 @pytest.fixture @@ -20,47 +19,23 @@ def ldf(): ) -def test_field_getitem(engine: pl.GPUEngine, request, ldf): - request.applymarker( - pytest.mark.xfail( - condition=POLARS_VERSION_LT_131, - reason="not supported until polars 1.31", - ) - ) +def test_field_getitem(engine: pl.GPUEngine, ldf): q = ldf.select(pl.col("a").struct[0]) assert_gpu_result_equal(q, engine=engine) @pytest.mark.parametrize("fields", [("b",), ("b", "d"), ("^b.*|f.*$",)]) -def test_field(engine: pl.GPUEngine, request, ldf, fields): - request.applymarker( - pytest.mark.xfail( - condition=POLARS_VERSION_LT_131, - reason="not supported until polars 1.31", - ) - ) +def test_field(engine: pl.GPUEngine, ldf, fields): q = ldf.select(pl.col("a").struct.field(*fields)) assert_gpu_result_equal(q, engine=engine) -def test_unnest(engine: pl.GPUEngine, request, ldf): - request.applymarker( - pytest.mark.xfail( - condition=POLARS_VERSION_LT_131, - reason="not supported until polars 1.31", - ) - ) +def test_unnest(engine: pl.GPUEngine, ldf): q = ldf.select(pl.col("a").struct.unnest()) assert_gpu_result_equal(q, engine=engine) -def test_json_encode(engine: pl.GPUEngine, request, ldf): - request.applymarker( - pytest.mark.xfail( - condition=POLARS_VERSION_LT_131, - reason="not supported until polars 1.31", - ) - ) +def test_json_encode(engine: pl.GPUEngine, ldf): q = ldf.select(pl.col("a").struct.json_encode()) assert_gpu_result_equal(q, engine=engine) @@ -69,16 +44,10 @@ def test_json_encode(engine: pl.GPUEngine, request, ldf): assert_gpu_result_equal(q, engine=engine) -def test_json_encode_empty(engine: pl.GPUEngine, request): +def test_json_encode_empty(engine: pl.GPUEngine): # ``write_json`` emits no lines for a zero-row input, so the # ``from_iterable_of_py(buff.split())`` round-trip cannot infer a # dtype. The expression short-circuits to an empty string column. - request.applymarker( - pytest.mark.xfail( - condition=POLARS_VERSION_LT_131, - reason="not supported until polars 1.31", - ) - ) ldf = pl.LazyFrame( {"a": pl.Series([], dtype=pl.Struct({"b": pl.String, "d": pl.String}))} ) @@ -86,13 +55,7 @@ def test_json_encode_empty(engine: pl.GPUEngine, request): assert_gpu_result_equal(q, engine=engine) -def test_rename_fields(engine: pl.GPUEngine, request, ldf): - request.applymarker( - pytest.mark.xfail( - condition=POLARS_VERSION_LT_131, - reason="not supported until polars 1.31", - ) - ) +def test_rename_fields(engine: pl.GPUEngine, ldf): q = ldf.select(pl.col("a").struct.rename_fields(["1", "2", "3"]).struct.unnest()) assert_gpu_result_equal(q, engine=engine) @@ -109,13 +72,7 @@ def test_with_fields(ldf): [pl.col("a").name.prefix_fields, pl.col("a").name.suffix_fields], ids=lambda x: x.__name__, ) -def test_prefix_suffix_fields(engine: pl.GPUEngine, request, ldf, expr): - request.applymarker( - pytest.mark.xfail( - condition=POLARS_VERSION_LT_131, - reason="not supported until polars 1.31", - ) - ) +def test_prefix_suffix_fields(engine: pl.GPUEngine, ldf, expr): q = ldf.select(expr("foo").struct.unnest()) assert_gpu_result_equal(q, engine=engine) diff --git a/python/cudf_polars/tests/test_cache.py b/python/cudf_polars/tests/test_cache.py index 0a13ecb82f37..1bfb0a2b646c 100644 --- a/python/cudf_polars/tests/test_cache.py +++ b/python/cudf_polars/tests/test_cache.py @@ -11,16 +11,10 @@ from cudf_polars.dsl.ir import IRExecutionContext from cudf_polars.dsl.traversal import traversal from cudf_polars.testing.asserts import assert_gpu_result_equal -from cudf_polars.utils.versions import POLARS_VERSION_LT_1323 -def test_cache(engine: pl.GPUEngine, request): - request.applymarker( - pytest.mark.xfail( - condition=not POLARS_VERSION_LT_1323, - reason="python no longer manages cache hits", - ) - ) +@pytest.mark.xfail(reason="python no longer manages cache hits") +def test_cache(engine: pl.GPUEngine): df1 = pl.LazyFrame( { "a": [1, 2, 3, 4, 5, 6, 7], diff --git a/python/cudf_polars/tests/test_drop_nulls.py b/python/cudf_polars/tests/test_drop_nulls.py index b2f1a2098536..c7b974eb91dc 100644 --- a/python/cudf_polars/tests/test_drop_nulls.py +++ b/python/cudf_polars/tests/test_drop_nulls.py @@ -10,7 +10,6 @@ assert_gpu_result_equal, assert_ir_translation_raises, ) -from cudf_polars.utils.versions import POLARS_VERSION_LT_132 @pytest.fixture( @@ -57,10 +56,7 @@ def test_fill_null_with_string(engine: pl.GPUEngine): ) def test_fill_null_with_strategy(engine: pl.GPUEngine, null_data, strategy): q = null_data.select(pl.col("a").fill_null(strategy=strategy)) - if POLARS_VERSION_LT_132: - assert_ir_translation_raises(q, NotImplementedError) - else: - assert_gpu_result_equal(q, engine=engine) + assert_gpu_result_equal(q, engine=engine) @pytest.mark.parametrize("strategy", ["zero", "one"]) @@ -68,10 +64,7 @@ def test_fill_null_with_strategy_bool(engine: pl.GPUEngine, strategy): q = pl.LazyFrame({"a": [True, None, False]}).select( pl.col("a").fill_null(strategy=strategy) ) - if POLARS_VERSION_LT_132: - assert_ir_translation_raises(q, NotImplementedError) - else: - assert_gpu_result_equal(q, engine=engine) + assert_gpu_result_equal(q, engine=engine) @pytest.mark.parametrize("strategy", ["forward", "backward"]) @@ -81,7 +74,4 @@ def test_fill_null_with_limit(engine: pl.GPUEngine, null_data, strategy, limit): if limit != 0: assert_ir_translation_raises(q, NotImplementedError) else: - if POLARS_VERSION_LT_132: - assert_ir_translation_raises(q, NotImplementedError) - else: - assert_gpu_result_equal(q, engine=engine) + assert_gpu_result_equal(q, engine=engine) diff --git a/python/cudf_polars/tests/test_groupby.py b/python/cudf_polars/tests/test_groupby.py index f14160a1043e..c649a0997c50 100644 --- a/python/cudf_polars/tests/test_groupby.py +++ b/python/cudf_polars/tests/test_groupby.py @@ -17,10 +17,7 @@ ) from cudf_polars.testing.engine_utils import get_blocksize_mode, is_streaming_engine from cudf_polars.utils.versions import ( - POLARS_VERSION_LT_132, - POLARS_VERSION_LT_134, POLARS_VERSION_LT_136, - POLARS_VERSION_LT_1321, ) @@ -50,11 +47,9 @@ def df(): ], } ) - if not POLARS_VERSION_LT_132: - lf = lf.with_columns( - pl.col("float").cast(pl.Decimal(precision=9, scale=2)).alias("decimal") - ) - return lf + return lf.with_columns( + pl.col("float").cast(pl.Decimal(precision=9, scale=2)).alias("decimal") + ) @pytest.fixture( @@ -104,13 +99,11 @@ def keys(request): pl.col("datetime").max(), pl.col("datetime").max().dt.is_leap_year().alias("leapyear"), ], + # polars gives us precision=None, which we + # do not support + [pl.col("decimal").median()], ] -# polars gives us precision=None, which we -# do not supprt -if not POLARS_VERSION_LT_132: - _EXPRS.append([pl.col("decimal").median()]) - @pytest.fixture( params=_EXPRS, @@ -252,7 +245,6 @@ def test_groupby_nan_minmax_raises(op): pytest.param( pl.Series("value", [[4, 5, 6]], dtype=pl.List(pl.Int32)), marks=pytest.mark.xfail( - condition=not POLARS_VERSION_LT_1321, reason="https://github.com/rapidsai/cudf/issues/19610", ), ), @@ -420,15 +412,6 @@ def test_groupby_aggs_keep_unsupported_as_null( reason="polars raises now", ) ) - request.applymarker( - pytest.mark.xfail( - condition="quantile" in str(agg_expr) - and not POLARS_VERSION_LT_132 - and POLARS_VERSION_LT_134 - and is_streaming_engine(engine), - reason="Decimal precision mismatch (9 vs 38)", - ) - ) lf = df.filter(pl.col("datetime") == date(2004, 12, 1)) q = lf.group_by("datetime").agg(agg_expr) assert_gpu_result_equal(q, engine=engine) diff --git a/python/cudf_polars/tests/test_join.py b/python/cudf_polars/tests/test_join.py index a192645602bf..80554005de0e 100644 --- a/python/cudf_polars/tests/test_join.py +++ b/python/cudf_polars/tests/test_join.py @@ -16,7 +16,6 @@ from cudf_polars.dsl.ir import ConditionalJoin from cudf_polars.testing.asserts import assert_gpu_result_equal from cudf_polars.testing.engine_utils import is_streaming_engine -from cudf_polars.utils.versions import POLARS_VERSION_LT_132 @pytest.fixture(params=[False, True], ids=["nulls_not_equal", "nulls_equal"]) @@ -181,10 +180,7 @@ def test_join_where(engine: pl.GPUEngine, left, right, conditions, zlice): assert_gpu_result_equal(q_len, engine=engine) -def test_cross_join_empty_right_table(engine: pl.GPUEngine, request): - request.applymarker( - pytest.mark.xfail(condition=POLARS_VERSION_LT_132, reason="nested loop join") - ) +def test_cross_join_empty_right_table(engine: pl.GPUEngine): a = pl.LazyFrame({"a": [1, 2, 3], "x": [7, 2, 1]}) b = pl.LazyFrame({"b": [2, 2, 2], "x": [7, 1, 3]}) @@ -312,17 +308,8 @@ def test_join_maintain_order_with_slice( "ConditionalJoin not supported for multiple partitions" ) def test_cross_join_filter_with_decimals( - engine: pl.GPUEngine, request, expr, left_dtype, right_dtype + engine: pl.GPUEngine, expr, left_dtype, right_dtype ): - request.applymarker( - pytest.mark.xfail( - POLARS_VERSION_LT_132 - and isinstance(left_dtype, pl.Decimal) - and isinstance(right_dtype, pl.Decimal) - and "==" in repr(expr), - reason="Hash Inner Join between i128 and i128", - ) - ) left = pl.LazyFrame( { "foo": [Decimal("1.00"), Decimal("2.50"), Decimal("3.00")], diff --git a/python/cudf_polars/tests/test_mapfunction.py b/python/cudf_polars/tests/test_mapfunction.py index b369a06bf35e..94883f133923 100644 --- a/python/cudf_polars/tests/test_mapfunction.py +++ b/python/cudf_polars/tests/test_mapfunction.py @@ -13,8 +13,6 @@ assert_gpu_result_equal, assert_ir_translation_raises, ) -from cudf_polars.testing.engine_utils import get_blocksize_mode -from cudf_polars.utils.versions import POLARS_VERSION_LT_131, POLARS_VERSION_LT_135 def test_explode_multiple_raises(): @@ -44,12 +42,8 @@ def test_rename_duplicate_raises(mapping): q = df.rename(mapping) - if POLARS_VERSION_LT_131: + with pytest.raises(pl.exceptions.DuplicateError, match="is duplicate"): assert_ir_translation_raises(q, NotImplementedError) - else: - # Now raises before translation - with pytest.raises(pl.exceptions.DuplicateError, match="is duplicate"): - assert_ir_translation_raises(q, NotImplementedError) @pytest.mark.parametrize( @@ -116,19 +110,8 @@ def test_unique_hash(): assert hash(ir_a) != hash(ir_b) -def test_set_sorted_then_inner_join(engine: pl.GPUEngine, request): - request.applymarker( - pytest.mark.xfail( - condition=not POLARS_VERSION_LT_135, - reason="HintIR not supported", - ) - ) - request.applymarker( - pytest.mark.xfail( - condition=get_blocksize_mode(engine) == "small" and POLARS_VERSION_LT_135, - reason="set_sorted join result order differs in polars < 1.35", - ) - ) +@pytest.mark.xfail(reason="HintIR not supported") +def test_set_sorted_then_inner_join(engine: pl.GPUEngine): df = pl.LazyFrame({"a": [1, 2, 3, 4, 5]}) q = df.set_sorted("a").join( diff --git a/python/cudf_polars/tests/test_scan.py b/python/cudf_polars/tests/test_scan.py index a655efbe422a..1875de2f7627 100644 --- a/python/cudf_polars/tests/test_scan.py +++ b/python/cudf_polars/tests/test_scan.py @@ -20,8 +20,6 @@ from cudf_polars.testing.engine_utils import is_streaming_engine from cudf_polars.testing.io import make_partitioned_source from cudf_polars.utils.versions import ( - POLARS_VERSION_LT_131, - POLARS_VERSION_LT_135, POLARS_VERSION_LT_138, ) @@ -516,14 +514,8 @@ def test_scan_from_file_uri(tmp_path: Path) -> None: @pytest.mark.parametrize("chunked", [False, True]) def test_scan_parquet_remote( - request, tmp_path: Path, df: pl.DataFrame, httpserver: HTTPServer, *, chunked: bool + tmp_path: Path, df: pl.DataFrame, httpserver: HTTPServer, *, chunked: bool ) -> None: - request.applymarker( - pytest.mark.xfail( - condition=POLARS_VERSION_LT_131, - reason="remote IO not supported", - ) - ) path = tmp_path / "foo.parquet" df.write_parquet(path) bytes_ = path.read_bytes() @@ -582,17 +574,10 @@ def get_handler(req: Request) -> Response: def test_scan_ndjson_remote( engine: pl.GPUEngine, - request: pytest.FixtureRequest, tmp_path: Path, df: pl.DataFrame, httpserver: HTTPServer, ) -> None: - request.applymarker( - pytest.mark.xfail( - condition=POLARS_VERSION_LT_131, - reason="remote IO not supported", - ) - ) path = tmp_path / "foo.jsonl" df.write_ndjson(path) bytes_ = path.read_bytes() @@ -665,11 +650,7 @@ def test_hits_scan_row_index_duplicate(engine: pl.GPUEngine, request, tmp_path): "index" ) - if POLARS_VERSION_LT_135: - # Did not raise before - assert_gpu_result_equal(q, engine=engine) - else: - assert_ir_translation_raises(q, NotImplementedError) + assert_ir_translation_raises(q, NotImplementedError) @pytest.mark.parametrize("compression", ["gzip", "zlib", "zstd"]) diff --git a/python/cudf_polars/tests/test_select.py b/python/cudf_polars/tests/test_select.py index 16ae1cf4384c..eeacec4d726b 100644 --- a/python/cudf_polars/tests/test_select.py +++ b/python/cudf_polars/tests/test_select.py @@ -12,7 +12,6 @@ assert_gpu_result_equal, assert_ir_translation_raises, ) -from cudf_polars.utils.versions import POLARS_VERSION_LT_134 def test_select(engine: pl.GPUEngine): @@ -51,7 +50,7 @@ def test_select_decimal_precision_none_result_max_precision(): gpu_result = query.collect(engine="gpu") # See github.com/pola-rs/polars/issues/19784 # for context on the decimal changes. - assert cpu_result.schema["a"].precision is None if POLARS_VERSION_LT_134 else 38 + assert cpu_result.schema["a"].precision == 38 assert gpu_result.schema["a"].precision == 38 diff --git a/python/cudf_polars/tests/test_window_functions.py b/python/cudf_polars/tests/test_window_functions.py index cf7f9181652c..2dbb06fc271d 100644 --- a/python/cudf_polars/tests/test_window_functions.py +++ b/python/cudf_polars/tests/test_window_functions.py @@ -13,7 +13,7 @@ assert_gpu_result_equal, assert_ir_translation_raises, ) -from cudf_polars.utils.versions import POLARS_VERSION_LT_132, POLARS_VERSION_LT_136 +from cudf_polars.utils.versions import POLARS_VERSION_LT_136 @pytest.fixture @@ -120,7 +120,7 @@ def test_over_mapping_strategy( ) ] ) - if not POLARS_VERSION_LT_132 and mapping_strategy == "group_to_rows": + if mapping_strategy == "group_to_rows": assert_gpu_result_equal(q, engine=engine) else: assert_ir_translation_raises(q, NotImplementedError)