"""Narwhals-level equivalent of `CompliantNamespace`.""" from __future__ import annotations from typing import ( TYPE_CHECKING, Any, Callable, Generic, Literal, Protocol, TypeVar, overload, ) from narwhals._compliant.typing import CompliantNamespaceAny, CompliantNamespaceT_co from narwhals._utils import Implementation, Version from narwhals.dependencies import ( get_cudf, get_modin, get_pandas, get_polars, get_pyarrow, is_dask_dataframe, is_duckdb_relation, is_ibis_table, is_pyspark_connect_dataframe, is_pyspark_dataframe, is_sqlframe_dataframe, ) if TYPE_CHECKING: from types import ModuleType from typing import ClassVar import duckdb import pandas as pd import polars as pl import pyarrow as pa import pyspark.sql as pyspark_sql from pyspark.sql.connect.dataframe import DataFrame as PySparkConnectDataFrame from typing_extensions import TypeAlias, TypeIs from narwhals._arrow.namespace import ArrowNamespace from narwhals._dask.namespace import DaskNamespace from narwhals._duckdb.namespace import DuckDBNamespace from narwhals._ibis.namespace import IbisNamespace from narwhals._pandas_like.namespace import PandasLikeNamespace from narwhals._polars.namespace import PolarsNamespace from narwhals._spark_like.dataframe import SQLFrameDataFrame from narwhals._spark_like.namespace import SparkLikeNamespace from narwhals.typing import DataFrameLike, NativeFrame, NativeLazyFrame, NativeSeries T = TypeVar("T") _Guard: TypeAlias = "Callable[[Any], TypeIs[T]]" _Polars: TypeAlias = Literal["polars"] _Arrow: TypeAlias = Literal["pyarrow"] _Dask: TypeAlias = Literal["dask"] _DuckDB: TypeAlias = Literal["duckdb"] _PandasLike: TypeAlias = Literal["pandas", "cudf", "modin"] _Ibis: TypeAlias = Literal["ibis"] _SparkLike: TypeAlias = Literal["pyspark", "sqlframe", "pyspark[connect]"] _EagerOnly: TypeAlias = "_PandasLike | _Arrow" _EagerAllowed: TypeAlias = "_Polars | _EagerOnly" _LazyOnly: TypeAlias = "_SparkLike | _Dask | _DuckDB | _Ibis" _LazyAllowed: TypeAlias = "_Polars | _LazyOnly" Polars: TypeAlias = Literal[_Polars, Implementation.POLARS] Arrow: TypeAlias = Literal[_Arrow, Implementation.PYARROW] Dask: TypeAlias = Literal[_Dask, Implementation.DASK] DuckDB: TypeAlias = Literal[_DuckDB, Implementation.DUCKDB] Ibis: TypeAlias = Literal[_Ibis, Implementation.IBIS] PandasLike: TypeAlias = Literal[ _PandasLike, Implementation.PANDAS, Implementation.CUDF, Implementation.MODIN ] SparkLike: TypeAlias = Literal[ _SparkLike, Implementation.PYSPARK, Implementation.SQLFRAME, Implementation.PYSPARK_CONNECT, ] EagerOnly: TypeAlias = "PandasLike | Arrow" EagerAllowed: TypeAlias = "EagerOnly | Polars" LazyOnly: TypeAlias = "SparkLike | Dask | DuckDB | Ibis" LazyAllowed: TypeAlias = "LazyOnly | Polars" BackendName: TypeAlias = "_EagerAllowed | _LazyAllowed" IntoBackend: TypeAlias = "BackendName | Implementation | ModuleType" EagerAllowedNamespace: TypeAlias = "Namespace[PandasLikeNamespace] | Namespace[ArrowNamespace] | Namespace[PolarsNamespace]" EagerAllowedImplementation: TypeAlias = Literal[ Implementation.PANDAS, Implementation.CUDF, Implementation.MODIN, Implementation.PYARROW, Implementation.POLARS, ] class _NativeDask(Protocol): _partition_type: type[pd.DataFrame] class _NativeCuDF(Protocol): def to_pylibcudf(self, *args: Any, **kwds: Any) -> Any: ... class _NativeIbis(Protocol): def sql(self, *args: Any, **kwds: Any) -> Any: ... def __pyarrow_result__(self, *args: Any, **kwds: Any) -> Any: ... def __pandas_result__(self, *args: Any, **kwds: Any) -> Any: ... def __polars_result__(self, *args: Any, **kwds: Any) -> Any: ... class _ModinDataFrame(Protocol): _pandas_class: type[pd.DataFrame] class _ModinSeries(Protocol): _pandas_class: type[pd.Series[Any]] _NativePolars: TypeAlias = "pl.DataFrame | pl.LazyFrame | pl.Series" _NativeArrow: TypeAlias = "pa.Table | pa.ChunkedArray[Any]" _NativeDuckDB: TypeAlias = "duckdb.DuckDBPyRelation" _NativePandas: TypeAlias = "pd.DataFrame | pd.Series[Any]" _NativeModin: TypeAlias = "_ModinDataFrame | _ModinSeries" _NativePandasLike: TypeAlias = "_NativePandas | _NativeCuDF | _NativeModin" _NativeSQLFrame: TypeAlias = "SQLFrameDataFrame" _NativePySpark: TypeAlias = "pyspark_sql.DataFrame" _NativePySparkConnect: TypeAlias = "PySparkConnectDataFrame" _NativeSparkLike: TypeAlias = ( "_NativeSQLFrame | _NativePySpark | _NativePySparkConnect" ) NativeKnown: TypeAlias = "_NativePolars | _NativeArrow | _NativePandasLike | _NativeSparkLike | _NativeDuckDB | _NativeDask | _NativeIbis" NativeUnknown: TypeAlias = ( "NativeFrame | NativeSeries | NativeLazyFrame | DataFrameLike" ) NativeAny: TypeAlias = "NativeKnown | NativeUnknown" __all__ = ["Namespace"] class Namespace(Generic[CompliantNamespaceT_co]): _compliant_namespace: CompliantNamespaceT_co _version: ClassVar[Version] = Version.MAIN def __init__(self, namespace: CompliantNamespaceT_co, /) -> None: self._compliant_namespace = namespace def __init_subclass__(cls, *args: Any, version: Version, **kwds: Any) -> None: super().__init_subclass__(*args, **kwds) if isinstance(version, Version): cls._version = version else: msg = f"Expected {Version} but got {type(version).__name__!r}" raise TypeError(msg) def __repr__(self) -> str: return f"Namespace[{type(self.compliant).__name__}]" @property def compliant(self) -> CompliantNamespaceT_co: return self._compliant_namespace @property def implementation(self) -> Implementation: return self.compliant._implementation @property def version(self) -> Version: return self._version @overload @classmethod def from_backend(cls, backend: PandasLike, /) -> Namespace[PandasLikeNamespace]: ... @overload @classmethod def from_backend(cls, backend: Polars, /) -> Namespace[PolarsNamespace]: ... @overload @classmethod def from_backend(cls, backend: Arrow, /) -> Namespace[ArrowNamespace]: ... @overload @classmethod def from_backend(cls, backend: SparkLike, /) -> Namespace[SparkLikeNamespace]: ... @overload @classmethod def from_backend(cls, backend: DuckDB, /) -> Namespace[DuckDBNamespace]: ... @overload @classmethod def from_backend(cls, backend: Dask, /) -> Namespace[DaskNamespace]: ... @overload @classmethod def from_backend(cls, backend: Ibis, /) -> Namespace[IbisNamespace]: ... @overload @classmethod def from_backend(cls, backend: EagerAllowed, /) -> EagerAllowedNamespace: ... @overload @classmethod def from_backend( cls, backend: IntoBackend, / ) -> Namespace[CompliantNamespaceAny]: ... @classmethod def from_backend( cls: type[Namespace[Any]], backend: IntoBackend, / ) -> Namespace[Any]: """Instantiate from native namespace module, string, or Implementation. Arguments: backend: native namespace module, string, or Implementation. Returns: Namespace. Examples: >>> from narwhals._namespace import Namespace >>> Namespace.from_backend("polars") Namespace[PolarsNamespace] """ impl = Implementation.from_backend(backend) backend_version = impl._backend_version() version = cls._version ns: CompliantNamespaceAny if impl.is_pandas_like(): from narwhals._pandas_like.namespace import PandasLikeNamespace ns = PandasLikeNamespace( implementation=impl, backend_version=backend_version, version=version ) elif impl.is_polars(): from narwhals._polars.namespace import PolarsNamespace ns = PolarsNamespace(backend_version=backend_version, version=version) elif impl.is_pyarrow(): from narwhals._arrow.namespace import ArrowNamespace ns = ArrowNamespace(backend_version=backend_version, version=version) elif impl.is_spark_like(): from narwhals._spark_like.namespace import SparkLikeNamespace ns = SparkLikeNamespace( implementation=impl, backend_version=backend_version, version=version ) elif impl.is_duckdb(): from narwhals._duckdb.namespace import DuckDBNamespace ns = DuckDBNamespace(backend_version=backend_version, version=version) elif impl.is_dask(): from narwhals._dask.namespace import DaskNamespace ns = DaskNamespace(backend_version=backend_version, version=version) elif impl.is_ibis(): from narwhals._ibis.namespace import IbisNamespace ns = IbisNamespace(backend_version=backend_version, version=version) else: msg = "Not supported Implementation" # pragma: no cover raise AssertionError(msg) return cls(ns) @overload @classmethod def from_native_object( cls, native: _NativePolars, / ) -> Namespace[PolarsNamespace]: ... @overload @classmethod def from_native_object( cls, native: _NativePandasLike, / ) -> Namespace[PandasLikeNamespace]: ... @overload @classmethod def from_native_object(cls, native: _NativeArrow, /) -> Namespace[ArrowNamespace]: ... @overload @classmethod def from_native_object( cls, native: _NativeSparkLike, / ) -> Namespace[SparkLikeNamespace]: ... @overload @classmethod def from_native_object( cls, native: _NativeDuckDB, / ) -> Namespace[DuckDBNamespace]: ... @overload @classmethod def from_native_object(cls, native: _NativeDask, /) -> Namespace[DaskNamespace]: ... @overload @classmethod def from_native_object(cls, native: _NativeIbis, /) -> Namespace[IbisNamespace]: ... @overload @classmethod def from_native_object( cls, native: NativeUnknown, / ) -> Namespace[CompliantNamespaceAny]: ... @classmethod def from_native_object( # noqa: PLR0911 cls: type[Namespace[Any]], native: NativeAny, / ) -> Namespace[Any]: if is_native_polars(native): return cls.from_backend(Implementation.POLARS) elif is_native_pandas(native): return cls.from_backend(Implementation.PANDAS) elif is_native_arrow(native): return cls.from_backend(Implementation.PYARROW) elif is_native_spark_like(native): return cls.from_backend( Implementation.SQLFRAME if is_native_sqlframe(native) else Implementation.PYSPARK_CONNECT if is_native_pyspark_connect(native) else Implementation.PYSPARK ) elif is_native_dask(native): return cls.from_backend(Implementation.DASK) # pragma: no cover elif is_native_duckdb(native): return cls.from_backend(Implementation.DUCKDB) elif is_native_cudf(native): # pragma: no cover return cls.from_backend(Implementation.CUDF) elif is_native_modin(native): # pragma: no cover return cls.from_backend(Implementation.MODIN) elif is_native_ibis(native): return cls.from_backend(Implementation.IBIS) else: msg = f"Unsupported type: {type(native).__qualname__!r}" raise TypeError(msg) def is_native_polars(obj: Any) -> TypeIs[_NativePolars]: return (pl := get_polars()) is not None and isinstance( obj, (pl.DataFrame, pl.Series, pl.LazyFrame) ) def is_native_arrow(obj: Any) -> TypeIs[_NativeArrow]: return (pa := get_pyarrow()) is not None and isinstance( obj, (pa.Table, pa.ChunkedArray) ) def is_native_dask(obj: Any) -> TypeIs[_NativeDask]: return is_dask_dataframe(obj) is_native_duckdb: _Guard[_NativeDuckDB] = is_duckdb_relation is_native_sqlframe: _Guard[_NativeSQLFrame] = is_sqlframe_dataframe is_native_pyspark: _Guard[_NativePySpark] = is_pyspark_dataframe is_native_pyspark_connect: _Guard[_NativePySparkConnect] = is_pyspark_connect_dataframe def is_native_pandas(obj: Any) -> TypeIs[_NativePandas]: return (pd := get_pandas()) is not None and isinstance(obj, (pd.DataFrame, pd.Series)) def is_native_modin(obj: Any) -> TypeIs[_NativeModin]: return (mpd := get_modin()) is not None and isinstance( obj, (mpd.DataFrame, mpd.Series) ) # pragma: no cover def is_native_cudf(obj: Any) -> TypeIs[_NativeCuDF]: return (cudf := get_cudf()) is not None and isinstance( obj, (cudf.DataFrame, cudf.Series) ) # pragma: no cover def is_native_pandas_like(obj: Any) -> TypeIs[_NativePandasLike]: return ( is_native_pandas(obj) or is_native_cudf(obj) or is_native_modin(obj) ) # pragma: no cover def is_native_spark_like(obj: Any) -> TypeIs[_NativeSparkLike]: return ( is_native_sqlframe(obj) or is_native_pyspark(obj) or is_native_pyspark_connect(obj) ) def is_native_ibis(obj: Any) -> TypeIs[_NativeIbis]: return is_ibis_table(obj)