Repository: narwhals-dev/narwhals
Pull request #713 (2024-08-04 10:28:06)

feat: add `date` to datetime namespace
<!--
# Thanks for contributing a pull request! 
## Please make sure you see our contribution guidelines: https://github.com/narwhals-dev/narwhals/blob/main/CONTRIBUTING.md
-->

## What type of PR is this? (check all applicable)

- [ ] 💾 Refactor
- [x] ✨ Feature
- [ ] 🐛 Bug Fix
- [ ] 🔧 Optimization
- [ ] 📝 Documentation
- [ ] ✅ Test
- [ ] 🐳 Other

## Related issues 

- Related issue #637 #372 
- Closes #

## Checklist

- [x] Code follows style guide (ruff)
- [x] Tests added 
- [x] Documented the changes

## If you have comments or can explain your changes, please do so below.



lucianosrp: Will work to get this up-to-`**date**` 

Patch:
diff --git a/docs/api-reference/expr_dt.md b/docs/api-reference/expr_dt.md
index af929486..e04e9288 100644
--- a/docs/api-reference/expr_dt.md
+++ b/docs/api-reference/expr_dt.md
@@ -4,6 +4,7 @@
     handler: python
     options:
       members:
+        - date
         - year
         - month
         - day
diff --git a/docs/api-reference/series_dt.md b/docs/api-reference/series_dt.md
index ad33a083..ba342ad3 100644
--- a/docs/api-reference/series_dt.md
+++ b/docs/api-reference/series_dt.md
@@ -4,6 +4,7 @@
     handler: python
     options:
       members:
+        - date
         - year
         - month
         - day
diff --git a/docs/installation.md b/docs/installation.md
index 467ed963..5ef6df18 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -11,6 +11,6 @@ Then, if you start the Python REPL and see the following:
 ```python
 >>> import narwhals
 >>> narwhals.__version__
-'1.1.9'
+'1.2.0'
 ```
 then installation worked correctly!
diff --git a/narwhals/__init__.py b/narwhals/__init__.py
index 4bbe47ab..6bbed854 100644
--- a/narwhals/__init__.py
+++ b/narwhals/__init__.py
@@ -48,7 +48,7 @@ from narwhals.utils import maybe_align_index
 from narwhals.utils import maybe_convert_dtypes
 from narwhals.utils import maybe_set_index
 
-__version__ = "1.1.9"
+__version__ = "1.2.0"
 
 __all__ = [
     "selectors",
diff --git a/narwhals/_arrow/expr.py b/narwhals/_arrow/expr.py
index a539040f..25400dc7 100644
--- a/narwhals/_arrow/expr.py
+++ b/narwhals/_arrow/expr.py
@@ -287,6 +287,13 @@ class ArrowExpr:
     def gather_every(self: Self, n: int, offset: int = 0) -> Self:
         return reuse_series_implementation(self, "gather_every", n=n, offset=offset)
 
+    def clip(
+        self: Self, lower_bound: Any | None = None, upper_bound: Any | None = None
+    ) -> Self:
+        return reuse_series_implementation(
+            self, "clip", lower_bound=lower_bound, upper_bound=upper_bound
+        )
+
     def over(self: Self, keys: list[str]) -> Self:
         def func(df: ArrowDataFrame) -> list[ArrowSeries]:
             if self._output_names is None:
@@ -347,6 +354,9 @@ class ArrowExprDateTimeNamespace:
             self._expr, "dt", "to_string", format
         )
 
+    def date(self: Self) -> ArrowExpr:
+        return reuse_series_namespace_implementation(self._expr, "dt", "date")
+
     def year(self: Self) -> ArrowExpr:
         return reuse_series_namespace_implementation(self._expr, "dt", "year")
 
diff --git a/narwhals/_arrow/series.py b/narwhals/_arrow/series.py
index 3c13be2d..bd50c030 100644
--- a/narwhals/_arrow/series.py
+++ b/narwhals/_arrow/series.py
@@ -587,6 +587,18 @@ class ArrowSeries:
     def gather_every(self: Self, n: int, offset: int = 0) -> Self:
         return self._from_native_series(self._native_series[offset::n])
 
