-
Notifications
You must be signed in to change notification settings - Fork 170
feat: Adds private Namespace class
#2324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
| elif is_sqlframe_dataframe(native_object): # pragma: no cover | |
| from narwhals._spark_like.dataframe import SparkLikeLazyFrame | |
| if series_only: | |
| msg = "Cannot only use `series_only` with SQLFrame DataFrame" | |
| raise TypeError(msg) | |
| if eager_only or eager_or_interchange_only: | |
| msg = "Cannot only use `eager_only` or `eager_or_interchange_only` with SQLFrame DataFrame" | |
| raise TypeError(msg) | |
| import sqlframe._version | |
| backend_version = parse_version(sqlframe._version) | |
| return LazyFrame( | |
| SparkLikeLazyFrame( | |
| native_object, | |
| backend_version=backend_version, | |
| version=version, | |
| implementation=Implementation.SQLFRAME, | |
| ), | |
| level="lazy", | |
| ) |
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @dangotbanned
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