Skip to content

Now unpacking kwargs does not include args with default values #11589

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 11 additions & 6 deletions mypy/argmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,17 @@ def map_actuals_to_formals(actual_kinds: List[nodes.ArgKind],
#
# TODO: If there are also tuple varargs, we might be missing some potential
# matches if the tuple was short enough to not match everything.
unmatched_formals = [fi for fi in range(nformals)
if (formal_names[fi]
and (not formal_to_actual[fi]
or actual_kinds[formal_to_actual[fi][0]] == nodes.ARG_STAR)
and formal_kinds[fi] != nodes.ARG_STAR)
or formal_kinds[fi] == nodes.ARG_STAR2]
unmatched_formals = []
for fi in range(nformals):
if (formal_names[fi]
and formal_kinds[fi] != nodes.ARG_STAR
and (not formal_to_actual[fi]
or actual_kinds[formal_to_actual[fi][0]] == nodes.ARG_STAR)
# Arguments with default values should not be matched:
and not formal_kinds[fi].is_optional()
or formal_kinds[fi] == nodes.ARG_STAR2):
unmatched_formals.append(fi)

for ai in ambiguous_actual_kwargs:
for fi in unmatched_formals:
formal_to_actual[fi].append(ai)
Expand Down
2 changes: 1 addition & 1 deletion mypy/test/testcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
'check-functools.test',
'check-singledispatch.test',
'check-slots.test',
'check-formatting.test'
'check-formatting.test',
]

# Tests that use Python 3.8-only AST features (like expression-scoped ignores):
Expand Down
9 changes: 1 addition & 8 deletions test-data/unit/check-dataclasses.test
Original file line number Diff line number Diff line change
Expand Up @@ -1346,14 +1346,7 @@ from dataclasses import dataclass, field

@dataclass
class Foo:
bar: float = field(**{"repr": False})
[out]
main:7: error: Unpacking **kwargs in "field()" is not supported
main:7: error: No overload variant of "field" matches argument type "Dict[str, bool]"
main:7: note: Possible overload variants:
main:7: note: def [_T] field(*, default: _T, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T
main:7: note: def [_T] field(*, default_factory: Callable[[], _T], init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T
main:7: note: def field(*, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> Any
bar: float = field(**{"repr": False}) # E: Unpacking **kwargs in "field()" is not supported
[builtins fixtures/dataclasses.pyi]

[case testDataclassFieldWithPositionalArguments]
Expand Down
41 changes: 39 additions & 2 deletions test-data/unit/check-kwargs.test
Original file line number Diff line number Diff line change
Expand Up @@ -484,13 +484,50 @@ def f(*vargs: int, **kwargs: object) -> None:
def g(arg: int = 0, **kwargs: object) -> None:
pass

def h(*, arg: int = 0, **kwargs: object) -> None:
pass

def j(arg: int, **kwargs: object) -> None:
pass

def k(*, arg: int, **kwargs: object) -> None:
pass

d = {} # type: Dict[str, object]
f()
f(**d)
g(**d) # E: Argument 1 to "g" has incompatible type "**Dict[str, object]"; expected "int"
g()
g(**d)
g(1, **d)
g(arg=1, **d)
h()
h(**d)
h(arg=1, **d)
j(1)
j(1, **d)
j(arg=1, **d)
j(**d) # E: Argument 1 to "j" has incompatible type "**Dict[str, object]"; expected "int"
Copy link
Member Author

@sobolevn sobolevn Nov 21, 2021

Choose a reason for hiding this comment

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

When functions do not have a default value - we still raise a type error. Because we are not sure that **kwargs will match existing argument. But, when there's a default - we have nothing to worry about in any case.

Am I missing something?

k(arg=1)
k(arg=1, **d)
k(**d) # E: Argument 1 to "k" has incompatible type "**Dict[str, object]"; expected "int"

m = {} # type: Mapping[str, object]
f()
f(**m)
g(**m) # E: Argument 1 to "g" has incompatible type "**Mapping[str, object]"; expected "int"
g()
g(**m)
g(1, **d)
g(arg=1, **d)
h()
h(**m)
h(arg=1, **d)
j(1)
j(1, **d)
j(arg=1, **d)
j(**m) # E: Argument 1 to "j" has incompatible type "**Mapping[str, object]"; expected "int"
k(arg=1)
k(arg=1, **d)
k(**m) # E: Argument 1 to "k" has incompatible type "**Mapping[str, object]"; expected "int"
[builtins fixtures/dict.pyi]

[case testPassingEmptyDictWithStars]
Expand Down