+    def clip(
+        self: Self, lower_bound: Any | None = None, upper_bound: Any | None = None
+    ) -> Self:
+        pa = get_pyarrow()
+        pc = get_pyarrow_compute()
+
+        arr = self._native_series
+        arr = pc.max_element_wise(arr, pa.scalar(lower_bound, type=arr.type))
+        arr = pc.min_element_wise(arr, pa.scalar(upper_bound, type=arr.type))
+
+        return self._from_native_series(arr)
+
     @property
     def shape(self) -> tuple[int]:
         return (len(self._native_series),)
@@ -618,6 +630,12 @@ class ArrowSeriesDateTimeNamespace:
             pc.strftime(self._arrow_series._native_series, format)
         )
 
+    def date(self: Self) -> ArrowSeries:
+        pa = get_pyarrow()
+        return self._arrow_series._from_native_series(
+            self._arrow_series._native_series.cast(pa.date64())
+        )
+
     def year(self: Self) -> ArrowSeries:
         pc = get_pyarrow_compute()
         return self._arrow_series._from_native_series(
diff --git a/narwhals/_dask/expr.py b/narwhals/_dask/expr.py
index 4e5a5040..202d2d35 100644
--- a/narwhals/_dask/expr.py
+++ b/narwhals/_dask/expr.py
@@ -149,6 +149,14 @@ class DaskExpr:
             returns_scalar=False,
         )
 
+    def __radd__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__radd__(other),
+            "__radd__",
+            other,
+            returns_scalar=False,
+        )
+
     def __sub__(self, other: Any) -> Self:
         return self._from_call(
             lambda _input, other: _input.__sub__(other),
@@ -157,6 +165,14 @@ class DaskExpr:
             returns_scalar=False,
         )
 
+    def __rsub__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__rsub__(other),
+            "__rsub__",
+            other,
+            returns_scalar=False,
+        )
+
     def __mul__(self, other: Any) -> Self:
         return self._from_call(
             lambda _input, other: _input.__mul__(other),
@@ -165,6 +181,78 @@ class DaskExpr:
             returns_scalar=False,
         )
 
+    def __rmul__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__rmul__(other),
+            "__rmul__",
+            other,
+            returns_scalar=False,
+        )
+
+    def __truediv__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__truediv__(other),
+            "__truediv__",
+            other,
+            returns_scalar=False,
+        )
+
+    def __rtruediv__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__rtruediv__(other),
+            "__rtruediv__",
+            other,
+            returns_scalar=False,
+        )
+
+    def __floordiv__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__floordiv__(other),
+            "__floordiv__",
+            other,
+            returns_scalar=False,
+        )
+
+    def __rfloordiv__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__rfloordiv__(other),
+            "__rfloordiv__",
+            other,
+            returns_scalar=False,
+        )
+
+    def __pow__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__pow__(other),
+            "__pow__",
+            other,
+            returns_scalar=False,
+        )
+
+    def __rpow__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__rpow__(other),
+            "__rpow__",
+            other,
+            returns_scalar=False,
+        )
+
+    def __mod__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__mod__(other),
+            "__mod__",
+            other,
+            returns_scalar=False,
+        )
+
+    def __rmod__(self, other: Any) -> Self:
+        return self._from_call(
+            lambda _input, other: _input.__rmod__(other),
+            "__rmod__",
+            other,
+            returns_scalar=False,
+        )
+
     def __eq__(self, other: DaskExpr) -> Self:  # type: ignore[override]
         return self._from_call(
             lambda _input, other: _input.__eq__(other),
@@ -221,6 +309,14 @@ class DaskExpr:
             returns_scalar=False,
         )
 
+    def __rand__(self, other: DaskExpr) -> Self:  # pragma: no cover
+        return self._from_call(
+            lambda _input, other: _input.__rand__(other),
+            "__rand__",
+            other,
+            returns_scalar=False,
+        )
+
     def __or__(self, other: DaskExpr) -> Self:
         return self._from_call(
             lambda _input, other: _input.__or__(other),
@@ -229,6 +325,14 @@ class DaskExpr:
             returns_scalar=False,
         )
 
