Skip to content
Open
Show file tree
Hide file tree
Changes from 71 commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
6d36b4e
attempted outline of how things could look
ym-pett Aug 13, 2025
1062d99
attempting to read in plugins
ym-pett Aug 13, 2025
8d4cda6
linting
ym-pett Aug 13, 2025
cfd156f
trying to see an effect of plugin
ym-pett Aug 13, 2025
e3a2f3b
cleanup after pair session
ym-pett Aug 14, 2025
f65d7bf
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 14, 2025
a133796
Merge branch 'narwhals-dev:main' into create_fromnative_daft
ym-pett Aug 14, 2025
eee9068
think we're managing to import the class
ym-pett Aug 14, 2025
c3a8c4d
changes mean we can read daft df, joy
ym-pett Aug 14, 2025
ca01346
Merge branch 'main' into create_fromnative_daft
ym-pett Aug 15, 2025
c4611fe
nicer error handling, not there yet
ym-pett Aug 16, 2025
90ad973
nicer error handling, not there yet
ym-pett Aug 16, 2025
76232df
added some thoughts, currently going in circles
ym-pett Aug 16, 2025
ebc1a8f
added some thoughts, currently going in circles
ym-pett Aug 16, 2025
15d9c8c
work from pair session
ym-pett Aug 18, 2025
16832f9
more explicit error handling
ym-pett Aug 18, 2025
9e11a8f
error handling now passes ruff check
ym-pett Aug 18, 2025
d8663d8
moved plugins back to end, raising general Exception
ym-pett Aug 19, 2025
34e7fb6
silencing ruff errors, deleted t.py
ym-pett Aug 19, 2025
8a2b019
Merge branch 'main' into create_fromnative_daft
ym-pett Aug 19, 2025
f92cdbf
Merge branch 'main' into create_fromnative_daft
ym-pett Aug 19, 2025
54509d2
Merge branch 'main' into create_fromnative_daft
ym-pett Aug 19, 2025
941edc4
Merge branch 'main' into create_fromnative_daft
ym-pett Aug 19, 2025
72a5df4
checking for version and preventing tests on plugin codeblock
ym-pett Aug 19, 2025
e783af7
wip implementing marco's proposal
ym-pett Aug 20, 2025
4cf2f96
new version passes tests
ym-pett Aug 20, 2025
a52a6bb
discover_plugins function and fixed pragma no cover
ym-pett Aug 20, 2025
a4e539a
added pragma, removed group argument
ym-pett Aug 22, 2025
aca8cc4
Merge branch 'main' into create_fromnative_daft
ym-pett Aug 22, 2025
02d544a
wip: add test-plugin
MarcoGorelli Aug 22, 2025
0952cd9
wip
MarcoGorelli Aug 22, 2025
de0e5ce
wip
MarcoGorelli Aug 22, 2025
9dfcd09
fixup
MarcoGorelli Aug 22, 2025
b999e9a
remove unused function, fixup type ignore
MarcoGorelli Aug 22, 2025
3460f4e
install test-plugin in CI
MarcoGorelli Aug 22, 2025
1c246dc
pass Version down
MarcoGorelli Aug 22, 2025
ab8303d
fixup, remove more defaults
MarcoGorelli Aug 22, 2025
a8501f5
coverage
MarcoGorelli Aug 22, 2025
19dc900
actually stage test file
MarcoGorelli Aug 22, 2025
42f2df8
remove daft traces
MarcoGorelli Aug 22, 2025
d6a384a
rename
MarcoGorelli Aug 22, 2025
9cf290d
install test_plugin in makefile
MarcoGorelli Aug 22, 2025
bdd71e7
coverage
MarcoGorelli Aug 22, 2025
bde288f
fix typing (for real this time)
MarcoGorelli Aug 22, 2025
7035533
aah ruff check was removing the not-really-unused import
MarcoGorelli Aug 22, 2025
4868aab
lru_cache -> cache
MarcoGorelli Aug 22, 2025
afd8620
Merge branch 'main' into create_fromnative_daft
ym-pett Sep 1, 2025
b09d3ad
merged main into branch to resolve conflicts
ym-pett Sep 4, 2025
1a73ab8
feat(suggestion): `<3.10` support?
dangotbanned Sep 4, 2025
1481d48
fix: don't expect plugins?
dangotbanned Sep 4, 2025
59589c1
chore(typing): Ignore unimplemented
dangotbanned Sep 4, 2025
6888f78
Merge branch 'main' into create_fromnative_daft
ym-pett Sep 8, 2025
8f22fdb
wip: hybrid approach to importing
ym-pett Sep 8, 2025
b3f2b60
wip: checkpoint
ym-pett Sep 8, 2025
422ec70
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 8, 2025
b0b524b
damn I broke the plugin tests
ym-pett Sep 8, 2025
0960d69
wip: fixed local mess in test_plugin
ym-pett Sep 8, 2025
3207de1
wip: fixed local mess in test_plugin
ym-pett Sep 8, 2025
d951220
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 8, 2025
ea28de2
Merge branch 'main' into create_fromnative_daft
ym-pett Sep 9, 2025
b563ace
added not_implemented functions to namespace
ym-pett Sep 9, 2025
9847793
removed comment
ym-pett Sep 9, 2025
1f38e31
changed naming for plugin entrypoins
ym-pett Sep 9, 2025
5dee0bb
added not_implemented functions to dictnamespace
ym-pett Sep 9, 2025
c508ab0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 9, 2025
f2d6e57
Merge branch 'main' into create_fromnative_daft
ym-pett Sep 9, 2025
6c65fd4
fix(typing): Add `py.typed` marker
dangotbanned Sep 9, 2025
b26d7d4
fix: Fully qualify imports (from `tests`)
dangotbanned Sep 9, 2025
22362ad
added protocol & plugin detection function
ym-pett Sep 11, 2025
d22f046
changed plugin contract
ym-pett Sep 11, 2025
028e473
Merge branch 'main' into create_fromnative_daft
ym-pett Sep 11, 2025
87e67bf
Merge branch 'main' into create_fromnative_daft
ym-pett Sep 12, 2025
5c16c7f
added is_native back in, adapted plugin tests
ym-pett Sep 12, 2025
e00c7c6
Merge branch 'main' into create_fromnative_daft
ym-pett Sep 15, 2025
0c69762
wip: moving plugins
ym-pett Sep 15, 2025
f7765e8
wip: fixed imports in plugins utils
ym-pett Sep 15, 2025
e3e5ec7
refactored plugin-related utils into their own file
ym-pett Sep 15, 2025
0cd5f2f
refactor: Make `plugins` a module instead of a package
dangotbanned Sep 16, 2025
d511fc0
chore(typing): ignore `@cache` warning
dangotbanned Sep 16, 2025
6dd4d1c
refactor: Expose as `plugins.from_native`
dangotbanned Sep 16, 2025
8a96d84
fix(typing): Add the `Plugin` annotation I forgot
dangotbanned Sep 16, 2025
1944e47
docs(DRAFT): Start working on `plugins.from_native`
dangotbanned Sep 16, 2025
ce712e0
feat(typing): Add some slightly narrower typing
dangotbanned Sep 16, 2025
7bcec61
docs: Update `plugins.from_native`
dangotbanned Sep 16, 2025
9bbc0a6
Merge pull request #2 from narwhals-dev/plugin/dgb
ym-pett Sep 25, 2025
0f65066
Merge pull request #1 from ym-pett/tidy_plugin_utils
ym-pett Sep 25, 2025
8ad25ea
Merge branch 'main' into create_fromnative_daft
ym-pett Sep 25, 2025
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 .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ jobs:
cache-dependency-glob: "pyproject.toml"
- name: install-reqs
run: uv pip install -e ".[dask, modin, ibis]" --group core-tests --group extra --system
- name: install-test-plugin
run: uv pip install -e tests/test_plugin --system
- name: show-deps
run: uv pip freeze
- name: Run pytest
Expand Down
5 changes: 4 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ repos:
rev: v6.0.0
hooks:
- id: name-tests-test
exclude: ^tests/utils\.py
exclude: |
(?x)
^(tests/utils\.py)
|^(tests/test_plugin/)
- id: no-commit-to-branch
- id: end-of-file-fixer
exclude: .svg$
40 changes: 39 additions & 1 deletion narwhals/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os
import re
import sys
from collections.abc import Collection, Container, Iterable, Iterator, Mapping, Sequence
from datetime import timezone
from enum import Enum, auto
Expand Down Expand Up @@ -50,6 +51,7 @@

if TYPE_CHECKING:
from collections.abc import Set # noqa: PYI025
from importlib.metadata import EntryPoints
from types import ModuleType

import pandas as pd
Expand All @@ -72,7 +74,12 @@
CompliantSeriesT,
NativeSeriesT_co,
)
from narwhals._compliant.typing import EvalNames, NativeDataFrameT, NativeLazyFrameT
from narwhals._compliant.typing import (
CompliantNamespaceAny,
EvalNames,
NativeDataFrameT,
NativeLazyFrameT,
)
from narwhals._namespace import (
Namespace,
_NativeArrow,
Expand Down Expand Up @@ -2062,6 +2069,37 @@ def deep_getattr(obj: Any, name_1: str, *nested: str) -> Any:
return deep_attrgetter(name_1, *nested)(obj)


@cache
def discover_entrypoints() -> EntryPoints:
import sys
from importlib.metadata import entry_points as eps

group = "narwhals.plugins"
if sys.version_info < (3, 10):
return cast("EntryPoints", eps().get(group, ()))
return eps(group=group)


# @mp: should the protocol be defined in namespace?
Copy link
Contributor Author

@ym-pett ym-pett Sep 11, 2025

Choose a reason for hiding this comment

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

@dangotbanned, pushed this as it's working, but still needs some cleaning up (e.g I've left your naming so you can see which solution we've implemented. I'm guessing the Plugin Protocol shouldn't be in here, I'll look back on your PR on this topic I reviewed and have a think about where it should go instead.

btw, are you happy with us using this one, or should we use the one you say is even simpler? (I worked with the one I could understand better, but that shouldn't be a barrier ;))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, re-read your #3086, until it can live in your _native, should I move it into _namespace? (forgot _ above)

Copy link
Member

Choose a reason for hiding this comment

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

I'm guessing the Plugin Protocol shouldn't be in here, I'll look back on your PR on this topic I reviewed and have a think about where it should go instead.

Ok, re-read your #3086, until it can live in your _native, should I move it into _namespace? (forgot _ above)

#3086 (comment)

Have re-read this & thinking that the plugin Protocol that's currently in draft version in #2978 should also go into the lower parts of this file when it's merged? ('it' = narwhals/_native.py)

Background

In narwhals, we've got three main conceptual layers:

Skip me if you don't need a refresher πŸ˜‰

(1) Narwhals

In the top-level modules, we define things like:

  • nw.DataFrame
  • nw.LazyFrame
  • nw.Series

They're directly user-facing

(2) Compliant

Under the nw._compliant sub-package, we define things like:

  • CompliantDataFrame
  • CompliantLazyFrame
  • CompliantSeries

They're user-adjacent, some appear in nw.typing and annotations but not to the same degree as Narwhals.

(3) Native

Following #3035, our typing for this level will be in nw._native.py:

  • NativeDataFrame
  • NativeLazyFrame
  • NativeSeries

But we are mostly talking about concrete external types like:

  • pandas.DataFrame
  • polars.LazyFrame
  • pyarrow.ChunkedArray

What's that got to do with plugins?

If somebody wants to extend narwhals; they're going to be building a new set of Compliant classes.
We can use them from Narwhals to talk to this new Native stuff.

Soooooo, I would say that a plugin is most closely related to the Compliant-level.

Suggestion

Rather than adding to nw._native, I think a better fit would be either:

  • Adding a new module
    • nw._compliant.plugins.py
    • or just nw.plugins.py
  • Keeping it in nw._utils.py for now
    • until we eventually split that module up better πŸ˜„

class Plugin2(Protocol):
NATIVE_PACKAGE: LiteralString

def __narwhals_namespace__(self, version: Version) -> CompliantNamespaceAny: ...


@cache
def _might_be(cls: type, type_: str) -> bool:
try:
return any(type_ in o.__module__.split(".") for o in cls.mro())
except TypeError:
return False


def _is_native_plugin(native_object: Any, plugin: Plugin2) -> bool:
pkg = plugin.NATIVE_PACKAGE
return sys.modules.get(pkg) is not None and _might_be(type(native_object), pkg)


class Compliant(
_StoresNative[NativeT_co], _StoresImplementation, Protocol[NativeT_co]
): ...
Expand Down
1 change: 1 addition & 0 deletions narwhals/stable/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ def from_native( # noqa: D417
eager_only=eager_only,
series_only=series_only,
allow_series=allow_series,
eager_or_interchange_only=False,
version=Version.V2,
)

Expand Down
126 changes: 86 additions & 40 deletions narwhals/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@
is_native_polars,
is_native_spark_like,
)
from narwhals._utils import Implementation, Version, has_native_namespace
from narwhals._utils import (
Implementation,
Version,
_is_native_plugin,
discover_entrypoints,
has_native_namespace,
is_compliant_dataframe,
is_compliant_lazyframe,
is_compliant_series,
)
from narwhals.dependencies import (
get_dask_expr,
get_numpy,
Expand Down Expand Up @@ -314,23 +323,65 @@ def from_native( # noqa: D417
)


def _translate_if_compliant( # noqa: C901,PLR0911
compliant_object: Any,
*,
pass_through: bool = False,
eager_only: bool = False,
# Interchange-level was removed after v1
eager_or_interchange_only: bool,
series_only: bool,
allow_series: bool | None,
version: Version,
) -> Any:
if is_compliant_dataframe(compliant_object):
if series_only:
if not pass_through:
msg = "Cannot only use `series_only` with dataframe"
raise TypeError(msg)
return compliant_object
return version.dataframe(
compliant_object.__narwhals_dataframe__()._with_version(version), level="full"
)
if is_compliant_lazyframe(compliant_object):
if series_only:
if not pass_through:
msg = "Cannot only use `series_only` with lazyframe"
raise TypeError(msg)
return compliant_object
if eager_only or eager_or_interchange_only:
if not pass_through:
msg = "Cannot only use `eager_only` or `eager_or_interchange_only` with lazyframe"
raise TypeError(msg)
return compliant_object
return version.lazyframe(
compliant_object.__narwhals_lazyframe__()._with_version(version), level="full"
)
if is_compliant_series(compliant_object):
if not allow_series:
if not pass_through:
msg = "Please set `allow_series=True` or `series_only=True`"
raise TypeError(msg)
return compliant_object
return version.series(
compliant_object.__narwhals_series__()._with_version(version), level="full"
)
# Object wasn't compliant, can't translate here.
return None


def _from_native_impl( # noqa: C901, PLR0911, PLR0912, PLR0915
native_object: Any,
*,
pass_through: bool = False,
eager_only: bool = False,
# Interchange-level was removed after v1
eager_or_interchange_only: bool = False,
series_only: bool = False,
allow_series: bool | None = None,
eager_or_interchange_only: bool,
series_only: bool,
allow_series: bool | None,
version: Version,
) -> Any:
from narwhals._interchange.dataframe import supports_dataframe_interchange
from narwhals._utils import (
is_compliant_dataframe,
is_compliant_lazyframe,
is_compliant_series,
)
from narwhals.dataframe import DataFrame, LazyFrame
from narwhals.series import Series

Expand All @@ -350,38 +401,18 @@ def _from_native_impl( # noqa: C901, PLR0911, PLR0912, PLR0915
raise ValueError(msg)

# Extensions
if is_compliant_dataframe(native_object):
if series_only:
if not pass_through:
msg = "Cannot only use `series_only` with dataframe"
raise TypeError(msg)
return native_object
return version.dataframe(
native_object.__narwhals_dataframe__()._with_version(version), level="full"
)
if is_compliant_lazyframe(native_object):
if series_only:
if not pass_through:
msg = "Cannot only use `series_only` with lazyframe"
raise TypeError(msg)
return native_object
if eager_only or eager_or_interchange_only:
if not pass_through:
msg = "Cannot only use `eager_only` or `eager_or_interchange_only` with lazyframe"
raise TypeError(msg)
return native_object
return version.lazyframe(
native_object.__narwhals_lazyframe__()._with_version(version), level="full"
)
if is_compliant_series(native_object):
if not allow_series:
if not pass_through:
msg = "Please set `allow_series=True` or `series_only=True`"
raise TypeError(msg)
return native_object
return version.series(
native_object.__narwhals_series__()._with_version(version), level="full"
if (
translated := _translate_if_compliant(
native_object,
pass_through=pass_through,
eager_only=eager_only,
eager_or_interchange_only=eager_or_interchange_only,
series_only=series_only,
allow_series=allow_series,
version=version,
)
) is not None:
return translated

# Polars
if is_native_polars(native_object):
Expand Down Expand Up @@ -534,6 +565,21 @@ def _from_native_impl( # noqa: C901, PLR0911, PLR0912, PLR0915
raise TypeError(msg)
return Version.V1.dataframe(InterchangeFrame(native_object), level="interchange")

for entry_point in discover_entrypoints():
plugin = entry_point.load()
if _is_native_plugin(native_object, plugin):
compliant_namespace = plugin.__narwhals_namespace__(version=version)
compliant_object = compliant_namespace.from_native(native_object)
Copy link
Member

Choose a reason for hiding this comment

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

the issue is that here, if we have narwhals-daft (for daft dataframes) and test-plugin (for builtins.dict), then, narwhals will actually try using the test-plugin for a daft.dataframe, and it won't raise at these steps (from_native doesn't raise), as from_native typically doesn't do validation

So, steps would be:

  1. add is_native to the Plugin protocol
  2. make sure that narwhals-daft and test-plugin both have is_native functions at the top-level
  3. change _is_native_plugin to do:
def _is_native_plugin(native_object: Any, plugin: Plugin2) -> bool:
    pkg = plugin.NATIVE_PACKAGE
    return sys.modules.get(pkg) is not None and _might_be(type(native_object), pkg) and pkg.is_native(native_object)

Then, I think we should be good to go

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've pushed those changes and the corresponding change in narwhals-daft is in a PR here : MarcoGorelli/narwhals-daft#15

oh dear, just seen test failures, will investigate (🀦 I only ran tests for the plugin locally..)

Copy link
Member

Choose a reason for hiding this comment

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

altair and shiny should be unrelated

the coverage one complains about missing coverage - we only check coverage with python3.11, so the check for sys.version < 3,10 should get a # pragma: no cover on the line of the if-statement

the typing failure shows

narwhals/_utils.py:2104: error: Argument 1 to "__call__" of
"_lru_cache_wrapper" has incompatible type "type[Any]"; expected "Hashable" 
[arg-type]
            and _might_be(type(native_object), pkg)

, as @dangotbanned suggested the function let's see if he has a preferred solution

Copy link
Member

@dangotbanned dangotbanned Sep 13, 2025

Choose a reason for hiding this comment

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

Thanks both for the ping and trying those ideas out πŸ™‚

btw, are you happy with us using this one, or should we use the one you say is even simpler?

Since the first one in (#2978 (comment)) didn't pan out - I'll add a PR today with the changes to the *Namespace protocols to make the other one easier

The nice thing about it is that if an extension inherits from LazyNamespace or EagerNamespace - then they won't need to implement any of this in their plugin πŸ˜„

Update

Most of the new code is just tests πŸ₯³

Copy link
Contributor Author

Choose a reason for hiding this comment

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

great, thanks for that @dangotbanned ! Do I understand correctly that your changes in #3130 will fix the typing error once your PR has been merged? So I don't need to add a pragma: no cover for the time being (referring to #2978 (comment))

Copy link
Member

Choose a reason for hiding this comment

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

Yeah the changes I've made will remove the need for that code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks!

return _translate_if_compliant(
compliant_object,
pass_through=pass_through,
eager_only=eager_only,
eager_or_interchange_only=eager_or_interchange_only,
series_only=series_only,
allow_series=allow_series,
version=version,
)

if not pass_through:
msg = f"Expected pandas-like dataframe, Polars dataframe, or Polars lazyframe, got: {type(native_object)}"
raise TypeError(msg)
Expand Down
16 changes: 16 additions & 0 deletions tests/plugins_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from __future__ import annotations

import sys

import pytest

import narwhals as nw


@pytest.mark.skipif(sys.version_info < (3, 10), reason="3.10+ required for entrypoints")
def test_plugin() -> None:
Comment on lines +10 to +11
Copy link
Member

@dangotbanned dangotbanned Sep 9, 2025

Choose a reason for hiding this comment

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

This skip might be the reason behind the KeyError here (#2978 (comment))

Either way, we should be able to test < (3, 10) now - so removing it would be the first step

Copy link
Contributor Author

Choose a reason for hiding this comment

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

did you still want this removing or is this obsolete?

pytest.importorskip("test_plugin")
df_native = {"a": [1, 1, 2], "b": [4, 5, 6]}
lf = nw.from_native(df_native) # type: ignore[call-overload]
assert isinstance(lf, nw.LazyFrame)
assert lf.columns == ["a", "b"]
Empty file added tests/test_plugin/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions tests/test_plugin/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "test_plugin"
version = "0.1.0"

[project.entry-points.'narwhals.plugins']
test-plugin = 'test_plugin'
17 changes: 17 additions & 0 deletions tests/test_plugin/test_plugin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from narwhals.utils import Version
from tests.test_plugin.test_plugin.dataframe import DictFrame as DictFrame
from tests.test_plugin.test_plugin.namespace import DictNamespace


def __narwhals_namespace__(version: Version) -> DictNamespace: # noqa: N807
from tests.test_plugin.test_plugin.namespace import DictNamespace

return DictNamespace(version=version)


NATIVE_PACKAGE = "builtins"
77 changes: 77 additions & 0 deletions tests/test_plugin/test_plugin/dataframe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any, TypeAlias
Copy link
Member

Choose a reason for hiding this comment

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

@MarcoGorelli I think ruff isn't configured correctly πŸ€”

TypeAlias wasn't added until 3.10


from narwhals._utils import (
Implementation,
ValidateBackendVersion,
Version,
not_implemented,
)
from narwhals.typing import CompliantLazyFrame

DictFrame: TypeAlias = dict[str, list[Any]]

if TYPE_CHECKING:
from typing_extensions import Self

from narwhals import LazyFrame # noqa: F401


class DictLazyFrame(
CompliantLazyFrame[Any, "DictFrame", "LazyFrame[DictFrame]"], # type: ignore[type-var]
ValidateBackendVersion,
):
_implementation = Implementation.UNKNOWN

def __init__(self, native_dataframe: DictFrame, *, version: Version) -> None:
self._native_frame: DictFrame = native_dataframe
self._version = version

def __narwhals_lazyframe__(self) -> Self:
return self

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

@property
def columns(self) -> list[str]:
return list(self._native_frame.keys())

# Dunders
__narwhals_namespace__ = not_implemented()
__native_namespace__ = not_implemented()

# Properties
schema = not_implemented() # type: ignore[assignment]

# Static
_is_native = not_implemented() # type: ignore[assignment]

# Helpers
_iter_columns = not_implemented()

# Functions
aggregate = not_implemented()
collect = not_implemented()
collect_schema = not_implemented()
drop = not_implemented()
drop_nulls = not_implemented()
explode = not_implemented()
filter = not_implemented()
from_native = not_implemented()
group_by = not_implemented()
head = not_implemented()
join = not_implemented()
join_asof = not_implemented()
rename = not_implemented()
select = not_implemented()
simple_select = not_implemented()
sink_parquet = not_implemented()
sort = not_implemented()
tail = not_implemented()
to_narwhals = not_implemented()
unique = not_implemented()
unpivot = not_implemented()
with_columns = not_implemented()
with_row_index = not_implemented()
Loading
Loading