fix(typing): Narrow TypeVar used in Series#2347
Conversation
Still need to fix `from_native`
tests/translate/from_native_test.py
Outdated
| # TODO @dangotbanned: Fix this one | ||
| # Current: | ||
| # `unstable_nw.Series[unstable_nw.Series[pl.Series]]`` | ||
| # Goal: | ||
| # `unstable_nw.Series[pl.Series]` | ||
| assert_type(nw_series_passthrough, unstable_nw.Series[pl.Series]) |
There was a problem hiding this comment.
NOTE
What we need is for from_native to return the same type on these branches
narwhals/narwhals/translate.py
Lines 387 to 391 in d2a9f42
Right now, this introduces a level of nesting that gets silly quickly
Type of "nw_series_nest_2" is "narwhals.series.Series[narwhals.series.Series[narwhals.series.Series[polars.series.series.Series]]]"
There was a problem hiding this comment.
Ignore mypy (https://github.com/narwhals-dev/narwhals/actions/runs/14271920405/job/40006873171?pr=2347)
I'm working on resolving this one
There was a problem hiding this comment.
(6bd47b4) is a very heavy-handed fix.
My thinking is
- Passing a
narwhalsobject tofrom_nativereally should never have been supported- None of these are native types:
nw.Series | nw.DataFrame | nw.LazyFrame
- None of these are native types:
- Removing that support would likely break someones workflow
- Ignoring all flags requires only adding 1 new
@overloadper-class- None of those need to deal with recursion
There was a problem hiding this comment.
Was expecting the v1 backport (253da29) would surface some downstream issues - but all seems fine 🤔
narwhals/typing.py
Outdated
| class NativeLazyFrame(NativeFrame, Protocol): | ||
| def explain(self, *args: Any, **kwargs: Any) -> Any: ... | ||
|
|
||
| # TODO @dangotbanned: `nw.Series` **cannot** be allowed to match this!!! |
There was a problem hiding this comment.
NativeSeries is now what is used for IntoSeries.
An issue - that's new to me - is that Series also matches NativeSeries 🤦♂️
So for the original alias:
IntoSeries: TypeAlias = Union["Series[Any]", "NativeSeries"]nw.Series can be a match in 3 positions
IntoSeries: TypeAlias = Union["Series[Any]", "NativeSeries"]
# ^--1--^ ^2^ ^-----3-----^There was a problem hiding this comment.
@dangotbanned literally an hour ago (#2347)
Should be the easiest to fix in (#2239)
🤦♂️🤦♂️🤦♂️🤦♂️🤦♂️
| @overload | ||
| def from_native(native_object: SeriesT, **kwds: Any) -> SeriesT: ... |
There was a problem hiding this comment.
But why does this work?
By having an unconditional @overload when a nw.Series is passed - it ensures no other @overload(s) are checked.
If we were to spell out all the valid cases, they would overlap with the NativeSeries cases (see #2347 (comment))
narwhals.from_native(native_series, allow_series=True)
narwhals.from_native(native_series, series_only=True)Needing to have **kwds is unfortunate - but
practicality beats purity
I'm open to any other solutions that can pass the new tests 🙂
There was a problem hiding this comment.
shall we raise at runtime if kwds?
There was a problem hiding this comment.
shall we raise at runtime
if kwds?
Yeah I was thinking about that as well!
I wasn't sure
- what the message should be?
- could that logic be shared for both
v1andmain?
MarcoGorelli
left a comment
There was a problem hiding this comment.
thanks @dangotbanned ! just one comment
| @overload | ||
| def from_native(native_object: SeriesT, **kwds: Any) -> SeriesT: ... |
There was a problem hiding this comment.
shall we raise at runtime if kwds?
| if kwds: | ||
| msg = f"from_native() got an unexpected keyword argument {next(iter(kwds))!r}" | ||
| raise TypeError(msg) |
There was a problem hiding this comment.
Important
This matches the runtime behavior of python.
import polars as pl
import narwhals as nw
df = pl.DataFrame({"a": ["A", "B", "A"]})
nw_df = nw.from_native(df)
nw.to_native(nw_df, keyword_1="a", keyword_2="b")Only the first bad argument is ever included:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[5], line 39
37 df = pl.DataFrame({"a": ["A", "B", "A"]})
38 nw_df = nw.from_native(df)
---> 39 nw.to_native(nw_df, keyword_1="a", keyword_2="b")
TypeError: to_native() got an unexpected keyword argument 'keyword_1'

Will close #2343
What type of PR is this? (check all applicable)
Related issues
TypeVarused inSeries#2343Checklist
If you have comments or can explain your changes, please do so below
IntoSeriesgets us most of the way there