Skip to content

Commit

Permalink
depr: deprecate strict in from_native / to_native in favour of …
Browse files Browse the repository at this point in the history
…`pass_through` (#1308)
  • Loading branch information
MarcoGorelli authored Nov 3, 2024
1 parent b9720b2 commit dbba76a
Show file tree
Hide file tree
Showing 9 changed files with 450 additions and 91 deletions.
23 changes: 21 additions & 2 deletions docs/backcompat.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,27 @@ before making any change.

### After `stable.v1`

- `Datetime` and `Duration` dtypes hash using both `time_unit` and `time_zone`.
The effect of this can be seen when doing `dtype in {...}` checks:
- Since Narwhals 1.13.0, the `strict` parameter in `from_native`, `to_native`, and `narwhalify`
has been deprecated in favour of `pass_through`. This is because several users expressed
confusion/surprise over what `strict=False` did.
```python
# v1 syntax:
nw.from_native(df, strict=False)

# main namespace (and, when we get there, v2) syntax:
nw.from_native(df, pass_through=True)
```
If you are using Narwhals>=1.13.0, then we recommend using `pass_through`, as that
works consistently across namespaces.

In the future:

- in the main Narwhals namespace, `strict` will be removed in favour of `pass_through`
- in `stable.v1`, we will keep both `strict` and `pass_through`

- Since Narwhals 1.9.0, `Datetime` and `Duration` dtypes hash using both `time_unit` and
`time_zone`.
The effect of this can be seen when placing these dtypes in sets:

```python exec="1" source="above" session="backcompat"
import narwhals.stable.v1 as nw_v1
Expand Down
2 changes: 1 addition & 1 deletion narwhals/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -2874,7 +2874,7 @@ def to_native(self) -> FrameT:
└─────┴─────┴─────┘
"""

return to_native(narwhals_object=self, strict=True)
return to_native(narwhals_object=self, pass_through=False)

# inherited
def pipe(self, function: Callable[[Any], Self], *args: Any, **kwargs: Any) -> Self:
Expand Down
2 changes: 1 addition & 1 deletion narwhals/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ def _from_dict_impl(
else:
msg = "Calling `from_dict` without `native_namespace` is only supported if all input values are already Narwhals Series"
raise TypeError(msg)
data = {key: to_native(value, strict=False) for key, value in data.items()}
data = {key: to_native(value, pass_through=True) for key, value in data.items()}
implementation = Implementation.from_native_namespace(native_namespace)

if implementation is Implementation.POLARS:
Expand Down
241 changes: 232 additions & 9 deletions narwhals/stable/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
from narwhals.utils import maybe_get_index
from narwhals.utils import maybe_reset_index
from narwhals.utils import maybe_set_index
from narwhals.utils import validate_strict_and_pass_though

if TYPE_CHECKING:
from types import ModuleType
Expand Down Expand Up @@ -775,12 +776,212 @@ def from_native(
"""


@overload
def from_native(
native_object: IntoDataFrameT | IntoSeriesT,
*,
pass_through: Literal[True],
eager_only: None = ...,
eager_or_interchange_only: Literal[True],
series_only: None = ...,
allow_series: Literal[True],
) -> DataFrame[IntoDataFrameT]: ...


@overload
def from_native(
native_object: IntoDataFrameT | IntoSeriesT,
*,
pass_through: Literal[True],
eager_only: Literal[True],
eager_or_interchange_only: None = ...,
series_only: None = ...,
allow_series: Literal[True],
) -> DataFrame[IntoDataFrameT] | Series: ...


@overload
def from_native(
native_object: IntoDataFrameT,
*,
pass_through: Literal[True],
eager_only: None = ...,
eager_or_interchange_only: Literal[True],
series_only: None = ...,
allow_series: None = ...,
) -> DataFrame[IntoDataFrameT]: ...


@overload
def from_native(
native_object: T,
*,
pass_through: Literal[True],
eager_only: None = ...,
eager_or_interchange_only: Literal[True],
series_only: None = ...,
allow_series: None = ...,
) -> T: ...


@overload
def from_native(
native_object: IntoDataFrameT,
*,
pass_through: Literal[True],
eager_only: Literal[True],
eager_or_interchange_only: None = ...,
series_only: None = ...,
allow_series: None = ...,
) -> DataFrame[IntoDataFrameT]: ...


@overload
def from_native(
native_object: T,
*,
pass_through: Literal[True],
eager_only: Literal[True],
eager_or_interchange_only: None = ...,
series_only: None = ...,
allow_series: None = ...,
) -> T: ...


@overload
def from_native(
native_object: IntoFrameT | IntoSeriesT,
*,
pass_through: Literal[True],
eager_only: None = ...,
eager_or_interchange_only: None = ...,
series_only: None = ...,
allow_series: Literal[True],
) -> DataFrame[IntoFrameT] | LazyFrame[IntoFrameT] | Series: ...


@overload
def from_native(
native_object: IntoSeriesT,
*,
pass_through: Literal[True],
eager_only: None = ...,
eager_or_interchange_only: None = ...,
series_only: Literal[True],
allow_series: None = ...,
) -> Series: ...


@overload
def from_native(
native_object: IntoFrameT,
*,
pass_through: Literal[True],
eager_only: None = ...,
eager_or_interchange_only: None = ...,
series_only: None = ...,
allow_series: None = ...,
) -> DataFrame[IntoFrameT] | LazyFrame[IntoFrameT]: ...


@overload
def from_native(
native_object: T,
*,
pass_through: Literal[True],
eager_only: None = ...,
eager_or_interchange_only: None = ...,
series_only: None = ...,
allow_series: None = ...,
) -> T: ...


@overload
def from_native(
native_object: IntoDataFrameT,
*,
pass_through: Literal[False] = ...,
eager_only: None = ...,
eager_or_interchange_only: Literal[True],
series_only: None = ...,
allow_series: None = ...,
) -> DataFrame[IntoDataFrameT]:
"""
from_native(df, pass_through=False, eager_or_interchange_only=True)
from_native(df, eager_or_interchange_only=True)
"""


@overload
def from_native(
native_object: IntoDataFrameT,
*,
pass_through: Literal[False] = ...,
eager_only: Literal[True],
eager_or_interchange_only: None = ...,
series_only: None = ...,
allow_series: None = ...,
) -> DataFrame[IntoDataFrameT]:
"""
from_native(df, pass_through=False, eager_only=True)
from_native(df, eager_only=True)
"""


@overload
def from_native(
native_object: IntoFrameT | IntoSeriesT,
*,
pass_through: Literal[False] = ...,
eager_only: None = ...,
eager_or_interchange_only: None = ...,
series_only: None = ...,
allow_series: Literal[True],
) -> DataFrame[Any] | LazyFrame[Any] | Series:
"""
from_native(df, pass_through=False, allow_series=True)
from_native(df, allow_series=True)
"""


@overload
def from_native(
native_object: IntoSeriesT,
*,
pass_through: Literal[False] = ...,
eager_only: None = ...,
eager_or_interchange_only: None = ...,
series_only: Literal[True],
allow_series: None = ...,
) -> Series:
"""
from_native(df, pass_through=False, series_only=True)
from_native(df, series_only=True)
"""


@overload
def from_native(
native_object: IntoFrameT,
*,
pass_through: Literal[False] = ...,
eager_only: None = ...,
eager_or_interchange_only: None = ...,
series_only: None = ...,
allow_series: None = ...,
) -> DataFrame[IntoFrameT] | LazyFrame[IntoFrameT]:
"""
from_native(df, pass_through=False)
from_native(df)
"""


# All params passed in as variables
@overload
def from_native(
native_object: Any,
*,
strict: bool,
pass_through: bool,
eager_only: bool | None,
eager_or_interchange_only: bool | None = None,
series_only: bool | None,
Expand All @@ -791,7 +992,8 @@ def from_native(
def from_native(
native_object: Any,
*,
strict: bool = True,
strict: bool | None = None,
pass_through: bool | None = None,
eager_only: bool | None = None,
eager_or_interchange_only: bool | None = None,
series_only: bool | None = None,
Expand All @@ -811,8 +1013,19 @@ def from_native(
- pandas.Series
- polars.Series
- anything with a `__narwhals_series__` method
strict: Whether to raise if object can't be converted (default) or
to just leave it as-is.
strict: Determine what happens if the object isn't supported by Narwhals:
- `True` (default): raise an error
- `False`: pass object through as-is
**Deprecated** (v1.13.0):
Please use `pass_through` instead. Note that `strict` is still available
(and won't emit a deprecation warning) if you use `narwhals.stable.v1`,
see [perfect backwards compatibility policy](https://narwhals-dev.github.io/narwhals/backcompat/).
pass_through: Determine what happens if the object isn't supported by Narwhals:
- `False` (default): raise an error
- `True`: pass object through as-is
eager_only: Whether to only allow eager objects.
eager_or_interchange_only: Whether to only allow eager objects or objects which
implement the Dataframe Interchange Protocol.
Expand All @@ -829,9 +1042,14 @@ def from_native(
return native_object
if isinstance(native_object, Series) and (series_only or allow_series):
return native_object

pass_through = validate_strict_and_pass_though(
strict, pass_through, pass_through_default=False, emit_deprecation_warning=False
)

result = _from_native_impl(
native_object,
strict=strict,
pass_through=pass_through,
eager_only=eager_only,
eager_or_interchange_only=eager_or_interchange_only,
series_only=series_only,
Expand All @@ -844,7 +1062,8 @@ def from_native(
def narwhalify(
func: Callable[..., Any] | None = None,
*,
strict: bool = False,
strict: bool | None = None,
pass_through: bool | None = None,
eager_only: bool | None = False,
eager_or_interchange_only: bool | None = False,
series_only: bool | None = False,
Expand Down Expand Up @@ -905,13 +1124,17 @@ def func(df):
allow_series: Whether to allow series (default is only dataframe / lazyframe).
"""

pass_through = validate_strict_and_pass_though(
strict, pass_through, pass_through_default=True, emit_deprecation_warning=False
)

def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
args = [
from_native(
arg,
strict=strict,
pass_through=pass_through,
eager_only=eager_only,
eager_or_interchange_only=eager_or_interchange_only,
series_only=series_only,
Expand All @@ -923,7 +1146,7 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
kwargs = {
name: from_native(
value,
strict=strict,
pass_through=pass_through,
eager_only=eager_only,
eager_or_interchange_only=eager_or_interchange_only,
series_only=series_only,
Expand All @@ -944,7 +1167,7 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:

result = func(*args, **kwargs)

return to_native(result, strict=strict)
return to_native(result, pass_through=pass_through)

return wrapper

Expand Down
Loading

0 comments on commit dbba76a

Please sign in to comment.