Repository: narwhals-dev/narwhals

Issue:
enh: support `.collect_schema` for interchange-level dataframe
Currently we can only use `.schema`. We should definitely support `.collect_schema` as well

The following should work:
```python
import narwhals as nw

import duckdb
import polars as pl

df = pl.DataFrame({'a': [1,2,3]})
rel = duckdb.table('df')
assert nw.from_native(rel).collect_schema()['a'] == nw.Int64
```

Discussion:

Patch:
diff --git a/narwhals/_duckdb/dataframe.py b/narwhals/_duckdb/dataframe.py
index fe4a5856..339fca13 100644
--- a/narwhals/_duckdb/dataframe.py
+++ b/narwhals/_duckdb/dataframe.py
@@ -151,3 +151,11 @@ class DuckDBInterchangeFrame:
 
     def _from_native_frame(self: Self, df: Any) -> Self:
         return self.__class__(df, version=self._version)
+
+    def collect_schema(self) -> dict[str, DType]:
+        return {
+            column_name: native_to_narwhals_dtype(str(duckdb_dtype), self._version)
+            for column_name, duckdb_dtype in zip(
+                self._native_frame.columns, self._native_frame.types
+            )
+        }
diff --git a/narwhals/_ibis/dataframe.py b/narwhals/_ibis/dataframe.py
index fe9bb034..f62a31e8 100644
--- a/narwhals/_ibis/dataframe.py
+++ b/narwhals/_ibis/dataframe.py
@@ -129,3 +129,9 @@ class IbisInterchangeFrame:
 
     def _from_native_frame(self: Self, df: Any) -> Self:
         return self.__class__(df, version=self._version)
+
+    def collect_schema(self) -> dict[str, DType]:
+        return {
+            column_name: native_to_narwhals_dtype(ibis_dtype, self._version)
+            for column_name, ibis_dtype in self._native_frame.schema().items()
+        }


Test patch:
diff --git a/tests/frame/interchange_schema_test.py b/tests/frame/interchange_schema_test.py
index 588c9259..e06a482d 100644
--- a/tests/frame/interchange_schema_test.py
+++ b/tests/frame/interchange_schema_test.py
@@ -156,6 +156,7 @@ def test_interchange_schema_ibis(
     assert result == expected
     assert df["a"].dtype == nw.Int64
     assert df.columns == list(expected.keys())
+    assert df.collect_schema() == expected
 
 
 def test_interchange_schema_duckdb() -> None:
@@ -221,6 +222,7 @@ def test_interchange_schema_duckdb() -> None:
     assert result == expected
     assert df["a"].dtype == nw.Int64
     assert df.columns == list(expected.keys())
+    assert df.collect_schema() == expected
 
 
 def test_invalid() -> None: