Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
2c36eea
feat(typing): Make `Implementation` less opaque
dangotbanned Aug 20, 2025
2350dfc
ci(typing): fix pyright coverage
dangotbanned Aug 20, 2025
fe80d52
ci: Handle descriptors in API reference
dangotbanned Aug 20, 2025
123dc2e
Merge branch 'main' into implementation-typing
dangotbanned Aug 20, 2025
54bfbe4
Merge remote-tracking branch 'upstream/main' into implementation-typing
dangotbanned Aug 21, 2025
cadcdf0
cov
dangotbanned Aug 21, 2025
5b2bc62
add typing tests, tweak overloads
dangotbanned Aug 21, 2025
14974bc
refactor(typing): Switch most overloads to `BaseFrame`
dangotbanned Aug 21, 2025
685409c
feat(typing): Get basic `LazyFrame.implementation` working
dangotbanned Aug 21, 2025
0f83e44
kinda support dask
dangotbanned Aug 21, 2025
49a10bd
ci: try include `dask` in typing?
dangotbanned Aug 21, 2025
fd2b93e
aaaaand `modin` as well
dangotbanned Aug 21, 2025
618ce8c
feat(typing): `duckdb` & `sqlframe` work!
dangotbanned Aug 21, 2025
0606a14
kinda support `ibis`
dangotbanned Aug 21, 2025
37aaa69
test(typing): Simplify Any/Into, also test lazy
dangotbanned Aug 21, 2025
141b687
test(typing): Add `DataFrame.lazy` suite
dangotbanned Aug 21, 2025
cabedd4
refactor: Prepare for `Series` support
dangotbanned Aug 22, 2025
71c5163
extend this overload abomination
dangotbanned Aug 22, 2025
2b7945b
refactor: Move `_ImplDescriptor` to `_utils`
dangotbanned Aug 22, 2025
c573cfd
feat(typing): Add (new) `Series.implementation`
dangotbanned Aug 22, 2025
410b5bd
oop
dangotbanned Aug 22, 2025
e07cbc5
test(typing): Add `Series` tests
dangotbanned Aug 22, 2025
c4bceed
test: Redo everything, check collect as well
dangotbanned Aug 22, 2025
c8dbe07
Merge branch 'main' into implementation-typing
dangotbanned Aug 23, 2025
eaa43c1
docs: Ensure `BaseFrame.implementation` shows in api ref
dangotbanned Aug 23, 2025
5ef8103
fix(typing): `ibis`, `dask` work!!!
dangotbanned Aug 23, 2025
f55cb3a
fix(typing): Unbreak `modin`
dangotbanned Aug 23, 2025
811290c
test(typing): Check `mpd.Series` too
dangotbanned Aug 23, 2025
012c2bf
typo
dangotbanned Aug 23, 2025
b0694d0
fix `mpd.Series`
dangotbanned Aug 23, 2025
2a75529
chore: Add overload for pyspark
dangotbanned Aug 23, 2025
7d42972
simplify, add notes
dangotbanned Aug 23, 2025
fd736c6
Merge branch 'main' into implementation-typing
dangotbanned Aug 23, 2025
5d2f54f
Merge branch 'main' into implementation-typing
dangotbanned Aug 24, 2025
05d4115
rename, add brief doc to `_Implementation`
dangotbanned Aug 24, 2025
87d4439
refactor: Rename `NarwhalsObj` -> `Narwhals`
dangotbanned Aug 24, 2025
bee6984
tighten up `Narwhals` w/ `Compliant`
dangotbanned Aug 24, 2025
1c68c68
docs(typing): Add `Narwhals` explainer
dangotbanned Aug 24, 2025
fcafec6
docs: Add crossref to `Implementation`
dangotbanned Aug 24, 2025
7157bbd
refactor: shrinking
dangotbanned Aug 24, 2025
08d900c
Merge branch 'main' into implementation-typing
dangotbanned Aug 24, 2025
b2aaf0d
Merge branch 'main' into implementation-typing
dangotbanned Aug 25, 2025
635b5a8
Merge branch 'main' into implementation-typing
dangotbanned Aug 25, 2025
5049a2a
docs: Explain typing test structure
dangotbanned Aug 26, 2025
4b78837
Update narwhals/_utils.py
dangotbanned Aug 26, 2025
29daf5e
Merge branch 'main' into implementation-typing
dangotbanned Aug 26, 2025
f6da9ce
Merge branch 'main' into implementation-typing
dangotbanned Aug 27, 2025
fe21d09
Merge remote-tracking branch 'upstream/main' into implementation-typing
dangotbanned Aug 27, 2025
a94a0f8
test(typing): Update for (#3032)
dangotbanned Aug 27, 2025
884d135
Merge branch 'main' into implementation-typing
dangotbanned Aug 28, 2025
4ad081c
Merge branch 'main' into implementation-typing
dangotbanned Aug 28, 2025
791ecae
ci: Exclude `OrderedDict` methods from `check-api-reference`
dangotbanned Aug 28, 2025
a3bd3ac
Merge branch 'main' into implementation-typing
dangotbanned Aug 28, 2025
043b9d1
Merge branch 'main' into implementation-typing
dangotbanned Aug 28, 2025
6b59ed9
Merge branch 'main' into implementation-typing
dangotbanned Aug 29, 2025
919b22f
Merge remote-tracking branch 'upstream/main' into implementation-typing
dangotbanned Aug 29, 2025
5e2838e
Merge branch 'main' into implementation-typing
dangotbanned Aug 30, 2025
6bc2c47
Merge branch 'main' into implementation-typing
dangotbanned Sep 2, 2025
21800fe
ci: Try adding `--group 'typing-ci'`
dangotbanned Sep 3, 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
4 changes: 2 additions & 2 deletions narwhals/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@
_ArrowImpl,
_CudfImpl,
_DaskImpl,
_DataFrameLazyImpl,
_DuckDBImpl,
_EagerAllowedImpl,
_IbisImpl,
Expand Down Expand Up @@ -2139,8 +2138,9 @@ def __get__(self, instance: Narwhals[_NativeIbis], owner: Any) -> _IbisImpl: ...
def __get__(
self, instance: Narwhals[_NativePySpark | _NativePySparkConnect], owner: Any
) -> _PySparkImpl | _PySparkConnectImpl: ...
# NOTE: https://docs.python.org/3/howto/descriptor.html#invocation-from-a-class
@overload
def __get__(self, instance: None, owner: Any) -> Self: ...
def __get__(self, instance: None, owner: type[Narwhals[Any]]) -> Self: ...
Copy link
Member

Choose a reason for hiding this comment

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

πŸ‘ŒπŸΌ

@overload
def __get__(
self, instance: DataFrame[Any] | Series[Any], owner: Any
Expand Down
1 change: 0 additions & 1 deletion narwhals/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
Implementation,
Version,
_Implementation,
can_dataframe_lazy,
can_lazyframe_collect,
check_columns_exist,
flatten,
Expand Down
42 changes: 40 additions & 2 deletions tests/implementation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,41 @@ def test_implementation_new(member: str, value: str) -> None:
assert nw.Implementation(value) is getattr(nw.Implementation, member)


_TYPING_ONLY_TESTS = "_"
"""Exhaustive checks for overload matching native -> implementation.

## Arrange
Each test defines a function accepting a `native` frame.

Important:
We *must* use the concrete types and therefore the type checker *needs* the package installed.

Next, wrap `native` as a `nw.{Data,Lazy}Frame`.

Note:
If we support multiple native types, use `native` to generate `nw.{LazyFrame,Series}` as well.

Finally, look-up `.implementation` on all wrapped objects.

## Act
Try passing every result (`*_impl`) to functions that *only* accept a **subset** of `Implementation`.

This step *may require* a `# (type|pyright): ignore` directive, which defines the `# [... Negative]` result.
Otherwise, results are labelled with `# [... Positive]`.

If this *static* label matches *runtime* we use `# [True ...]`, otherwise `# [False ...]`.

Tip:
`# [False Negative]`s are the most frustrating for users.
Always try to minimize warning on safe code.

## Assert
The action determined whether or not our typing warns on an `@overload` match.

We still need to use [`assert_type`] to verify which `Implementation`(s) were returned as a result.

[`assert_type`]: https://typing-extensions.readthedocs.io/en/latest/#typing_extensions.assert_type
"""
if TYPE_CHECKING:
import dask.dataframe as dd
import duckdb
Expand Down Expand Up @@ -179,8 +214,9 @@ def test_sqlframe_typing(native: BaseDataFrame[Any, Any, Any, Any, Any]) -> None

ldf_impl = ldf.implementation

# [True Positive]
any_df.lazy(ldf_impl)
# [True Negative]
any_df.lazy(ldf_impl) # pyright: ignore[reportArgumentType]
any_ldf.collect(ldf_impl) # pyright: ignore[reportArgumentType]

assert_type(ldf.implementation, _SQLFrameImpl)
Expand Down Expand Up @@ -254,7 +290,9 @@ def test_bound_typing() -> None:

# [True Negative]
any_df.lazy(df_impl) # type: ignore[arg-type]
any_df.lazy(ldf_impl) # type: ignore[arg-type]
# [True Positive]
any_df.lazy(ldf_impl)
# [True Negative]
any_df.lazy(ser_impl) # type: ignore[arg-type]
any_ldf.collect(df_impl) # type: ignore[arg-type]
any_ldf.collect(ldf_impl) # type: ignore[arg-type]
Expand Down
8 changes: 6 additions & 2 deletions utils/check_api_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sys

# ruff: noqa: N806
from collections import deque
from collections import OrderedDict, deque
from inspect import isfunction, ismethoddescriptor
from pathlib import Path
from types import MethodType, ModuleType
Expand Down Expand Up @@ -223,7 +223,11 @@ def read_documented_members(source: str | Path) -> list[str]:
# Schema
schema_methods = list(iter_api_reference_names(nw.Schema))
documented = read_documented_members(DIR_API_REF / "schema.md")
if missing := set(schema_methods).difference(documented):
if (
missing := set(schema_methods)
.difference(documented)
.difference(iter_api_reference_names(OrderedDict))
):
print("Schema: not documented") # noqa: T201
print(missing) # noqa: T201
ret = 1
Expand Down
Loading