+    def __ror__(self, other: DaskExpr) -> Self:  # pragma: no cover
+        return self._from_call(
+            lambda _input, other: _input.__ror__(other),
+            "__ror__",
+            other,
+            returns_scalar=False,
+        )
+
     def mean(self) -> Self:
         return self._from_call(
             lambda _input: _input.mean(),
@@ -250,6 +354,14 @@ class DaskExpr:
             returns_scalar=True,
         )
 
+    def std(self, ddof: int = 1) -> Self:
+        return self._from_call(
+            lambda _input, ddof: _input.std(ddof=ddof),
+            "std",
+            ddof,
+            returns_scalar=True,
+        )
+
     def shift(self, n: int) -> Self:
         return self._from_call(
             lambda _input, n: _input.shift(n),
@@ -309,6 +421,17 @@ class DaskExpr:
             returns_scalar=False,
         )
 
+    def clip(
+        self: Self, lower_bound: Any | None = None, upper_bound: Any | None = None
+    ) -> Self:
+        return self._from_call(
+            lambda _input, _lower, _upper: _input.clip(lower=_lower, upper=_upper),
+            "clip",
+            lower_bound,
+            upper_bound,
+            returns_scalar=False,
+        )
+
     @property
     def str(self: Self) -> DaskExprStringNamespace:
         return DaskExprStringNamespace(self)
@@ -392,6 +515,13 @@ class DaskExprDateTimeNamespace:
     def __init__(self, expr: DaskExpr) -> None:
         self._expr = expr
 
