Skip to content

Commit 98cc8bd

Browse files
committed
chore: Add _utils.iterable_to_sequence
Preferring to use thrid-party conversions whenever possible
1 parent ce8e591 commit 98cc8bd

File tree

1 file changed

+57
-0
lines changed

1 file changed

+57
-0
lines changed

narwhals/_utils.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
is_numpy_array_1d_int,
4848
is_pandas_like_dataframe,
4949
is_pandas_like_series,
50+
is_polars_series,
5051
)
5152
from narwhals.exceptions import ColumnNotFoundError, DuplicateError, InvalidOperationError
5253

@@ -124,6 +125,7 @@
124125
CompliantLazyFrame,
125126
CompliantSeries,
126127
DTypes,
128+
EagerAllowed,
127129
FileSource,
128130
IntoSeriesT,
129131
MultiIndexSelector,
@@ -2120,3 +2122,58 @@ def extend_bool(
21202122
Stolen from https://github.com/pola-rs/polars/blob/b8bfb07a4a37a8d449d6d1841e345817431142df/py-polars/polars/_utils/various.py#L580-L594
21212123
"""
21222124
return (value,) * n_match if isinstance(value, bool) else tuple(value)
2125+
2126+
2127+
class _CanTo_List(Protocol): # noqa: N801
2128+
def to_list(self, *args: Any, **kwds: Any) -> list[Any]: ...
2129+
2130+
2131+
class _CanToList(Protocol):
2132+
def tolist(self, *args: Any, **kwds: Any) -> list[Any]: ...
2133+
2134+
2135+
class _CanTo_PyList(Protocol): # noqa: N801
2136+
def to_pylist(self, *args: Any, **kwds: Any) -> list[Any]: ...
2137+
2138+
2139+
def can_to_list(obj: Any) -> TypeIs[_CanTo_List]:
2140+
return (
2141+
is_narwhals_series(obj)
2142+
or is_polars_series(obj)
2143+
or _hasattr_static(obj, "to_list")
2144+
)
2145+
2146+
2147+
def can_tolist(obj: Any) -> TypeIs[_CanToList]:
2148+
return is_numpy_array_1d(obj) or _hasattr_static(obj, "tolist")
2149+
2150+
2151+
def can_to_pylist(obj: Any) -> TypeIs[_CanTo_PyList]:
2152+
return (
2153+
(pa := get_pyarrow()) and isinstance(obj, (pa.Array, pa.ChunkedArray))
2154+
) or _hasattr_static(obj, "to_pylist")
2155+
2156+
2157+
# TODO @dangotbanned: Use in `{Expr,Series}.is_in`
2158+
# TODO @dangotbanned: Add (brief) doc
2159+
def iterable_to_sequence(
2160+
iterable: Iterable[Any], /, *, backend: EagerAllowed | None = None
2161+
) -> Sequence[Any]:
2162+
result: Sequence[Any]
2163+
if backend is not None:
2164+
from narwhals.series import Series
2165+
2166+
result = Series.from_iterable("", iterable, backend=backend).to_list()
2167+
elif isinstance(iterable, (tuple, list)):
2168+
result = iterable
2169+
elif isinstance(iterable, (Iterator, Sequence)):
2170+
result = tuple(iterable)
2171+
elif can_to_list(iterable):
2172+
result = iterable.to_list()
2173+
elif can_tolist(iterable):
2174+
result = iterable.tolist()
2175+
elif can_to_pylist(iterable):
2176+
result = iterable.to_pylist()
2177+
else:
2178+
result = tuple(iterable)
2179+
return result

0 commit comments

Comments
 (0)