diff --git a/narwhals/_native.py b/narwhals/_native.py index 594370515e..f2d0eeef9f 100644 --- a/narwhals/_native.py +++ b/narwhals/_native.py @@ -190,7 +190,8 @@ def columns(self) -> Any: ... def join(self, *args: Any, **kwargs: Any) -> Any: ... -class NativeDataFrame(Sized, NativeFrame, Protocol): ... +class NativeDataFrame(Sized, NativeFrame, Protocol): + def drop(self, *args: Any, **kwargs: Any) -> Any: ... class NativeLazyFrame(NativeFrame, Protocol): @@ -199,6 +200,8 @@ def explain(self, *args: Any, **kwargs: Any) -> Any: ... class NativeSeries(Sized, Iterable[Any], Protocol): def filter(self, *args: Any, **kwargs: Any) -> Any: ... + def value_counts(self, *args: Any, **kwargs: Any) -> Any: ... + def unique(self, *args: Any, **kwargs: Any) -> Any: ... class _BasePandasLike(Sized, Protocol): diff --git a/narwhals/_translate.py b/narwhals/_translate.py index 7bfd531a2a..48531e144c 100644 --- a/narwhals/_translate.py +++ b/narwhals/_translate.py @@ -1,8 +1,8 @@ -"""[Protocols] defining conversion methods between representations. +"""[Protocols] defining conversion methods between representations, and related [structural] typing. -These come in 3 flavors and are [generic] to promote reuse. +The protocols come in 3 flavors and are [generic] to promote reuse. -The following examples use the placeholder types `Narwhal` and `Other`: +These examples use the placeholder types `Narwhal` and `Other`: - `Narwhal`: some class written in `narwhals`. - `Other`: any other class, could be native, compliant, or a builtin. @@ -53,6 +53,7 @@ class OtherConvertible( [Protocols]: https://typing.python.org/en/latest/spec/protocol.html [generic]: https://typing.python.org/en/latest/spec/generics.html +[structural]: https://typing.python.org/en/latest/spec/glossary.html#term-structural [upstream signature]: https://numpy.org/doc/stable/user/basics.interoperability.html#the-array-method [positional-only]: https://peps.python.org/pep-0570/ [moist]: https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types @@ -63,13 +64,13 @@ class OtherConvertible( from __future__ import annotations from collections.abc import Iterable, Mapping -from typing import TYPE_CHECKING, Any, Protocol +from typing import TYPE_CHECKING, Any, Literal, Protocol, TypedDict from narwhals._typing_compat import TypeVar if TYPE_CHECKING: import pyarrow as pa - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Required, Self, TypeAlias, TypeIs class ArrowStreamExportable(Protocol): @@ -184,3 +185,137 @@ class ToNarwhals(Protocol[ToNarwhalsT_co]): def to_narwhals(self) -> ToNarwhalsT_co: """Convert into public representation.""" ... + + +class _ExcludeSeries(TypedDict, total=False): + eager_only: bool + series_only: Literal[False] + allow_series: Literal[False] | None + + +class ExcludeSeries(_ExcludeSeries, total=False): + pass_through: bool + + +class ExcludeSeriesV1(_ExcludeSeries, total=False): + pass_through: bool | None + eager_or_interchange_only: Literal[False] + + +class ExcludeSeriesStrictV1(_ExcludeSeries, total=False): + strict: bool | None + eager_or_interchange_only: Literal[False] + + +class _AllowSeries(TypedDict, total=False): + eager_only: bool + series_only: Literal[False] + allow_series: Required[Literal[True]] + + +class AllowSeries(_AllowSeries, total=False): + pass_through: bool + + +class AllowSeriesV1(_AllowSeries, total=False): + pass_through: bool | None + eager_or_interchange_only: Literal[False] + + +class AllowSeriesStrictV1(_AllowSeries, total=False): + strict: bool | None + eager_or_interchange_only: Literal[False] + + +class _OnlySeries(TypedDict, total=False): + eager_only: bool + series_only: Required[Literal[True]] + allow_series: bool | None + + +class OnlySeries(_OnlySeries, total=False): + pass_through: bool + + +class OnlySeriesV1(_OnlySeries, total=False): + pass_through: bool | None + eager_or_interchange_only: Literal[False] + + +class OnlySeriesStrictV1(_OnlySeries, total=False): + strict: bool | None + eager_or_interchange_only: Literal[False] + + +class _OnlyEagerOrInterchange(TypedDict, total=False): + eager_or_interchange_only: Required[Literal[True]] + series_only: Literal[False] + allow_series: bool | None + + +class OnlyEagerOrInterchange(_OnlyEagerOrInterchange, total=False): + pass_through: bool | None + + +class OnlyEagerOrInterchangeStrict(_OnlyEagerOrInterchange, total=False): + strict: bool | None + + +class _AllowLazy(TypedDict, total=False): + eager_only: Literal[False] + series_only: Literal[False] + allow_series: bool | None + + +class AllowLazy(_AllowLazy, total=False): + pass_through: bool + + +class AllowLazyV1(_AllowLazy, total=False): + pass_through: bool | None + eager_or_interchange_only: Literal[False] + + +class AllowLazyStrictV1(_AllowLazy, total=False): + strict: bool | None + eager_or_interchange_only: Literal[False] + + +class _AllowAny(TypedDict, total=False): + eager_only: Literal[False] + series_only: Literal[False] + allow_series: Required[Literal[True]] + + +class AllowAny(_AllowAny, total=False): + pass_through: bool + + +class AllowAnyV1(_AllowAny, total=False): + pass_through: bool | None + eager_or_interchange_only: Literal[False] + + +class AllowAnyStrictV1(_AllowAny, total=False): + strict: bool | None + eager_or_interchange_only: Literal[False] + + +class _Unknown(TypedDict, total=False): + eager_only: bool + series_only: bool + allow_series: bool | None + + +class PassThroughUnknown(_Unknown, total=False): + pass_through: Required[Literal[True]] + + +class PassThroughUnknownV1(_Unknown, total=False): + pass_through: Required[Literal[True]] + eager_or_interchange_only: bool + + +class StrictUnknownV1(_Unknown, total=False): + strict: Required[Literal[False]] + eager_or_interchange_only: bool diff --git a/narwhals/stable/v1/__init__.py b/narwhals/stable/v1/__init__.py index e97e3dc7d7..c6e0b11de9 100644 --- a/narwhals/stable/v1/__init__.py +++ b/narwhals/stable/v1/__init__.py @@ -58,16 +58,41 @@ UInt128, Unknown, ) -from narwhals.stable.v1.typing import IntoDataFrameT, IntoLazyFrameT +from narwhals.stable.v1.typing import ( + DataFrameT, + IntoDataFrameT, + IntoFrame, + IntoLazyFrameT, + IntoSeries, + IntoSeriesT, + LazyFrameT, + SeriesT, +) from narwhals.translate import _from_native_impl, get_native_namespace, to_py_scalar if TYPE_CHECKING: from collections.abc import Iterable, Mapping, Sequence from types import ModuleType - from typing_extensions import ParamSpec, Self - - from narwhals._translate import IntoArrowTable + from typing_extensions import ParamSpec, Self, Unpack + + from narwhals._translate import ( + AllowAnyStrictV1 as AllowAnyStrict, + AllowAnyV1 as AllowAny, + AllowLazyStrictV1 as AllowLazyStrict, + AllowLazyV1 as AllowLazy, + AllowSeriesStrictV1 as AllowSeriesStrict, + AllowSeriesV1 as AllowSeries, + ExcludeSeriesStrictV1 as ExcludeSeriesStrict, + ExcludeSeriesV1 as ExcludeSeries, + IntoArrowTable, + OnlyEagerOrInterchange, + OnlyEagerOrInterchangeStrict, + OnlySeriesStrictV1 as OnlySeriesStrict, + OnlySeriesV1 as OnlySeries, + PassThroughUnknownV1 as PassThroughUnknown, + StrictUnknownV1 as StrictUnknown, + ) from narwhals._typing import ( Arrow, Backend, @@ -83,9 +108,7 @@ FileSource, IntoDType, IntoExpr, - IntoFrame, IntoSchema, - IntoSeries, NonNestedLiteral, SingleColSelector, SingleIndexSelector, @@ -93,15 +116,10 @@ _2DArray, ) - DataFrameT = TypeVar("DataFrameT", bound="DataFrame[Any]") - LazyFrameT = TypeVar("LazyFrameT", bound="LazyFrame[Any]") - SeriesT = TypeVar("SeriesT", bound="Series[Any]") T = TypeVar("T", default=Any) P = ParamSpec("P") R = TypeVar("R") -IntoSeriesT = TypeVar("IntoSeriesT", bound="IntoSeries", default=Any) - # NOTE legit class DataFrame(NwDataFrame[IntoDataFrameT]): # type: ignore[type-var] @@ -499,417 +517,80 @@ def _stableify( @overload -def from_native(native_object: SeriesT, **kwds: Any) -> SeriesT: ... - - +def from_native(native_object: SeriesT, **kwds: Unpack[OnlySeries]) -> SeriesT: ... @overload -def from_native(native_object: DataFrameT, **kwds: Any) -> DataFrameT: ... - - +def from_native(native_object: SeriesT, **kwds: Unpack[OnlySeriesStrict]) -> SeriesT: ... @overload -def from_native(native_object: LazyFrameT, **kwds: Any) -> LazyFrameT: ... - - +def from_native(native_object: SeriesT, **kwds: Unpack[AllowSeries]) -> SeriesT: ... @overload -def from_native( - native_object: DataFrameT | LazyFrameT, **kwds: Any -) -> DataFrameT | LazyFrameT: ... - - +def from_native(native_object: SeriesT, **kwds: Unpack[AllowSeriesStrict]) -> SeriesT: ... @overload def from_native( - native_object: IntoDataFrameT | IntoSeriesT, - *, - strict: Literal[False], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ... - - + native_object: DataFrameT, **kwds: Unpack[ExcludeSeries] +) -> DataFrameT: ... @overload def from_native( - native_object: IntoDataFrameT | IntoSeriesT, - *, - strict: Literal[False], - eager_only: Literal[True], - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ... - - + native_object: DataFrameT, **kwds: Unpack[ExcludeSeriesStrict] +) -> DataFrameT: ... @overload -def from_native( - native_object: IntoDataFrameT, - *, - strict: Literal[False], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - +def from_native(native_object: LazyFrameT, **kwds: Unpack[AllowLazy]) -> LazyFrameT: ... @overload def from_native( - native_object: T, - *, - strict: Literal[False], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> T: ... - - + native_object: LazyFrameT, **kwds: Unpack[AllowLazyStrict] +) -> LazyFrameT: ... @overload def from_native( - native_object: IntoDataFrameT, - *, - strict: Literal[False], - eager_only: Literal[True], - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., + native_object: IntoDataFrameT, **kwds: Unpack[OnlyEagerOrInterchange] ) -> DataFrame[IntoDataFrameT]: ... - - @overload def from_native( - native_object: T, - *, - strict: Literal[False], - eager_only: Literal[True], - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> T: ... - - -@overload -def from_native( - native_object: IntoDataFrameT | IntoLazyFrameT | IntoSeriesT, - *, - strict: Literal[False], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[IntoDataFrameT] | LazyFrame[IntoLazyFrameT] | Series[IntoSeriesT]: ... - - -@overload -def from_native( - native_object: IntoSeriesT, - *, - strict: Literal[False], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[True], - allow_series: None = ..., -) -> Series[IntoSeriesT]: ... - - -@overload -def from_native( - native_object: IntoDataFrameT, - *, - strict: Literal[False], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., + native_object: IntoDataFrameT, **kwds: Unpack[OnlyEagerOrInterchangeStrict] ) -> DataFrame[IntoDataFrameT]: ... - - @overload def from_native( - native_object: IntoLazyFrameT, - *, - strict: Literal[False], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> LazyFrame[IntoLazyFrameT]: ... - - -@overload -def from_native( - native_object: T, - *, - strict: Literal[False], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> T: ... - - -@overload -def from_native( - native_object: IntoDataFrameT, - *, - strict: Literal[True] | None = ..., - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., + native_object: IntoDataFrameT, **kwds: Unpack[ExcludeSeries] ) -> DataFrame[IntoDataFrameT]: ... - - -@overload -def from_native( - native_object: IntoLazyFrameT, - *, - strict: Literal[True] | None = ..., - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> LazyFrame[IntoLazyFrameT]: ... - - @overload def from_native( - native_object: IntoDataFrameT, - *, - strict: Literal[True] | None = ..., - eager_only: Literal[True], - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., + native_object: IntoDataFrameT, **kwds: Unpack[ExcludeSeriesStrict] ) -> DataFrame[IntoDataFrameT]: ... - - -@overload -def from_native( - native_object: IntoFrame | IntoSeries, - *, - strict: Literal[True] | None = ..., - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[Any] | LazyFrame[Any] | Series[Any]: ... - - @overload def from_native( - native_object: IntoSeriesT, - *, - strict: Literal[True] | None = ..., - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[True], - allow_series: None = ..., + native_object: IntoSeriesT, **kwds: Unpack[OnlySeries] ) -> Series[IntoSeriesT]: ... - - @overload def from_native( - native_object: IntoDataFrameT, - *, - strict: Literal[True] | None = ..., - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - -@overload -def from_native( - native_object: IntoDataFrameT | IntoSeries, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[IntoDataFrameT]: ... - - + native_object: IntoSeriesT, **kwds: Unpack[OnlySeriesStrict] +) -> Series[IntoSeriesT]: ... @overload def from_native( - native_object: IntoDataFrameT | IntoSeriesT, - *, - pass_through: Literal[True], - eager_only: Literal[True], - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: Literal[True], + native_object: IntoDataFrameT | IntoSeriesT, **kwds: Unpack[AllowSeries] ) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ... - - @overload def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - + native_object: IntoDataFrameT | IntoSeriesT, **kwds: Unpack[AllowSeriesStrict] +) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ... @overload def from_native( - native_object: T, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> T: ... - - + native_object: IntoLazyFrameT, **kwds: Unpack[AllowLazy] +) -> LazyFrame[IntoLazyFrameT]: ... @overload def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[True], - eager_only: Literal[True], - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - + native_object: IntoLazyFrameT, **kwds: Unpack[AllowLazyStrict] +) -> LazyFrame[IntoLazyFrameT]: ... @overload def from_native( - native_object: T, - *, - pass_through: Literal[True], - eager_only: Literal[True], - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> T: ... - - + native_object: IntoDataFrameT | IntoLazyFrameT | IntoSeriesT, **kwds: Unpack[AllowAny] +) -> DataFrame[IntoDataFrameT] | LazyFrame[IntoLazyFrameT] | Series[IntoSeriesT]: ... @overload def from_native( native_object: IntoDataFrameT | IntoLazyFrameT | IntoSeriesT, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: Literal[True], + **kwds: Unpack[AllowAnyStrict], ) -> DataFrame[IntoDataFrameT] | LazyFrame[IntoLazyFrameT] | Series[IntoSeriesT]: ... - - @overload -def from_native( - native_object: IntoSeriesT, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[True], - allow_series: None = ..., -) -> Series[IntoSeriesT]: ... - - +def from_native(native_object: T, **kwds: Unpack[PassThroughUnknown]) -> T: ... @overload -def from_native( - native_object: IntoDataFrameT | IntoLazyFrameT, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT] | LazyFrame[IntoLazyFrameT]: ... - - -@overload -def from_native( - native_object: T, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> T: ... - - -@overload -def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - -@overload -def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[True], - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - -@overload -def from_native( - native_object: IntoFrame | IntoSeries, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[Any] | LazyFrame[Any] | Series[Any]: ... - - -@overload -def from_native( - native_object: IntoSeriesT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[True], - allow_series: None = ..., -) -> Series[IntoSeriesT]: ... - - -@overload -def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - -@overload -def from_native( - native_object: IntoLazyFrameT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - eager_or_interchange_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> LazyFrame[IntoLazyFrameT]: ... +def from_native(native_object: T, **kwds: Unpack[StrictUnknown]) -> T: ... # All params passed in as variables diff --git a/narwhals/stable/v1/typing.py b/narwhals/stable/v1/typing.py index ab5cd17552..a154a1a3f8 100644 --- a/narwhals/stable/v1/typing.py +++ b/narwhals/stable/v1/typing.py @@ -1,13 +1,14 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Protocol, TypeVar, Union +from typing import TYPE_CHECKING, Any, Protocol, Union -from narwhals._native import IntoSeries, IntoSeriesT +from narwhals._native import IntoSeries +from narwhals._typing_compat import TypeVar if TYPE_CHECKING: from typing_extensions import TypeAlias - from narwhals._native import NativeDataFrame, NativeLazyFrame + from narwhals._native import NativeDataFrame, NativeDuckDB, NativeLazyFrame from narwhals.stable.v1 import DataFrame, Expr, LazyFrame, Series class DataFrameLike(Protocol): @@ -24,7 +25,7 @@ def __dataframe__(self, *args: Any, **kwargs: Any) -> Any: ... `nw.Expr`, e.g. `df.select('a')`. """ -IntoDataFrame: TypeAlias = Union["NativeDataFrame", "DataFrameLike"] +IntoDataFrame: TypeAlias = Union["NativeDataFrame", "DataFrameLike", "NativeDuckDB"] """Anything which can be converted to a Narwhals DataFrame. Use this if your function accepts a narwhalifiable object but doesn't care about its backend. @@ -123,6 +124,9 @@ def __dataframe__(self, *args: Any, **kwargs: Any) -> Any: ... ... return df.with_columns(c=df["a"] + 1) """ +LazyFrameT = TypeVar("LazyFrameT", bound="LazyFrame[Any]") +SeriesT = TypeVar("SeriesT", bound="Series[Any]") +IntoSeriesT = TypeVar("IntoSeriesT", bound="IntoSeries", default=Any) __all__ = [ "DataFrameT", @@ -135,4 +139,6 @@ def __dataframe__(self, *args: Any, **kwargs: Any) -> Any: ... "IntoFrameT", "IntoSeries", "IntoSeriesT", + "LazyFrameT", + "SeriesT", ] diff --git a/narwhals/stable/v2/__init__.py b/narwhals/stable/v2/__init__.py index d85b129b8a..064cc19622 100644 --- a/narwhals/stable/v2/__init__.py +++ b/narwhals/stable/v2/__init__.py @@ -55,15 +55,32 @@ from narwhals.schema import Schema as NwSchema from narwhals.series import Series as NwSeries from narwhals.stable.v2 import dependencies, dtypes, selectors +from narwhals.stable.v2.typing import ( + DataFrameT, + IntoDataFrameT, + IntoFrame, + IntoLazyFrameT, + IntoSeries, + IntoSeriesT, + LazyFrameT, + SeriesT, +) from narwhals.translate import _from_native_impl, get_native_namespace, to_py_scalar -from narwhals.typing import IntoDataFrameT, IntoLazyFrameT if TYPE_CHECKING: from collections.abc import Iterable, Mapping, Sequence - from typing_extensions import ParamSpec, Self + from typing_extensions import ParamSpec, Self, Unpack - from narwhals._translate import IntoArrowTable + from narwhals._translate import ( + AllowAny, + AllowLazy, + AllowSeries, + ExcludeSeries, + IntoArrowTable, + OnlySeries, + PassThroughUnknown, + ) from narwhals._typing import ( Arrow, Backend, @@ -78,9 +95,7 @@ from narwhals.typing import ( IntoDType, IntoExpr, - IntoFrame, IntoSchema, - IntoSeries, NonNestedLiteral, SingleColSelector, SingleIndexSelector, @@ -88,15 +103,10 @@ _2DArray, ) - DataFrameT = TypeVar("DataFrameT", bound="DataFrame[Any]") - LazyFrameT = TypeVar("LazyFrameT", bound="LazyFrame[Any]") - SeriesT = TypeVar("SeriesT", bound="Series[Any]") T = TypeVar("T", default=Any) P = ParamSpec("P") R = TypeVar("R") -IntoSeriesT = TypeVar("IntoSeriesT", bound="IntoSeries", default=Any) - class DataFrame(NwDataFrame[IntoDataFrameT]): _version = Version.V2 @@ -191,7 +201,7 @@ def __getitem__( ) -> Series[Any] | Self | Any: return super().__getitem__(item) - def get_column(self, name: str) -> Series: + def get_column(self, name: str) -> Series[Any]: # Type checkers complain that `nw.Series` is not assignable to `nw.v2.stable.Series`. # However the return type actually is `nw.v2.stable.Series`, check `tests/v2_test.py`. return super().get_column(name) # type: ignore[return-value] @@ -345,155 +355,42 @@ def _stableify( @overload -def from_native(native_object: SeriesT, **kwds: Any) -> SeriesT: ... - - -@overload -def from_native(native_object: DataFrameT, **kwds: Any) -> DataFrameT: ... - - -@overload -def from_native(native_object: LazyFrameT, **kwds: Any) -> LazyFrameT: ... - - -@overload -def from_native( - native_object: DataFrameT | LazyFrameT, **kwds: Any -) -> DataFrameT | LazyFrameT: ... - - -@overload -def from_native( - native_object: IntoDataFrameT | IntoSeries, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[IntoDataFrameT]: ... - - +def from_native(native_object: SeriesT, **kwds: Unpack[OnlySeries]) -> SeriesT: ... @overload -def from_native( - native_object: IntoDataFrameT | IntoSeriesT, - *, - pass_through: Literal[True], - eager_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ... - - +def from_native(native_object: SeriesT, **kwds: Unpack[AllowSeries]) -> SeriesT: ... @overload def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - + native_object: DataFrameT, **kwds: Unpack[ExcludeSeries] +) -> DataFrameT: ... +# Closer to *intended* than https://github.com/narwhals-dev/narwhals/issues/3226 @overload -def from_native( - native_object: T, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> T: ... - - +def from_native(native_object: LazyFrameT, **kwds: Unpack[AllowLazy]) -> LazyFrameT: ... @overload def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[True], - eager_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., + native_object: IntoDataFrameT, **kwds: Unpack[ExcludeSeries] ) -> DataFrame[IntoDataFrameT]: ... - - @overload def from_native( - native_object: T, - *, - pass_through: Literal[True], - eager_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> T: ... - - -@overload -def from_native( - native_object: IntoSeriesT, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - series_only: Literal[True], - allow_series: None = ..., + native_object: IntoSeriesT, **kwds: Unpack[OnlySeries] ) -> Series[IntoSeriesT]: ... - - @overload def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - + native_object: IntoSeriesT, **kwds: Unpack[AllowSeries] +) -> Series[IntoSeriesT]: ... @overload def from_native( - native_object: IntoLazyFrameT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., + native_object: IntoLazyFrameT, **kwds: Unpack[AllowLazy] ) -> LazyFrame[IntoLazyFrameT]: ... - - @overload def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - + native_object: IntoDataFrameT | IntoSeriesT, **kwds: Unpack[AllowSeries] +) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ... @overload def from_native( - native_object: IntoFrame | IntoSeries, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[Any] | LazyFrame[Any] | Series[Any]: ... - - + native_object: IntoDataFrameT | IntoLazyFrameT | IntoSeriesT, **kwds: Unpack[AllowAny] +) -> DataFrame[IntoDataFrameT] | LazyFrame[IntoLazyFrameT] | Series[IntoSeriesT]: ... @overload -def from_native( - native_object: IntoSeriesT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - series_only: Literal[True], - allow_series: None = ..., -) -> Series[IntoSeriesT]: ... - - +def from_native(native_object: T, **kwds: Unpack[PassThroughUnknown]) -> T: ... # All params passed in as variables @overload def from_native( @@ -504,13 +401,11 @@ def from_native( series_only: bool, allow_series: bool | None, ) -> Any: ... - - -def from_native( # noqa: D417 - native_object: IntoDataFrameT - | IntoLazyFrameT - | IntoFrame +def from_native( + native_object: IntoLazyFrameT + | IntoDataFrameT | IntoSeriesT + | IntoFrame | IntoSeries | T, *, @@ -518,7 +413,6 @@ def from_native( # noqa: D417 eager_only: bool = False, series_only: bool = False, allow_series: bool | None = None, - **kwds: Any, ) -> LazyFrame[IntoLazyFrameT] | DataFrame[IntoDataFrameT] | Series[IntoSeriesT] | T: """Convert `native_object` to Narwhals Dataframe, Lazyframe, or Series. @@ -556,10 +450,6 @@ def from_native( # noqa: D417 if isinstance(native_object, Series) and (series_only or allow_series): return native_object - if kwds: - msg = f"from_native() got an unexpected keyword argument {next(iter(kwds))!r}" - raise TypeError(msg) - return _from_native_impl( # type: ignore[no-any-return] native_object, pass_through=pass_through, diff --git a/narwhals/stable/v2/typing.py b/narwhals/stable/v2/typing.py index ca27111410..c95a85b787 100644 --- a/narwhals/stable/v2/typing.py +++ b/narwhals/stable/v2/typing.py @@ -71,6 +71,8 @@ ... return df.with_columns(c=df["a"] + 1) """ +LazyFrameT = TypeVar("LazyFrameT", bound="LazyFrame[Any]") +SeriesT = TypeVar("SeriesT", bound="Series[Any]") __all__ = [ "DataFrameT", @@ -85,4 +87,6 @@ "IntoLazyFrameT", "IntoSeries", "IntoSeriesT", + "LazyFrameT", + "SeriesT", ] diff --git a/narwhals/translate.py b/narwhals/translate.py index 1886ef2843..cd3e17f07b 100644 --- a/narwhals/translate.py +++ b/narwhals/translate.py @@ -30,6 +30,16 @@ ) if TYPE_CHECKING: + from typing_extensions import Unpack + + from narwhals._translate import ( + AllowAny, + AllowLazy, + AllowSeries, + ExcludeSeries, + OnlySeries, + PassThroughUnknown, + ) from narwhals.dataframe import DataFrame, LazyFrame from narwhals.series import Series from narwhals.typing import ( @@ -100,149 +110,42 @@ def to_native( @overload -def from_native(native_object: SeriesT, **kwds: Any) -> SeriesT: ... - - +def from_native(native_object: SeriesT, **kwds: Unpack[OnlySeries]) -> SeriesT: ... @overload -def from_native(native_object: DataFrameT, **kwds: Any) -> DataFrameT: ... - - -@overload -def from_native(native_object: LazyFrameT, **kwds: Any) -> LazyFrameT: ... - - +def from_native(native_object: SeriesT, **kwds: Unpack[AllowSeries]) -> SeriesT: ... @overload def from_native( - native_object: IntoDataFrameT | IntoSeriesT, - *, - pass_through: Literal[True], - eager_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ... - - + native_object: DataFrameT, **kwds: Unpack[ExcludeSeries] +) -> DataFrameT: ... +# Closer to *intended* than https://github.com/narwhals-dev/narwhals/issues/3226 @overload -def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - +def from_native(native_object: LazyFrameT, **kwds: Unpack[AllowLazy]) -> LazyFrameT: ... @overload def from_native( - native_object: T, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> T: ... - - -@overload -def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[True], - eager_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., + native_object: IntoDataFrameT, **kwds: Unpack[ExcludeSeries] ) -> DataFrame[IntoDataFrameT]: ... - - @overload def from_native( - native_object: T, - *, - pass_through: Literal[True], - eager_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> T: ... - - -@overload -def from_native( - native_object: IntoDataFrameT | IntoLazyFrameT | IntoSeriesT, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[IntoDataFrameT] | LazyFrame[IntoLazyFrameT] | Series[IntoSeriesT]: ... - - + native_object: IntoSeriesT, **kwds: Unpack[OnlySeries] +) -> Series[IntoSeriesT]: ... @overload def from_native( - native_object: IntoSeriesT, - *, - pass_through: Literal[True], - eager_only: Literal[False] = ..., - series_only: Literal[True], - allow_series: None = ..., + native_object: IntoSeriesT, **kwds: Unpack[AllowSeries] ) -> Series[IntoSeriesT]: ... - - @overload def from_native( - native_object: IntoLazyFrameT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., + native_object: IntoLazyFrameT, **kwds: Unpack[AllowLazy] ) -> LazyFrame[IntoLazyFrameT]: ... - - @overload def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - -@overload -def from_native( - native_object: IntoDataFrameT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[True], - series_only: Literal[False] = ..., - allow_series: None = ..., -) -> DataFrame[IntoDataFrameT]: ... - - + native_object: IntoDataFrameT | IntoSeriesT, **kwds: Unpack[AllowSeries] +) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ... @overload def from_native( - native_object: IntoFrame | IntoSeries, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - series_only: Literal[False] = ..., - allow_series: Literal[True], -) -> DataFrame[Any] | LazyFrame[Any] | Series[Any]: ... - - + native_object: IntoDataFrameT | IntoLazyFrameT | IntoSeriesT, **kwds: Unpack[AllowAny] +) -> DataFrame[IntoDataFrameT] | LazyFrame[IntoLazyFrameT] | Series[IntoSeriesT]: ... @overload -def from_native( - native_object: IntoSeriesT, - *, - pass_through: Literal[False] = ..., - eager_only: Literal[False] = ..., - series_only: Literal[True], - allow_series: None = ..., -) -> Series[IntoSeriesT]: ... - - +def from_native(native_object: T, **kwds: Unpack[PassThroughUnknown]) -> T: ... # All params passed in as variables @overload def from_native( @@ -253,9 +156,7 @@ def from_native( series_only: bool, allow_series: bool | None, ) -> Any: ... - - -def from_native( # noqa: D417 +def from_native( native_object: IntoLazyFrameT | IntoDataFrameT | IntoSeriesT @@ -267,7 +168,6 @@ def from_native( # noqa: D417 eager_only: bool = False, series_only: bool = False, allow_series: bool | None = None, - **kwds: Any, ) -> LazyFrame[IntoLazyFrameT] | DataFrame[IntoDataFrameT] | Series[IntoSeriesT] | T: """Convert `native_object` to Narwhals Dataframe, Lazyframe, or Series. @@ -299,10 +199,6 @@ def from_native( # noqa: D417 DataFrame, LazyFrame, Series, or original object, depending on which combination of parameters was passed. """ - if kwds: - msg = f"from_native() got an unexpected keyword argument {next(iter(kwds))!r}" - raise TypeError(msg) - return _from_native_impl( # type: ignore[no-any-return] native_object, pass_through=pass_through, diff --git a/tests/translate/from_native_test.py b/tests/translate/from_native_test.py index 3bc58cbf2c..786ecea523 100644 --- a/tests/translate/from_native_test.py +++ b/tests/translate/from_native_test.py @@ -19,7 +19,8 @@ from __future__ import annotations -# mypy: disallow-any-generics=false +# Using pyright's assert type instead +# mypy: disallow-any-generics=false, disable-error-code="assert-type" from contextlib import nullcontext as does_not_raise from importlib.util import find_spec from itertools import chain @@ -342,6 +343,20 @@ def test_dataframe_recursive() -> None: assert_type(nw_frame_depth_2, nw.DataFrame[Any]) assert_type(nw_frame_early_return, nw.DataFrame[pl.DataFrame]) + # NOTE: Invalid parameter combinations + # Review runtime in https://github.com/narwhals-dev/narwhals/issues/3226 + nw.from_native(nw_frame, series_only=True) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=True) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=True, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=True, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=True, allow_series=True) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=False, series_only=True, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=False, series_only=True, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=False, series_only=True, allow_series=True) # type: ignore[call-overload] + nw.from_native(nw_frame, series_only=True, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_frame, series_only=True, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_frame, series_only=True, allow_series=True) # type: ignore[call-overload] + def test_lazyframe_recursive() -> None: pytest.importorskip("polars") @@ -363,6 +378,25 @@ def test_lazyframe_recursive() -> None: assert_type(nw_frame_depth_2, nw.LazyFrame[Any]) assert_type(nw_frame_early_return, nw.LazyFrame[pl.LazyFrame]) + # NOTE: Invalid parameter combinations + # Review runtime in https://github.com/narwhals-dev/narwhals/issues/3226 + nw.from_native(nw_frame, eager_only=True) # type: ignore[call-overload] + nw.from_native(nw_frame, series_only=True) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=True) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=False) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=True, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=True, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=True, allow_series=True) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=False, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=False, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=True, series_only=False, allow_series=True) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=False, series_only=True, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=False, series_only=True, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_frame, eager_only=False, series_only=True, allow_series=True) # type: ignore[call-overload] + nw.from_native(nw_frame, series_only=True, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_frame, series_only=True, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_frame, series_only=True, allow_series=True) # type: ignore[call-overload] + def test_series_recursive() -> None: """https://github.com/narwhals-dev/narwhals/issues/2239.""" @@ -385,6 +419,26 @@ def test_series_recursive() -> None: assert_type(nw_series_depth_2, nw.Series[Any]) assert_type(nw_series_early_return, nw.Series[pl.Series]) + # NOTE: Invalid parameter combinations + # Review runtime in https://github.com/narwhals-dev/narwhals/issues/3226 + nw.from_native(nw_series, eager_only=True) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=False) # type: ignore[call-overload] + nw.from_native(nw_series, series_only=False) # type: ignore[call-overload] + nw.from_native(nw_series, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_series, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=True, series_only=False) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=True, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=True, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=False, series_only=False) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=False, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=False, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_series, series_only=False, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_series, series_only=False, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=True, series_only=False, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=True, series_only=False, allow_series=None) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=False, series_only=False, allow_series=False) # type: ignore[call-overload] + nw.from_native(nw_series, eager_only=False, series_only=False, allow_series=None) # type: ignore[call-overload] + def test_from_native_invalid_keywords() -> None: pattern = r"from_native.+unexpected.+keyword.+bad_1" @@ -435,26 +489,288 @@ def test_pyspark_connect_deps_2517(constructor: Constructor) -> None: # pragma: nw.from_native(spark.createDataFrame([(1,)], ["a"])) -@pytest.mark.parametrize( - ("eager_only", "pass_through", "context"), - [ - (False, False, does_not_raise()), - (False, True, does_not_raise()), - (True, True, does_not_raise()), - (True, False, pytest.raises(TypeError, match="Cannot only use")), - ], -) -def test_eager_only_pass_through_main( - constructor: Constructor, *, eager_only: bool, pass_through: bool, context: Any -) -> None: +def test_eager_only_pass_through_main(constructor: Constructor) -> None: if not any(s in str(constructor) for s in ("pyspark", "dask", "ibis", "duckdb")): pytest.skip(reason="Non lazy or polars") df = constructor(data) - with context: - res = nw.from_native(df, eager_only=eager_only, pass_through=pass_through) # type: ignore[call-overload] - if eager_only and pass_through: - assert not isinstance(res, nw.LazyFrame) - else: - assert isinstance(res, nw.LazyFrame) + r1 = nw.from_native(df, eager_only=False, pass_through=False) + r2 = nw.from_native(df, eager_only=False, pass_through=True) + r3 = nw.from_native(df, eager_only=True, pass_through=True) + + assert isinstance(r1, nw.LazyFrame) + assert isinstance(r2, nw.LazyFrame) + assert not isinstance(r3, nw.LazyFrame) + + with pytest.raises(TypeError, match=r"Cannot.+use.+eager_only"): + nw.from_native(df, eager_only=True, pass_through=False) # type: ignore[type-var] + + +def test_from_native_lazyframe_exhaustive() -> None: # noqa: PLR0914, PLR0915 + pytest.importorskip("polars") + pytest.importorskip("typing_extensions") + + import polars as pl + from typing_extensions import assert_type + + pl_ldf = pl.LazyFrame(data) + + pl_1 = nw.from_native(pl_ldf) + pl_2 = nw.from_native(pl_ldf, pass_through=False) + pl_3 = nw.from_native(pl_ldf, pass_through=True) + pl_4 = nw.from_native(pl_ldf, eager_only=False) + pl_5 = nw.from_native(pl_ldf, series_only=False) + pl_6 = nw.from_native(pl_ldf, allow_series=False) + pl_7 = nw.from_native(pl_ldf, allow_series=None) + pl_8 = nw.from_native(pl_ldf, allow_series=True) + pl_9 = nw.from_native(pl_ldf, pass_through=False, eager_only=False) + pl_10 = nw.from_native(pl_ldf, pass_through=True, eager_only=False) + pl_11 = nw.from_native( + pl_ldf, pass_through=False, eager_only=False, series_only=False + ) + pl_12 = nw.from_native(pl_ldf, pass_through=True, eager_only=False, series_only=False) + pl_13 = nw.from_native( + pl_ldf, pass_through=False, eager_only=False, allow_series=False + ) + pl_14 = nw.from_native( + pl_ldf, pass_through=True, eager_only=False, allow_series=False + ) + pl_15 = nw.from_native( + pl_ldf, + pass_through=False, + eager_only=False, + series_only=False, + allow_series=False, + ) + pl_16 = nw.from_native( + pl_ldf, pass_through=True, eager_only=False, series_only=False, allow_series=False + ) + pl_17 = nw.from_native( + pl_ldf, pass_through=False, eager_only=False, allow_series=None + ) + pl_18 = nw.from_native(pl_ldf, pass_through=True, eager_only=False, allow_series=None) + pl_19 = nw.from_native( + pl_ldf, pass_through=False, eager_only=False, series_only=False, allow_series=None + ) + pl_20 = nw.from_native( + pl_ldf, pass_through=True, eager_only=False, series_only=False, allow_series=None + ) + pl_21 = nw.from_native( + pl_ldf, pass_through=False, eager_only=False, allow_series=True + ) + pl_21 = nw.from_native(pl_ldf, pass_through=True, eager_only=False, allow_series=True) + pl_21 = nw.from_native( + pl_ldf, pass_through=False, eager_only=False, series_only=False, allow_series=True + ) + pl_22 = nw.from_native( + pl_ldf, pass_through=True, eager_only=False, series_only=False, allow_series=True + ) + pl_23 = nw.from_native(pl_ldf, eager_only=False, series_only=False) + pl_24 = nw.from_native(pl_ldf, eager_only=False, allow_series=False) + pl_25 = nw.from_native( + pl_ldf, eager_only=False, series_only=False, allow_series=False + ) + pl_26 = nw.from_native(pl_ldf, eager_only=False, allow_series=None) + pl_27 = nw.from_native(pl_ldf, eager_only=False, series_only=False, allow_series=None) + pl_28 = nw.from_native(pl_ldf, eager_only=False, allow_series=True) + pl_29 = nw.from_native(pl_ldf, eager_only=False, series_only=False, allow_series=True) + pl_30 = nw.from_native( + pl_ldf, pass_through=False, series_only=False, allow_series=None + ) + pl_31 = nw.from_native( + pl_ldf, pass_through=False, series_only=False, allow_series=False + ) + pl_32 = nw.from_native( + pl_ldf, pass_through=False, series_only=False, allow_series=True + ) + pl_33 = nw.from_native( + pl_ldf, pass_through=True, series_only=False, allow_series=None + ) + pl_34 = nw.from_native( + pl_ldf, pass_through=True, series_only=False, allow_series=False + ) + pl_35 = nw.from_native( + pl_ldf, pass_through=True, series_only=False, allow_series=True + ) + pls = ( + pl_1, + pl_2, + pl_3, + pl_4, + pl_5, + pl_6, + pl_7, + pl_8, + pl_9, + pl_10, + pl_11, + pl_12, + pl_13, + pl_14, + pl_15, + pl_16, + pl_17, + pl_18, + pl_19, + pl_20, + pl_21, + pl_22, + pl_23, + pl_24, + pl_25, + pl_26, + pl_27, + pl_28, + pl_29, + pl_30, + pl_31, + pl_32, + pl_33, + pl_34, + pl_35, + ) + + assert_type(pl_1, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_2, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_3, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_4, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_5, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_6, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_7, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_8, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_9, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_10, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_11, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_12, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_13, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_14, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_15, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_16, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_17, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_18, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_19, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_20, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_21, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_22, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_23, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_24, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_25, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_26, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_27, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_28, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_29, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_30, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_31, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_32, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_33, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_34, nw.LazyFrame[pl.LazyFrame]) + assert_type(pl_35, nw.LazyFrame[pl.LazyFrame]) + + for ldf in pls: + assert isinstance(ldf, nw.LazyFrame) + + +def test_from_native_series_exhaustive() -> None: # noqa: PLR0914, PLR0915 + pytest.importorskip("polars") + pytest.importorskip("pandas") + pytest.importorskip("pyarrow") + pytest.importorskip("typing_extensions") + import pandas as pd + import polars as pl + import pyarrow as pa + from typing_extensions import assert_type + + pl_ser = pl.Series([1, 2, 3]) + pd_ser = cast("pd.Series[Any]", pd.Series([1, 2, 3])) + pa_ser = cast("pa.ChunkedArray[Any]", pa.chunked_array([pa.array([1])])) # type: ignore[redundant-cast] + + pl_1 = nw.from_native(pl_ser, series_only=True) + pl_2 = nw.from_native(pl_ser, allow_series=True) + pl_3 = nw.from_native(pl_ser, eager_only=True, series_only=True) + pl_4 = nw.from_native(pl_ser, eager_only=True, series_only=True, allow_series=True) + pl_5 = nw.from_native(pl_ser, eager_only=True, allow_series=True) + pl_6 = nw.from_native(pl_ser, series_only=True, allow_series=True) + pl_7 = nw.from_native(pl_ser, series_only=True, pass_through=True) + pl_8 = nw.from_native(pl_ser, allow_series=True, pass_through=True) + pl_9 = nw.from_native(pl_ser, eager_only=True, series_only=True, pass_through=True) + pl_10 = nw.from_native( + pl_ser, eager_only=True, series_only=True, allow_series=True, pass_through=True + ) + pl_11 = nw.from_native(pl_ser, eager_only=True, allow_series=True, pass_through=True) + pl_12 = nw.from_native(pl_ser, series_only=True, allow_series=True, pass_through=True) + pls = pl_1, pl_2, pl_3, pl_4, pl_5, pl_6, pl_7, pl_8, pl_9, pl_10, pl_11, pl_12 + + assert_type(pl_1, nw.Series[pl.Series]) + assert_type(pl_2, nw.Series[pl.Series]) + assert_type(pl_3, nw.Series[pl.Series]) + assert_type(pl_4, nw.Series[pl.Series]) + assert_type(pl_5, nw.Series[pl.Series]) + assert_type(pl_6, nw.Series[pl.Series]) + assert_type(pl_7, nw.Series[pl.Series]) + assert_type(pl_8, nw.Series[pl.Series]) + assert_type(pl_9, nw.Series[pl.Series]) + assert_type(pl_10, nw.Series[pl.Series]) + assert_type(pl_11, nw.Series[pl.Series]) + assert_type(pl_12, nw.Series[pl.Series]) + + pd_1 = nw.from_native(pd_ser, series_only=True) + pd_2 = nw.from_native(pd_ser, allow_series=True) + pd_3 = nw.from_native(pd_ser, eager_only=True, series_only=True) + pd_4 = nw.from_native(pd_ser, eager_only=True, series_only=True, allow_series=True) + pd_5 = nw.from_native(pd_ser, eager_only=True, allow_series=True) + pd_6 = nw.from_native(pd_ser, series_only=True, allow_series=True) + pd_7 = nw.from_native(pd_ser, series_only=True, pass_through=True) + pd_8 = nw.from_native(pd_ser, allow_series=True, pass_through=True) + pd_9 = nw.from_native(pd_ser, eager_only=True, series_only=True, pass_through=True) + pd_10 = nw.from_native( + pd_ser, eager_only=True, series_only=True, allow_series=True, pass_through=True + ) + pd_11 = nw.from_native(pd_ser, eager_only=True, allow_series=True, pass_through=True) + pd_12 = nw.from_native(pd_ser, series_only=True, allow_series=True, pass_through=True) + pds = pd_1, pd_2, pd_3, pd_4, pd_5, pd_6, pd_7, pd_8, pd_9, pd_10, pd_11, pd_12 + + assert_type(pd_1, nw.Series["pd.Series[Any]"]) + assert_type(pd_2, nw.Series["pd.Series[Any]"]) + assert_type(pd_3, nw.Series["pd.Series[Any]"]) + assert_type(pd_4, nw.Series["pd.Series[Any]"]) + assert_type(pd_5, nw.Series["pd.Series[Any]"]) + assert_type(pd_6, nw.Series["pd.Series[Any]"]) + assert_type(pd_7, nw.Series["pd.Series[Any]"]) + assert_type(pd_8, nw.Series["pd.Series[Any]"]) + assert_type(pd_9, nw.Series["pd.Series[Any]"]) + assert_type(pd_10, nw.Series["pd.Series[Any]"]) + assert_type(pd_11, nw.Series["pd.Series[Any]"]) + assert_type(pd_12, nw.Series["pd.Series[Any]"]) + + pa_1 = nw.from_native(pa_ser, series_only=True) + pa_2 = nw.from_native(pa_ser, allow_series=True) + pa_3 = nw.from_native(pa_ser, eager_only=True, series_only=True) + pa_4 = nw.from_native(pa_ser, eager_only=True, series_only=True, allow_series=True) + pa_5 = nw.from_native(pa_ser, eager_only=True, allow_series=True) + pa_6 = nw.from_native(pa_ser, series_only=True, allow_series=True) + pa_7 = nw.from_native(pa_ser, series_only=True, pass_through=True) + pa_8 = nw.from_native(pa_ser, allow_series=True, pass_through=True) + pa_9 = nw.from_native(pa_ser, eager_only=True, series_only=True, pass_through=True) + pa_10 = nw.from_native( + pa_ser, eager_only=True, series_only=True, allow_series=True, pass_through=True + ) + pa_11 = nw.from_native(pa_ser, eager_only=True, allow_series=True, pass_through=True) + pa_12 = nw.from_native(pa_ser, series_only=True, allow_series=True, pass_through=True) + pas = pa_1, pa_2, pa_3, pa_4, pa_5, pa_6, pa_7, pa_8, pa_9, pa_10, pa_11, pa_12 + + assert_type(pa_1, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_2, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_3, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_4, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_5, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_6, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_7, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_8, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_9, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_10, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_11, nw.Series["pa.ChunkedArray[Any]"]) + assert_type(pa_12, nw.Series["pa.ChunkedArray[Any]"]) + + for series in chain(pls, pds, pas): + assert isinstance(series, nw.Series) diff --git a/tests/utils_test.py b/tests/utils_test.py index 9d56774c9c..d2345a26a1 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -131,9 +131,11 @@ def test_maybe_set_index_pandas_direct_index( df = nw.from_native(native_df_or_series, allow_series=True) result = nw.maybe_set_index(df, index=narwhals_index) if isinstance(native_df_or_series, pd.Series): + assert isinstance(result, nw.Series) native_df_or_series.index = pandas_index # type: ignore[assignment] assert_series_equal(nw.to_native(result), native_df_or_series) else: + assert isinstance(result, nw.DataFrame) expected = native_df_or_series.set_index(pandas_index) # type: ignore[arg-type] assert_frame_equal(nw.to_native(result), expected)