Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions narwhals/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -2361,6 +2361,8 @@ class LazyFrame(BaseFrame[LazyFrameT]):
```
"""

_version: ClassVar[Version] = Version.MAIN
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hoping this has been an oversight and wasn't excluded for a strong reason. This change makes the DataFrame/LazyFrame api a bit more consistent internally.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah nice one, thanks @camriddell!

If you dig through the blame (I think) I added these on Series & DataFrame at the same time as the from_* constructors.

LazyFrame didn't get any new methods, so it slipped through 😅


@property
def _compliant(self) -> CompliantLazyFrame[Any, LazyFrameT, Self]:
return self._compliant_frame
Expand Down
4 changes: 2 additions & 2 deletions narwhals/stable/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from __future__ import annotations

from narwhals.stable import v1
from narwhals.stable import v1, v2

__all__ = ["v1"]
__all__ = ["v1", "v2"]
2 changes: 2 additions & 0 deletions narwhals/stable/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ def _l1_norm(self) -> Self:


class LazyFrame(NwLazyFrame[IntoLazyFrameT]):
_version = Version.V1

@inherit_doc(NwLazyFrame)
def __init__(self, df: Any, *, level: Literal["full", "lazy", "interchange"]) -> None:
assert df._version is Version.V1 # noqa: S101
Expand Down
2 changes: 2 additions & 0 deletions narwhals/stable/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ def is_unique(self) -> Series[Any]:


class LazyFrame(NwLazyFrame[IntoLazyFrameT]):
_version = Version.V2

@inherit_doc(NwLazyFrame)
def __init__(self, df: Any, *, level: Literal["full", "lazy", "interchange"]) -> None:
assert df._version is Version.V2 # noqa: S101
Expand Down
20 changes: 18 additions & 2 deletions narwhals/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,25 @@ def _from_native_impl( # noqa: C901, PLR0911, PLR0912, PLR0915

# Early returns
if isinstance(native_object, (DataFrame, LazyFrame)) and not series_only:
return native_object
if native_object._version is version:
return native_object

real_native_object = native_object.to_native()
return (
version.namespace.from_native_object(real_native_object)
.compliant.from_native(real_native_object)
.to_narwhals()
)
if isinstance(native_object, Series) and (series_only or allow_series):
return native_object
if native_object._version is version:
return native_object

real_native_object = native_object.to_native()
return (
version.namespace.from_native_object(real_native_object)
.compliant.from_native(real_native_object)
.to_narwhals()
)

if series_only:
if allow_series is False:
Expand Down
34 changes: 30 additions & 4 deletions tests/translate/from_native_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,14 @@ def test_pandas_like_validate() -> None:


@pytest.mark.skipif(lf_pl is None, reason="polars not found")
def test_init_already_narwhals() -> None:
df = nw.from_native(pl.DataFrame({"a": [1, 2, 3]}))
result = nw.from_native(df)
def test_init_already_narwhals_stable() -> None:
from narwhals.stable import v1 as nw_v1

df = nw_v1.from_native(pl.DataFrame({"a": [1, 2, 3]}))
result = nw_v1.from_native(df)
assert result is df
s = df["a"]
result_s = nw.from_native(s, allow_series=True)
result_s = nw_v1.from_native(s, allow_series=True)
assert result_s is s


Expand All @@ -225,6 +227,30 @@ def test_init_already_narwhals_unstable() -> None:
assert result_s is s


@pytest.mark.skipif(lf_pl is None, reason="polars not found")
def test_init_already_narwhals_unstable_to_stable() -> None:
from narwhals.stable import v1 as nw_v1

native = pl.DataFrame({"a": [1, 2, 3]})

unstable = nw.from_native(native)
stablified = nw_v1.from_native(unstable)

assert isinstance(stablified, nw_v1.DataFrame)


@pytest.mark.skipif(lf_pl is None, reason="polars not found")
def test_init_already_narwhals_stable_to_unstable() -> None:
from narwhals.stable import v1 as nw_v1

native = pl.DataFrame({"a": [1, 2, 3]})

stable_v1 = nw_v1.from_native(native)
unstablified = nw.from_native(stable_v1)

assert isinstance(unstablified, nw.DataFrame)


@pytest.mark.skipif(df_pd is None, reason="pandas not found")
def test_series_only_dask() -> None:
pytest.importorskip("dask")
Expand Down
2 changes: 1 addition & 1 deletion tests/v1_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ def test_lazyframe_recursive_v1() -> None:

pl_frame = pl.DataFrame({"a": [1, 2, 3]}).lazy()
nw_frame = nw_v1.from_native(pl_frame)
with pytest.raises(AttributeError):
with pytest.raises(AssertionError):
Copy link
Member

@dangotbanned dangotbanned Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, there may be a comment somewhere mentioning the error?

(Ignore me if this is too vague 😂)

nw_v1.LazyFrame(nw_frame, level="lazy")

nw_frame_early_return = nw_v1.from_native(nw_frame)
Expand Down
Loading