Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
e745e37
chore!: Remove interchange (temporary)
dangotbanned Jun 27, 2026
4940eb3
chore: Reintroduce (limited) interchange support
dangotbanned Jun 27, 2026
3288b0d
grow up mypy
dangotbanned Jun 27, 2026
ce86e5c
test: fix invalid test
dangotbanned Jun 27, 2026
60068f0
test: isolate `test_invalid_filter`
dangotbanned Jun 28, 2026
3cfb767
chore: reintroduce `ibis`
dangotbanned Jun 28, 2026
6ba4d66
fix: remember that `duckdb` doesn't support interchange
dangotbanned Jun 28, 2026
902796a
fix: add compat alias for marimo
dangotbanned Jun 28, 2026
b7e533d
ignore mypy
dangotbanned Jun 28, 2026
4e9f986
Merge branch 'chore/issue-2932-rm-level' into chore/issue-2932-rm-lev…
dangotbanned Jun 29, 2026
3ceb3e0
test: add `tests.interchange`
dangotbanned Jun 29, 2026
ecfc247
test: migrate `get_level` tests
dangotbanned Jun 29, 2026
545e9a3
test: migrate `from_native` tests
dangotbanned Jun 29, 2026
fc21485
test: migrate `interchange_*` test modules
dangotbanned Jun 29, 2026
af940e6
docs: add a package doc
dangotbanned Jun 29, 2026
68506ea
fix: Don't require `eager_or_interchange_only=True`
dangotbanned Jun 29, 2026
d7c5b45
test: isolate interchange `is_into_*frame` tests
dangotbanned Jun 29, 2026
edc6fea
fix: Stop expecting interchange to support everything
dangotbanned Jun 29, 2026
4a85d71
chore: isolate `duckdb` interchange
dangotbanned Jun 29, 2026
af0bb3c
refactor: Simplify both `duckdb` and `ibis`
dangotbanned Jun 29, 2026
f4f7d72
test: cover `get_column`, `to_native` explicitly
dangotbanned Jun 29, 2026
68f8136
refactor: remove everything that isn't tested
dangotbanned Jun 29, 2026
28dbbf2
refactor: goodbye `supports_dataframe_interchange`
dangotbanned Jun 29, 2026
94f2e86
fix: avoid `Protocol.__init__`
dangotbanned Jun 30, 2026
395ee9d
chore: no cover on unreachable path
dangotbanned Jun 30, 2026
9adb2db
chore: remove dead comment
dangotbanned Jun 30, 2026
c27bfbd
refactor: Simplify `SQLLazyFrame.to_narwhals`
dangotbanned Jun 30, 2026
9a7879e
refactor: Expose what is actually used externally
dangotbanned Jun 30, 2026
0d596eb
refactor: Fix weird leftovers
dangotbanned Jun 30, 2026
322ccba
chore: rename and explain `__getattr__` trick
dangotbanned Jun 30, 2026
7699757
docs(typing): Explain magic
dangotbanned Jun 30, 2026
7c121b7
refactor: `__all__` is shorter
dangotbanned Jun 30, 2026
0402c65
shrinky-dink
dangotbanned Jun 30, 2026
4eb26e2
fix: Don't give false hope
dangotbanned Jun 30, 2026
fd13f4b
refactor: remove `_interchange.series.py`
dangotbanned Jun 30, 2026
35478b3
refactor: share more between interchange series/frame
dangotbanned Jun 30, 2026
c857c8c
test: update error pattern x2
dangotbanned Jun 30, 2026
f19899b
refactor: consistent imports
dangotbanned Jun 30, 2026
9f794e5
test: move fixtures to `interchange.conftest`
dangotbanned Jun 30, 2026
4bbdf11
merge upstream
FBruzzesi Jul 2, 2026
bb05096
fix: exclude `cudf.DataFrame` too
dangotbanned Jul 3, 2026
28545e0
refactor: lower density, more clarity?
dangotbanned Jul 3, 2026
8eb114c
chore: guard against `ibis` removing `__dataframe__` support
dangotbanned Jul 3, 2026
3199d03
no cov fireducks
dangotbanned Jul 3, 2026
955a677
refactor: invert checks, explain a bit more
dangotbanned Jul 3, 2026
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
34 changes: 3 additions & 31 deletions src/narwhals/_ibis/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,23 @@
from types import ModuleType
from typing import TypeAlias

import pandas as pd
import pyarrow as pa
from ibis.expr.operations import Binary
from typing_extensions import Self, TypeIs

from narwhals._compliant.typing import CompliantDataFrameAny
from narwhals._ibis.group_by import IbisGroupBy
from narwhals._ibis.namespace import IbisNamespace
from narwhals._ibis.series import IbisInterchangeSeries
from narwhals._typing import _EagerAllowedImpl
from narwhals._utils import _LimitedContext
from narwhals.dataframe import LazyFrame
from narwhals.dtypes import DType
from narwhals.stable.v1 import DataFrame as DataFrameV1
from narwhals.typing import AsofJoinStrategy, JoinStrategy, UniqueKeepStrategy

JoinPredicates: TypeAlias = "Sequence[ir.BooleanColumn] | Sequence[str]"