+    def date(self) -> DaskExpr:
+        return self._expr._from_call(
+            lambda _input: _input.dt.date,
+            "date",
+            returns_scalar=False,
+        )
+
     def year(self) -> DaskExpr:
         return self._expr._from_call(
             lambda _input: _input.dt.year,
diff --git a/narwhals/_pandas_like/expr.py b/narwhals/_pandas_like/expr.py
index 5231412e..c978e26f 100644
--- a/narwhals/_pandas_like/expr.py
+++ b/narwhals/_pandas_like/expr.py
@@ -436,6 +436,9 @@ class PandasLikeExprDateTimeNamespace:
     def __init__(self, expr: PandasLikeExpr) -> None:
         self._expr = expr
 
+    def date(self) -> PandasLikeExpr:
+        return reuse_series_namespace_implementation(self._expr, "dt", "date")
+
     def year(self) -> PandasLikeExpr:
         return reuse_series_namespace_implementation(self._expr, "dt", "year")
 
diff --git a/narwhals/_pandas_like/series.py b/narwhals/_pandas_like/series.py
index acd99bf5..e52a034e 100644
--- a/narwhals/_pandas_like/series.py
+++ b/narwhals/_pandas_like/series.py
@@ -716,6 +716,21 @@ class PandasLikeSeriesDateTimeNamespace:
     def __init__(self, series: PandasLikeSeries) -> None:
         self._pandas_series = series
 
+    def date(self) -> PandasLikeSeries:
+        result = self._pandas_series._from_native_series(
+            self._pandas_series._native_series.dt.date,
+        )
+        if str(result.dtype).lower() == "object":
+            msg = (
+                "Accessing `date` on the default pandas backend "
+                "will return a Series of type `object`."
+                "\nThis differs from polars API and will prevent `.dt` chaining. "
+                "Please switch to the `pyarrow` backend:"
+                '\ndf.convert_dtypes(dtype_backend="pyarrow")'
+            )
+            raise NotImplementedError(msg)
+        return result
+
     def year(self) -> PandasLikeSeries:
         return self._pandas_series._from_native_series(
             self._pandas_series._native_series.dt.year,
diff --git a/narwhals/expr.py b/narwhals/expr.py
index 27185a6e..e51bd267 100644
--- a/narwhals/expr.py
+++ b/narwhals/expr.py
@@ -1868,11 +1868,11 @@ class Expr:
             │ i64 │
             ╞═════╡
             │ -1  │
-            │ 1   │
+            │  1  │
             │ -1  │
-            │ 3   │
+            │  3  │
             │ -1  │
-            │ 3   │
+            │  3  │
             └─────┘
         """
         return self.__class__(
@@ -2441,6 +2441,47 @@ class ExprDateTimeNamespace:
     def __init__(self, expr: Expr) -> None:
         self._expr = expr
 
+    def date(self) -> Expr:
+        """
+        Extract the date from underlying DateTime representation.
+
+        Raises:
+            NotImplementedError: If pandas default backend is being used.
+
+        Examples:
+            >>> import pandas as pd
+            >>> import polars as pl
+            >>> from datetime import datetime
+            >>> import narwhals as nw
+            >>> dates = [datetime(2012, 1, 7, 10, 20), datetime(2023, 3, 10, 11, 32)]
+            >>> s_pd = pd.Series(dates).convert_dtypes(
+            ...     dtype_backend="pyarrow"
+            ... )  # doctest:+SKIP
+            >>> s_pl = pl.Series(dates)
+
+            We define a library agnostic function:
+
+            >>> @nw.narwhalify
+            ... def func(s):
+            ...     return s.dt.date()
+
+            We can then pass either pandas or Polars to `func`:
+
+            >>> func(s_pd)  # doctest:+SKIP
+            0    2012-01-07
+            1    2023-03-10
+            dtype: date32[day][pyarrow]
+
+            >>> func(s_pl)  # doctest: +NORMALIZE_WHITESPACE
+            shape: (2,)
+            Series: '' [date]
+            [
+               2012-01-07
+               2023-03-10
+            ]
+        """
+        return self._expr.__class__(lambda plx: self._expr._call(plx).dt.date())
+
     def year(self) -> Expr:
         """
         Extract year from underlying DateTime representation.
diff --git a/narwhals/series.py b/narwhals/series.py
index b8e98cd7..8027cacf 100644
--- a/narwhals/series.py
+++ b/narwhals/series.py
@@ -634,11 +634,11 @@ class Series:
             Series: '' [i64]
             [
                -1
-               1
+                1
                -1
-               3
+                3
                -1
-               3
+                3
             ]
         """
         return self._from_compliant_series(
@@ -2678,6 +2678,49 @@ class SeriesDateTimeNamespace:
     def __init__(self, series: Series) -> None:
         self._narwhals_series = series
 
+    def date(self) -> Series:
+        """
+        Get the date in a datetime series.
+
+        Raises:
+            NotImplementedError: If pandas default backend is being used.
+
+        Examples:
+            >>> import pandas as pd
+            >>> import polars as pl
+            >>> from datetime import datetime
+            >>> import narwhals as nw
+            >>> dates = [datetime(2012, 1, 7, 10, 20), datetime(2023, 3, 10, 11, 32)]
+            >>> s_pd = pd.Series(dates).convert_dtypes(
+            ...     dtype_backend="pyarrow"
+            ... )  # doctest:+SKIP
+            >>> s_pl = pl.Series(dates)
+
+            We define a library agnostic function:
+
+            >>> @nw.narwhalify
+            ... def func(s):
+            ...     return s.dt.date()
+
+            We can then pass either pandas or Polars to `func`:
+
+            >>> func(s_pd)  # doctest:+SKIP
+            0    2012-01-07
+            1    2023-03-10
+            dtype: date32[day][pyarrow]
+
+            >>> func(s_pl)  # doctest: +NORMALIZE_WHITESPACE
+            shape: (2,)
+            Series: '' [date]
+            [
+               2012-01-07
+               2023-03-10
+            ]
+        """
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.date()
+        )
+
     def year(self) -> Series:
         """
         Get the year in a datetime series.
diff --git a/pyproject.toml b/pyproject.toml
index a1ed619f..acfa8dea 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "narwhals"
-version = "1.1.9"
+version = "1.2.0"
 authors = [
   { name="Marco Gorelli", email="33491632+MarcoGorelli@users.noreply.github.com" },
 ]


Test patch:
diff --git a/tests/expr_and_series/arithmetic_test.py b/tests/expr_and_series/arithmetic_test.py
index 0e93e7c4..fbddb6f1 100644
--- a/tests/expr_and_series/arithmetic_test.py
+++ b/tests/expr_and_series/arithmetic_test.py
@@ -28,12 +28,6 @@ def test_arithmetic(
     constructor: Any,
     request: Any,
 ) -> None:
-    if "dask" in str(constructor) and attr not in [
-        "__add__",
-        "__sub__",
-        "__mul__",
-    ]:
-        request.applymarker(pytest.mark.xfail)
     if attr == "__mod__" and any(
         x in str(constructor) for x in ["pandas_pyarrow", "pyarrow_table", "modin"]
     ):
@@ -64,8 +58,6 @@ def test_right_arithmetic(
     constructor: Any,
     request: Any,
 ) -> None:
-    if "dask" in str(constructor):
-        request.applymarker(pytest.mark.xfail)
     if attr == "__rmod__" and any(
         x in str(constructor) for x in ["pandas_pyarrow", "pyarrow_table", "modin"]
     ):
diff --git a/tests/expr_and_series/clip_test.py b/tests/expr_and_series/clip_test.py
index 94b54272..909b153b 100644
--- a/tests/expr_and_series/clip_test.py
+++ b/tests/expr_and_series/clip_test.py
@@ -1,24 +1,35 @@
 from typing import Any
 
-import pytest
-
 import narwhals.stable.v1 as nw
 from tests.utils import compare_dicts
 
 
-def test_clip(request: Any, constructor: Any) -> None:
-    if "pyarrow_table" in str(constructor) or "dask" in str(constructor):
-        request.applymarker(pytest.mark.xfail)
+def test_clip(constructor: Any) -> None:
     df = nw.from_native(constructor({"a": [1, 2, 3, -4, 5]}))
-    result = df.select(b=nw.col("a").clip(3, 5))
-    expected = {"b": [3, 3, 3, 3, 5]}
+    result = df.select(
+        lower_only=nw.col("a").clip(lower_bound=3),
+        upper_only=nw.col("a").clip(upper_bound=4),
+        both=nw.col("a").clip(3, 4),
+    )
+    expected = {
+        "lower_only": [3, 3, 3, 3, 5],
+        "upper_only": [1, 2, 3, -4, 4],
+        "both": [3, 3, 3, 3, 4],
+    }
     compare_dicts(result, expected)
 
 
-def test_clip_series(request: Any, constructor_eager: Any) -> None:
-    if "pyarrow_table" in str(constructor_eager):
-        request.applymarker(pytest.mark.xfail)
+def test_clip_series(constructor_eager: Any) -> None:
     df = nw.from_native(constructor_eager({"a": [1, 2, 3, -4, 5]}), eager_only=True)
-    result = {"b": df["a"].clip(3, 5)}
-    expected = {"b": [3, 3, 3, 3, 5]}
+    result = {
+        "lower_only": df["a"].clip(lower_bound=3),
+        "upper_only": df["a"].clip(upper_bound=4),
+        "both": df["a"].clip(3, 4),
+    }
+
+    expected = {
+        "lower_only": [3, 3, 3, 3, 5],
+        "upper_only": [1, 2, 3, -4, 4],
+        "both": [3, 3, 3, 3, 4],
+    }
     compare_dicts(result, expected)
diff --git a/tests/expr_and_series/dt/datetime_attributes_test.py b/tests/expr_and_series/dt/datetime_attributes_test.py
index 44d80915..4d59567d 100644
--- a/tests/expr_and_series/dt/datetime_attributes_test.py
+++ b/tests/expr_and_series/dt/datetime_attributes_test.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+from datetime import date
 from datetime import datetime
 from typing import Any
 
@@ -19,6 +20,7 @@ data = {
 @pytest.mark.parametrize(
     ("attribute", "expected"),
     [
+        ("date", [date(2021, 3, 1), date(2020, 1, 2)]),
         ("year", [2021, 2020]),
         ("month", [3, 1]),
         ("day", [1, 2]),
@@ -32,8 +34,15 @@ data = {
     ],
 )
 def test_datetime_attributes(
-    constructor: Any, attribute: str, expected: list[int]
+    request: Any, constructor: Any, attribute: str, expected: list[int]
 ) -> None:
+    if (
+        attribute == "date"
+        and "pandas" in str(constructor)
+        and "pyarrow" not in str(constructor)
+    ):
+        request.applymarker(pytest.mark.xfail)
+
     df = nw.from_native(constructor(data))
     result = df.select(getattr(nw.col("a").dt, attribute)())
     compare_dicts(result, {"a": expected})
@@ -42,6 +51,7 @@ def test_datetime_attributes(
 @pytest.mark.parametrize(
     ("attribute", "expected"),
     [
+        ("date", [date(2021, 3, 1), date(2020, 1, 2)]),
         ("year", [2021, 2020]),
         ("month", [3, 1]),
         ("day", [1, 2]),
@@ -55,8 +65,27 @@ def test_datetime_attributes(
     ],
 )
 def test_datetime_attributes_series(
-    constructor_eager: Any, attribute: str, expected: list[int]
+    request: Any, constructor_eager: Any, attribute: str, expected: list[int]
 ) -> None:
+    if (
+        attribute == "date"
+        and "pandas" in str(constructor_eager)
+        and "pyarrow" not in str(constructor_eager)
+    ):
+        request.applymarker(pytest.mark.xfail)
+
     df = nw.from_native(constructor_eager(data), eager_only=True)
     result = df.select(getattr(df["a"].dt, attribute)())
     compare_dicts(result, {"a": expected})
+
+
+def test_datetime_chained_attributes(request: Any, constructor_eager: Any) -> None:
+    if "pandas" in str(constructor_eager) and "pyarrow" not in str(constructor_eager):
+        request.applymarker(pytest.mark.xfail)
+
+    df = nw.from_native(constructor_eager(data), eager_only=True)
+    result = df.select(df["a"].dt.date().dt.year())
+    compare_dicts(result, {"a": [2021, 2020]})
+
+    result = df.select(nw.col("a").dt.date().dt.year())
+    compare_dicts(result, {"a": [2021, 2020]})
diff --git a/tests/expr_and_series/std_test.py b/tests/expr_and_series/std_test.py
index 341971c6..0c7e2f33 100644
--- a/tests/expr_and_series/std_test.py
+++ b/tests/expr_and_series/std_test.py
@@ -1,16 +1,12 @@
 from typing import Any
 
-import pytest
-
 import narwhals.stable.v1 as nw
 from tests.utils import compare_dicts
 
 data = {"a": [1, 3, 2], "b": [4, 4, 6], "z": [7.0, 8, 9]}
 
 
-def test_std(constructor: Any, request: Any) -> None:
-    if "dask" in str(constructor):
-        request.applymarker(pytest.mark.xfail)
+def test_std(constructor: Any) -> None:
     df = nw.from_native(constructor(data))
     result = df.select(
         nw.col("a").std().alias("a_ddof_default"),
diff --git a/tests/frame/add_test.py b/tests/frame/add_test.py
index 20927f37..b885bb05 100644
--- a/tests/frame/add_test.py
+++ b/tests/frame/add_test.py
@@ -1,14 +1,10 @@
 from typing import Any
 
-import pytest
-
 import narwhals.stable.v1 as nw
 from tests.utils import compare_dicts
 
 
-def test_add(constructor: Any, request: Any) -> None:
-    if "dask" in str(constructor):
-        request.applymarker(pytest.mark.xfail)
+def test_add(constructor: Any) -> None:
     data = {"a": [1, 3, 2], "b": [4, 4, 6], "z": [7.0, 8, 9]}
     df = nw.from_native(constructor(data))
     result = df.with_columns(
diff --git a/tests/translate/from_native_test.py b/tests/translate/from_native_test.py
index d6ee5473..af49c022 100644
--- a/tests/translate/from_native_test.py
+++ b/tests/translate/from_native_test.py
@@ -191,7 +191,7 @@ def test_series_only_dask() -> None:
 )
 def test_eager_only_lazy_dask(eager_only: Any, context: Any) -> None:
     pytest.importorskip("dask")
-    pytest.importorskip("dask_expr")
+    pytest.importorskip("dask_expr", exc_type=ImportError)
     import dask.dataframe as dd
 
     dframe = dd.from_pandas(df_pd)