diff --git a/docs/basics/dataframe.md b/docs/basics/dataframe.md index 78b9bcce4a..911f6e58dc 100644 --- a/docs/basics/dataframe.md +++ b/docs/basics/dataframe.md @@ -7,6 +7,20 @@ To write a dataframe-agnostic function, the steps you'll want to follow are: Note: if you need eager execution, make sure to pass `eager_only=True` to `nw.from_native`. + You can check if your dataframe's implementation supports eager evaluation using the `is_eager_allowed()` method: + + ```python + import narwhals as nw + + df = nw.from_native(your_dataframe) + if df.implementation.is_eager_allowed(): + # Safe to use eager_only=True + df_eager = nw.from_native(your_dataframe, eager_only=True) + else: + # Implementation only supports lazy evaluation + df_lazy = nw.from_native(your_dataframe) + ``` + 2. Express your logic using the subset of the Polars API supported by Narwhals. 3. If you need to return a dataframe to the user in its original library, call `nw.to_native`. diff --git a/narwhals/_utils.py b/narwhals/_utils.py index c9d17e367a..a66b58a136 100644 --- a/narwhals/_utils.py +++ b/narwhals/_utils.py @@ -590,6 +590,25 @@ def is_sqlframe(self) -> bool: """ return self is Implementation.SQLFRAME # pragma: no cover + def is_eager_allowed(self) -> bool: + """Return whether implementation supports eager evaluation. + + Examples: + >>> import pandas as pd + >>> import narwhals as nw + >>> df_native = pd.DataFrame({"a": [1, 2, 3]}) + >>> df = nw.from_native(df_native) + >>> df.implementation.is_eager_allowed() + True + """ + return self in { + Implementation.CUDF, + Implementation.MODIN, + Implementation.PANDAS, + Implementation.POLARS, + Implementation.PYARROW, + } + def _backend_version(self) -> tuple[int, ...]: """Returns backend version.""" return backend_version(self) diff --git a/narwhals/translate.py b/narwhals/translate.py index fdace19827..98548711b1 100644 --- a/narwhals/translate.py +++ b/narwhals/translate.py @@ -286,6 +286,10 @@ def from_native( # noqa: D417 - `False` (default): don't require `native_object` to be eager - `True`: only convert to Narwhals if `native_object` is eager + + To check if an implementation supports eager evaluation, use the + `.implementation.is_eager_allowed()` method on a Narwhals object. + Eager-compatible implementations include: pandas, Polars, PyArrow, cuDF, and Modin. series_only: Whether to only allow Series - `False` (default): don't require `native_object` to be a Series diff --git a/tests/implementation_test.py b/tests/implementation_test.py index 1915e112d0..ef9837d97e 100644 --- a/tests/implementation_test.py +++ b/tests/implementation_test.py @@ -73,6 +73,38 @@ def test_implementation_new(member: str, value: str) -> None: assert nw.Implementation(value) is getattr(nw.Implementation, member) +def test_is_eager_allowed() -> None: + """Test that is_eager_allowed correctly identifies eager-compatible implementations.""" + # Test eager-allowed implementations + assert nw.Implementation.PANDAS.is_eager_allowed() + assert nw.Implementation.POLARS.is_eager_allowed() + assert nw.Implementation.PYARROW.is_eager_allowed() + assert nw.Implementation.CUDF.is_eager_allowed() + assert nw.Implementation.MODIN.is_eager_allowed() + + # Test non-eager implementations + assert not nw.Implementation.DASK.is_eager_allowed() + assert not nw.Implementation.DUCKDB.is_eager_allowed() + assert not nw.Implementation.IBIS.is_eager_allowed() + assert not nw.Implementation.PYSPARK.is_eager_allowed() + assert not nw.Implementation.PYSPARK_CONNECT.is_eager_allowed() + assert not nw.Implementation.SQLFRAME.is_eager_allowed() + assert not nw.Implementation.UNKNOWN.is_eager_allowed() + + +def test_is_eager_allowed_with_actual_dataframes() -> None: + """Test is_eager_allowed with actual dataframe objects.""" + # Test with pandas (if available) + pd = pytest.importorskip("pandas") + df_pandas = nw.from_native(pd.DataFrame({"a": [1, 2, 3]})) + assert df_pandas.implementation.is_eager_allowed() + + # Test with polars (if available) + pl = pytest.importorskip("polars") + df_polars = nw.from_native(pl.DataFrame({"a": [1, 2, 3]})) + assert df_polars.implementation.is_eager_allowed() + + _TYPING_ONLY_TESTS = "_" """Exhaustive checks for overload matching native -> implementation.