class IbisLazyFrame(
SQLLazyFrame["IbisExpr", "ir.Table", "LazyFrame[ir.Table] | DataFrameV1[ir.Table]"],
ValidateBackendVersion,
SQLLazyFrame["IbisExpr", "ir.Table", "LazyFrame[ir.Table]"], ValidateBackendVersion
):
_implementation = Implementation.IBIS

Expand All @@ -70,20 +65,9 @@ def _is_native(obj: ir.Table | Any) -> TypeIs[ir.Table]:
def from_native(cls, data: ir.Table, /, *, context: _LimitedContext) -> Self:
return cls(data, version=context._version)

def to_narwhals(self) -> LazyFrame[ir.Table] | DataFrameV1[ir.Table]:
if self._version is Version.V1:
from narwhals.stable.v1 import DataFrame

return DataFrame(self)
def to_narwhals(self) -> LazyFrame[ir.Table]:
return self._version.lazyframe(self)

def __narwhals_dataframe__(self) -> Self: # pragma: no cover
# Keep around for backcompat.
if self._version is not Version.V1:
msg = "__narwhals_dataframe__ is not implemented for IbisLazyFrame"
raise AttributeError(msg)
return self

def __narwhals_lazyframe__(self) -> Self:
return self

Expand All @@ -95,11 +79,6 @@ def __narwhals_namespace__(self) -> IbisNamespace:

return IbisNamespace(version=self._version)

def get_column(self, name: str) -> IbisInterchangeSeries:
from narwhals._ibis.series import IbisInterchangeSeries

return IbisInterchangeSeries(self.native.select(name), version=self._version)

def _iter_columns(self) -> Iterator[ir.Expr]:
for name in self.columns:
yield self.native[name]
Expand Down Expand Up @@ -167,6 +146,7 @@ def drop(self, columns: Sequence[str], *, strict: bool) -> Self:
selection = (col for col in self.columns if col not in columns_to_drop)
return self._with_native(self.native.select(*selection))

# TODO @dangotbanned: Figure out if this is needed
def lazy(self, backend: None = None, **_: None) -> Self:
# The `backend`` argument has no effect but we keep it here for
# backwards compatibility because in `narwhals.stable.v1`
Expand Down Expand Up @@ -207,14 +187,6 @@ def columns(self) -> list[str]:
)
return self._cached_columns

def to_pandas(self) -> pd.DataFrame:
# only if version is v1, keep around for backcompat
return self.native.to_pandas()

def to_arrow(self) -> pa.Table:
# only if version is v1, keep around for backcompat
return self.native.to_pyarrow()

def _with_version(self, version: Version) -> Self:
return self.__class__(self.native, version=version)

Expand Down
75 changes: 75 additions & 0 deletions src/narwhals/_ibis/interchange.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from narwhals import _interchange
from narwhals._ibis.utils import native_to_narwhals_dtype
from narwhals._utils import Implementation

if TYPE_CHECKING:
from collections.abc import Sequence

import ibis.expr.types as ir
import ibis.expr.types.dataframe_interchange as ibis_dfi
import pandas as pd
import pyarrow as pa
from typing_extensions import Self

from narwhals.dtypes import DType


class IbisDataFrame(_interchange.InterchangeFrameV1["ir.Table"]):
_implementation = Implementation.IBIS

def __init__(self, df: ir.Table | ibis_dfi.IbisDataFrame) -> None:
self._dfi: ibis_dfi.IbisDataFrame = df.__dataframe__()

@classmethod
def from_dfi(cls, df: ibis_dfi.IbisDataFrame) -> Self:
self = cls.__new__(cls)
self._dfi = df
return self

@property
def native(self) -> ir.Table:
return self._dfi._table

@property
def _df(self) -> ir.Table:
return self.native

def to_pandas(self) -> pd.DataFrame:
return self.native.to_pandas()

def to_arrow(self) -> pa.Table:
return self.native.to_pyarrow()

def get_column(self, name: str) -> IbisSeries:
return IbisSeries(self.native.select(name))

def column_names(self) -> list[str]:
return list(self.native.columns)

def select_columns_by_name(self, names: Sequence[str]) -> Self:
return self.from_dfi(self._dfi.select_columns_by_name(names))

@property
def schema(self) -> dict[str, DType]:
return {
name: native_to_narwhals_dtype(dtype, self._version)
for name, dtype in self.native.schema().fields.items()
}


class IbisSeries(_interchange.InterchangeSeriesV1["ir.Table"]):
_implementation = Implementation.IBIS

def __init__(self, df: ir.Table) -> None:
self._native_series: ir.Table = df

@property
def dtype(self) -> DType:
native = next(iter(self._native_series.schema().values()))
# NOTE: `Concrete` (base class for `DataType`) has two bases that define `__hash__ = None`,
# and then decides to implement `__hash__`? Okay ...
return native_to_narwhals_dtype(native, self._version) # type: ignore[arg-type]
41 changes: 0 additions & 41 deletions src/narwhals/_ibis/series.py

This file was deleted.

6 changes: 6 additions & 0 deletions src/narwhals/_interchange/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from __future__ import annotations

from narwhals._interchange.dataframe import (
InterchangeFrameV1 as InterchangeFrameV1,
InterchangeSeriesV1 as InterchangeSeriesV1,
)
Loading
Loading