Skip to content

Commit

Permalink
feat: Expose nw.Implementation, along with Implementation.is_pandas
Browse files Browse the repository at this point in the history
…, `Implementation.is_pandas_like`, and more (#1531)
  • Loading branch information
MarcoGorelli authored Dec 7, 2024
1 parent 9e8cdee commit e5ea1e4
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/api-reference/dataframe.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- get_column
- group_by
- head
- implementation
- is_duplicated
- is_empty
- is_unique
Expand Down
1 change: 1 addition & 0 deletions docs/api-reference/lazyframe.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- gather_every
- group_by
- head
- implementation
- join
- join_asof
- lazy
Expand Down
1 change: 1 addition & 0 deletions docs/api-reference/series.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
- filter
- gather_every
- head
- implementation
- is_between
- is_duplicated
- is_empty
Expand Down
2 changes: 2 additions & 0 deletions narwhals/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
from narwhals.translate import narwhalify
from narwhals.translate import to_native
from narwhals.translate import to_py_scalar
from narwhals.utils import Implementation
from narwhals.utils import generate_temporary_column_name
from narwhals.utils import is_ordered_categorical
from narwhals.utils import maybe_align_index
Expand All @@ -86,6 +87,7 @@
"Field",
"Float32",
"Float64",
"Implementation",
"Int8",
"Int16",
"Int32",
Expand Down
53 changes: 53 additions & 0 deletions narwhals/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from narwhals.typing import IntoDataFrame
from narwhals.typing import IntoExpr
from narwhals.typing import IntoFrame
from narwhals.utils import Implementation

FrameT = TypeVar("FrameT", bound="IntoFrame")
DataFrameT = TypeVar("DataFrameT", bound="IntoDataFrame")
Expand Down Expand Up @@ -366,6 +367,33 @@ def __init__(
msg = f"Expected an object which implements `__narwhals_dataframe__`, got: {type(df)}"
raise AssertionError(msg)

@property
def implementation(self) -> Implementation:
"""Return implementation of native frame.
This can be useful when you need to some special-casing for
some libraries for features outside of Narwhals' scope - for
example, when dealing with pandas' Period Dtype.
Returns:
Implementation.
Examples:
>>> import narwhals as nw
>>> import pandas as pd
>>> df_native = pd.DataFrame({"a": [1, 2, 3]})
>>> df = nw.from_native(df_native)
>>> df.implementation
<Implementation.PANDAS: 1>
>>> df.implementation.is_pandas()
True
>>> df.implementation.is_pandas_like()
True
>>> df.implementation.is_polars()
False
"""
return self._compliant_frame._implementation # type: ignore[no-any-return]

def __len__(self) -> Any:
return self._compliant_frame.__len__()

Expand Down Expand Up @@ -2938,6 +2966,31 @@ def __repr__(self) -> str: # pragma: no cover
+ "┘"
)

@property
def implementation(self) -> Implementation:
"""Return implementation of native frame.
This can be useful when you need to some special-casing for
some libraries for features outside of Narwhals' scope - for
example, when dealing with pandas' Period Dtype.
Returns:
Implementation.
Examples:
>>> import narwhals as nw
>>> import polars as pl
>>> lf_native = pl.LazyFrame({"a": [1, 2, 3]})
>>> lf = nw.from_native(lf_native)
>>> lf.implementation
<Implementation.POLARS: 6>
>>> lf.implementation.is_pandas()
False
>>> lf.implementation.is_polars()
True
"""
return self._compliant_frame._implementation # type: ignore[no-any-return]

def __getitem__(self, item: str | slice) -> NoReturn:
msg = "Slicing is not supported on LazyFrame"
raise TypeError(msg)
Expand Down
28 changes: 28 additions & 0 deletions narwhals/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from narwhals.dataframe import DataFrame
from narwhals.dtypes import DType
from narwhals.utils import Implementation


class Series(Generic[IntoSeriesT]):
Expand Down Expand Up @@ -57,6 +58,33 @@ def __init__(
msg = f"Expected Polars Series or an object which implements `__narwhals_series__`, got: {type(series)}."
raise AssertionError(msg)

@property
def implementation(self) -> Implementation:
"""Return implementation of native Series.
This can be useful when you need to some special-casing for
some libraries for features outside of Narwhals' scope - for
example, when dealing with pandas' Period Dtype.
Returns:
Implementation.
Examples:
>>> import narwhals as nw
>>> import pandas as pd
>>> s_native = pd.Series([1, 2, 3])
>>> s = nw.from_native(s_native, series_only=True)
>>> s.implementation
<Implementation.PANDAS: 1>
>>> s.implementation.is_pandas()
True
>>> s.implementation.is_pandas_like()
True
>>> s.implementation.is_polars()
False
"""
return self._compliant_series._implementation # type: ignore[no-any-return]

def __array__(self: Self, dtype: Any = None, copy: bool | None = None) -> np.ndarray:
return self._compliant_series.__array__(dtype=dtype, copy=copy)

Expand Down
2 changes: 2 additions & 0 deletions narwhals/stable/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
from narwhals.typing import IntoDataFrameT
from narwhals.typing import IntoFrameT
from narwhals.typing import IntoSeriesT
from narwhals.utils import Implementation
from narwhals.utils import Version
from narwhals.utils import generate_temporary_column_name
from narwhals.utils import is_ordered_categorical
Expand Down Expand Up @@ -3397,6 +3398,7 @@ def from_numpy(
"Field",
"Float32",
"Float64",
"Implementation",
"Int8",
"Int16",
"Int32",
Expand Down
138 changes: 138 additions & 0 deletions narwhals/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,25 @@ class Version(Enum):


class Implementation(Enum):
"""Implementation of native object (pandas, Polars, PyArrow, ...)."""

PANDAS = auto()
"""Pandas implementation."""
MODIN = auto()
"""Modin implementation."""
CUDF = auto()
"""cuDF implementation."""
PYARROW = auto()
"""PyArrow implementation."""
PYSPARK = auto()
"""PySpark implementation."""
POLARS = auto()
"""Polars implementation."""
DASK = auto()
"""Dask implementation."""

UNKNOWN = auto()
"""Unknown implementation."""

@classmethod
def from_native_namespace(
Expand Down Expand Up @@ -105,6 +115,134 @@ def to_native_namespace(self: Self) -> ModuleType:
}
return mapping[self] # type: ignore[no-any-return]

def is_pandas(self) -> bool:
"""Return whether implementation is pandas.
Returns:
Boolean.
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_pandas()
True
"""
return self is Implementation.PANDAS

def is_pandas_like(self) -> bool:
"""Return whether implementation is pandas, Modin, or cuDF.
Returns:
Boolean.
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_pandas_like()
True
"""
return self in {Implementation.PANDAS, Implementation.MODIN, Implementation.CUDF}

def is_polars(self) -> bool:
"""Return whether implementation is Polars.
Returns:
Boolean.
Examples:
>>> import polars as pl
>>> import narwhals as nw
>>> df_native = pl.DataFrame({"a": [1, 2, 3]})
>>> df = nw.from_native(df_native)
>>> df.implementation.is_polars()
True
"""
return self is Implementation.POLARS

def is_cudf(self) -> bool:
"""Return whether implementation is cuDF.
Returns:
Boolean.
Examples:
>>> import polars as pl
>>> import narwhals as nw
>>> df_native = pl.DataFrame({"a": [1, 2, 3]})
>>> df = nw.from_native(df_native)
>>> df.implementation.is_cudf()
False
"""
return self is Implementation.CUDF # pragma: no cover

def is_modin(self) -> bool:
"""Return whether implementation is Modin.
Returns:
Boolean.
Examples:
>>> import polars as pl
>>> import narwhals as nw
>>> df_native = pl.DataFrame({"a": [1, 2, 3]})
>>> df = nw.from_native(df_native)
>>> df.implementation.is_modin()
False
"""
return self is Implementation.MODIN # pragma: no cover

def is_pyspark(self) -> bool:
"""Return whether implementation is PySpark.
Returns:
Boolean.
Examples:
>>> import polars as pl
>>> import narwhals as nw
>>> df_native = pl.DataFrame({"a": [1, 2, 3]})
>>> df = nw.from_native(df_native)
>>> df.implementation.is_pyspark()
False
"""
return self is Implementation.PYSPARK # pragma: no cover

def is_pyarrow(self) -> bool:
"""Return whether implementation is PyArrow.
Returns:
Boolean.
Examples:
>>> import polars as pl
>>> import narwhals as nw
>>> df_native = pl.DataFrame({"a": [1, 2, 3]})
>>> df = nw.from_native(df_native)
>>> df.implementation.is_pyarrow()
False
"""
return self is Implementation.PYARROW # pragma: no cover

def is_dask(self) -> bool:
"""Return whether implementation is Dask.
Returns:
Boolean.
Examples:
>>> import polars as pl
>>> import narwhals as nw
>>> df_native = pl.DataFrame({"a": [1, 2, 3]})
>>> df = nw.from_native(df_native)
>>> df.implementation.is_dask()
False
"""
return self is Implementation.DASK # pragma: no cover


def import_dtypes_module(version: Version) -> DTypes:
if version is Version.V1:
Expand Down
25 changes: 25 additions & 0 deletions tests/implementation_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from __future__ import annotations

import pandas as pd
import polars as pl

import narwhals.stable.v1 as nw


def test_implementation() -> None:
assert (
nw.from_native(pd.DataFrame({"a": [1, 2, 3]})).implementation
is nw.Implementation.PANDAS
)
assert (
nw.from_native(pd.DataFrame({"a": [1, 2, 3]}))["a"].implementation
is nw.Implementation.PANDAS
)
assert nw.from_native(pd.DataFrame({"a": [1, 2, 3]})).implementation.is_pandas()
assert nw.from_native(pd.DataFrame({"a": [1, 2, 3]})).implementation.is_pandas_like()
assert not nw.from_native(pl.DataFrame({"a": [1, 2, 3]})).implementation.is_pandas()
assert not nw.from_native(pl.DataFrame({"a": [1, 2, 3]}))[
"a"
].implementation.is_pandas()
assert nw.from_native(pl.DataFrame({"a": [1, 2, 3]})).implementation.is_polars()
assert nw.from_native(pl.LazyFrame({"a": [1, 2, 3]})).implementation.is_polars()
1 change: 1 addition & 0 deletions utils/check_api_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
EXPR_ONLY_METHODS = {"over", "map_batches"}
SERIES_ONLY_METHODS = {
"dtype",
"implementation",
"is_empty",
"is_sorted",
"item",
Expand Down

0 comments on commit e5ea1e4

Please sign in to comment.