feat: Adds private Namespace class#2324
Conversation
- Currently just a fancy `nw.utils._into_compliant_namespace` - `_version` defined in the same way as `nw.Schema` - Just override in subclass
We *at least* get completions for `self.compliant`, even if they just resolve to `Any`
Will support the pattern from (#2315)
Tests passing now phew
Also checks repr at runtime, which right now is the same as annotations
These are the *kinds* of tasks I'm hoping to simplify and introduce typing to
Might switch more over if they show up as issues in CI
- The stuff in `nw.utils` will be replaced by this afterwards - Best to avoid depending on them for now
| @property | ||
| def _backend_version(self) -> tuple[int, ...]: | ||
| native = self.to_native_namespace() | ||
| into_version: Any | ||
| if self not in { | ||
| Implementation.PYSPARK, | ||
| Implementation.DASK, | ||
| Implementation.SQLFRAME, | ||
| }: | ||
| into_version = native | ||
| elif self is Implementation.PYSPARK: | ||
| into_version = get_pyspark() | ||
| elif self is Implementation.DASK: | ||
| into_version = get_dask() | ||
| else: | ||
| import sqlframe._version | ||
|
|
||
| into_version = sqlframe._version | ||
| return parse_version(into_version) |
There was a problem hiding this comment.
This could be used in place of most calls we currently do to parse_version.
Here we'd only need Implementation and use the same property for all of them.
is_sqlframe_dataframeSparkLikeLazyFramesqlframe._version
narwhals/narwhals/translate.py
Lines 755 to 775 in 4d2b9d5
If you then combine this PR with (#2315), we can do:
from __future__ import annotations
from typing import cast
from narwhals._namespace import Namespace
import narwhals as nw
if TYPE_CHECKING:
from narwhals._spark_like.dataframe import SQLFrameDataFrame
native_object = cast("SQLFrameDataFrame", "pretend im a sqlframe")
nw.LazyFrame(
Namespace.from_native_object(native_object).compliant.from_native(native_object),
level="lazy",
)There was a problem hiding this comment.
For the last nw.LazyFrame part, we could even add something like this to Compliant* classes:
ToNarwhals Protocol
from __future__ import annotations
from typing import Any
from typing import Protocol
from typing import TypeVar
ToNarwhalsT_co = TypeVar("ToNarwhalsT_co", covariant=True)
class ToNarwhals(Protocol[ToNarwhalsT_co]):
def to_narwhals(self, *args: Any, **kwds: Any) -> ToNarwhalsT_co: ...Since they're already initialized, they have a Version - so we have everything we need:
SparkLikeLazyFrame implementation
from __future__ import annotations
from typing import TYPE_CHECKING
from narwhals.typing import CompliantLazyFrame
from narwhals.utils import Implementation
from narwhals.utils import Version
if TYPE_CHECKING:
from narwhals._spark_like.expr import SparkLikeExpr
from narwhals.dataframe import LazyFrame
SQLFrameDataFrame = BaseDataFrame[Any, Any, Any, Any, Any]
class SparkLikeLazyFrame(CompliantLazyFrame["SparkLikeExpr", "SQLFrameDataFrame"]):
_native_frame: SQLFrameDataFrame
_implementation: Implementation
_backend_version: tuple[int, ...]
_version: Version
def to_narwhals(self) -> LazyFrame[SQLFrameDataFrame]:
if self._version is Version.MAIN:
from narwhals.dataframe import LazyFrame
return LazyFrame(self, level="lazy")
from narwhals.stable.v1 import LazyFrame as LazyFrameV1
return LazyFrameV1(self, level="lazy")Putting it all together
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import cast
from narwhals._namespace import Namespace
if TYPE_CHECKING:
from narwhals._spark_like.dataframe import SQLFrameDataFrame
native_object = cast("SQLFrameDataFrame", "pretend im a sqlframe")
narwhals_object = (
Namespace.from_native_object(native_object)
.compliant.from_native(native_object)
.to_narwhals()
)I think this is pretty clean 🙂
| class _NativeDask(Protocol): | ||
| _partition_type: type[pd.DataFrame] | ||
|
|
||
| class _NativeCuDF(Protocol): | ||
| def to_pylibcudf(self, *args: Any, **kwds: Any) -> Any: ... | ||
|
|
||
| class _ModinDataFrame(Protocol): | ||
| _pandas_class: type[pd.DataFrame] | ||
|
|
||
| class _ModinSeries(Protocol): | ||
| _pandas_class: type[pd.Series[Any]] |
There was a problem hiding this comment.
I tried using the actual types first (acb5787), but they broke the @overload(s).
These seem specific enough to match only the intended target(s)
thanks @MarcoGorelli! Just going to make the replacements now in |
|
@MarcoGorelli after (#2324 (commits)) I'm gonna call it here, too easy to get carried away 😅 Hopefully you get an idea for how much of |
Namespace classNamespace class

What type of PR is this? (check all applicable)
Related issues
*(Namespace|DataFrame).from_numpy#2283)Compliant*.from_native#2315CompliantDataFrame.from_dict#2304Checklist
If you have comments or can explain your changes, please do so below
Had this idea nagging away at me while working on (#2116).
Super high-level
This PR adds a
Namespaceclass, that can be created via either:Supporting these new
@classmethods is a new method forImplementation:Notes
I've tried to keep things isolated from the rest of
narwhals- for now - but the goal would be replacing similar logic from all of:functions.pytranslate.pyutils.pyImportant
Not planning to make this a public class
Example
This further simplifies (#2283) to a one-liner, with the added bonus of typing from a
backendstring