Skip to content

Commit 5b0df48

Browse files
authored
feat: allow from_dict to not have native_namespace arg if all inputs are already narwhals Series (#760)
1 parent 54cd277 commit 5b0df48

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

narwhals/functions.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def from_dict(
5252
data: dict[str, Any],
5353
schema: dict[str, DType] | Schema | None = None,
5454
*,
55-
native_namespace: ModuleType,
55+
native_namespace: ModuleType | None = None,
5656
) -> DataFrame[Any]:
5757
"""
5858
Instantiate DataFrame from dictionary.
@@ -64,7 +64,8 @@ def from_dict(
6464
Arguments:
6565
data: Dictionary to create DataFrame from.
6666
schema: The DataFrame schema as Schema or dict of {name: type}.
67-
native_namespace: The native library to use for DataFrame creation.
67+
native_namespace: The native library to use for DataFrame creation. Only
68+
necessary if inputs are not Narwhals Series.
6869
6970
Examples:
7071
>>> import pandas as pd
@@ -97,6 +98,21 @@ def from_dict(
9798
│ 2 ┆ 4 │
9899
└─────┴─────┘
99100
"""
101+
from narwhals.series import Series
102+
from narwhals.translate import to_native
103+
104+
if not data:
105+
msg = "from_dict cannot be called with empty dictionary"
106+
raise ValueError(msg)
107+
if native_namespace is None:
108+
for val in data.values():
109+
if isinstance(val, Series):
110+
native_namespace = val.__native_namespace__()
111+
break
112+
else:
113+
msg = "Calling `from_dict` without `native_namespace` is only supported if all input values are already Narwhals Series"
114+
raise TypeError(msg)
115+
data = {key: to_native(value, strict=False) for key, value in data.items()}
100116
implementation = Implementation.from_native_namespace(native_namespace)
101117

102118
if implementation is Implementation.POLARS:

narwhals/stable/v1.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,7 +1473,7 @@ def from_dict(
14731473
data: dict[str, Any],
14741474
schema: dict[str, DType] | Schema | None = None,
14751475
*,
1476-
native_namespace: ModuleType,
1476+
native_namespace: ModuleType | None = None,
14771477
) -> DataFrame[Any]:
14781478
"""
14791479
Instantiate DataFrame from dictionary.
@@ -1485,7 +1485,8 @@ def from_dict(
14851485
Arguments:
14861486
data: Dictionary to create DataFrame from.
14871487
schema: The DataFrame schema as Schema or dict of {name: type}.
1488-
native_namespace: The native library to use for DataFrame creation.
1488+
native_namespace: The native library to use for DataFrame creation. Only
1489+
necessary if inputs are not Narwhals Series.
14891490
14901491
Examples:
14911492
>>> import pandas as pd

tests/from_dict_test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,27 @@ def test_from_dict_schema(constructor: Any, request: Any) -> None:
2929
schema=schema, # type: ignore[arg-type]
3030
)
3131
assert result.collect_schema() == schema
32+
33+
34+
def test_from_dict_without_namespace(constructor: Any) -> None:
35+
df = nw.from_native(constructor({"a": [1, 2, 3], "b": [4, 5, 6]})).lazy().collect()
36+
result = nw.from_dict({"c": df["a"], "d": df["b"]})
37+
compare_dicts(result, {"c": [1, 2, 3], "d": [4, 5, 6]})
38+
39+
40+
def test_from_dict_without_namespace_invalid(constructor: Any) -> None:
41+
df = nw.from_native(constructor({"a": [1, 2, 3], "b": [4, 5, 6]})).lazy().collect()
42+
with pytest.raises(TypeError, match="namespace"):
43+
nw.from_dict({"c": nw.to_native(df["a"]), "d": nw.to_native(df["b"])})
44+
45+
46+
def test_from_dict_one_native_one_narwhals(constructor: Any) -> None:
47+
df = nw.from_native(constructor({"a": [1, 2, 3], "b": [4, 5, 6]})).lazy().collect()
48+
result = nw.from_dict({"c": nw.to_native(df["a"]), "d": df["b"]})
49+
expected = {"c": [1, 2, 3], "d": [4, 5, 6]}
50+
compare_dicts(result, expected)
51+
52+
53+
def test_from_dict_empty() -> None:
54+
with pytest.raises(ValueError, match="empty"):
55+
nw.from_dict({})

0 commit comments

Comments
 (0)