Skip to content
Open
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
5 changes: 5 additions & 0 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,11 @@ def incompatible_argument(
if isinstance(arg_type, Instance) and isinstance(type, Instance):
notes = append_invariance_notes(notes, arg_type, type)
notes = append_numbers_notes(notes, arg_type, type)
if arg_kind == ARG_STAR2 and isinstance(arg_type, Instance):
notes.append(
"Consider annotating the ** argument as"
' "**kwargs: Any" or using a TypedDict'
)
object_type = get_proper_type(object_type)
if isinstance(object_type, TypedDictType):
code = codes.TYPEDDICT_ITEM
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-columns.test
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ def g(**x: int) -> None: pass
a = ['']
f(*a) # E:4: Argument 1 to "f" has incompatible type "*list[str]"; expected "int"
b = {'x': 'y'}
g(**b) # E:5: Argument 1 to "g" has incompatible type "**dict[str, str]"; expected "int"
g(**b) # E:5: Argument 1 to "g" has incompatible type "**dict[str, str]"; expected "int" \
# N:5: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
[builtins fixtures/dict.pyi]

[case testColumnsMultipleStatementsPerLine]
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-expressions.test
Original file line number Diff line number Diff line change
Expand Up @@ -1770,7 +1770,8 @@ kw2 = {'x': ''}
d2 = dict(it, **kw2)
d2() # E: "dict[str, object]" not callable

d3 = dict(it, **kw2) # type: Dict[str, int] # E: Argument 2 to "dict" has incompatible type "**dict[str, str]"; expected "int"
d3 = dict(it, **kw2) # type: Dict[str, int] # E: Argument 2 to "dict" has incompatible type "**dict[str, str]"; expected "int" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
[builtins fixtures/dict.pyi]

[case testDictFromIterableAndStarStarArgs2]
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -3769,7 +3769,8 @@ def foo(x: P, y: P) -> None: ...
args: list[object]
foo(*args) # E: Argument 1 to "foo" has incompatible type "*list[object]"; expected "P"
kwargs: dict[str, object]
foo(**kwargs) # E: Argument 1 to "foo" has incompatible type "**dict[str, object]"; expected "P"
foo(**kwargs) # E: Argument 1 to "foo" has incompatible type "**dict[str, object]"; expected "P" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
[builtins fixtures/dict.pyi]

[case testNoImplicitReturnErrorOnDeferral_no_empty]
Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/check-functools.test
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,10 @@ functools.partial(foo, 1, "a", "b", "c", d="a") # E: Argument 3 to "foo" has in
def bar(*a: bytes, **k: int):
p1("a", 2, 3, 4, d="a", **k)
p1("a", d="a", **k)
p1("a", **k) # E: Argument 2 to "foo" has incompatible type "**dict[str, int]"; expected "str"
p1(**k) # E: Argument 1 to "foo" has incompatible type "**dict[str, int]"; expected "str"
p1("a", **k) # E: Argument 2 to "foo" has incompatible type "**dict[str, int]"; expected "str" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
p1(**k) # E: Argument 1 to "foo" has incompatible type "**dict[str, int]"; expected "str" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
p1(*a) # E: Expected iterable as variadic argument


Expand Down
1 change: 1 addition & 0 deletions test-data/unit/check-generic-subtyping.test
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,7 @@ main:12: note: Revealed type is "typing.Iterator[builtins.int]"
main:13: note: Revealed type is "builtins.dict[builtins.int, builtins.str]"
main:14: error: Argument after ** must have string keys
main:14: error: Argument 1 to "func_with_kwargs" has incompatible type "**X1[str, int]"; expected "int"
main:14: note: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
[builtins fixtures/dict.pyi]
[typing fixtures/typing-medium.pyi]

Expand Down
35 changes: 27 additions & 8 deletions test-data/unit/check-kwargs.test
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,20 @@ d: Dict[str, A]
f(**d)
f(x=A(), **d)
d2: Dict[str, B]
f(**d2) # E: Argument 1 to "f" has incompatible type "**dict[str, B]"; expected "A"
f(x=A(), **d2) # E: Argument 2 to "f" has incompatible type "**dict[str, B]"; expected "A"
f(**{'x': B()}) # E: Argument 1 to "f" has incompatible type "**dict[str, B]"; expected "A"
f(**d2) # E: Argument 1 to "f" has incompatible type "**dict[str, B]"; expected "A" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
f(x=A(), **d2) # E: Argument 2 to "f" has incompatible type "**dict[str, B]"; expected "A" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
f(**{'x': B()}) # E: Argument 1 to "f" has incompatible type "**dict[str, B]"; expected "A" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
[builtins fixtures/dict.pyi]

[case testIncompatibleKwargsNote]
from typing import Any
def f(x: int, y: str) -> None: pass
d: dict[str, int] = {}
f(**d) # E: Argument 1 to "f" has incompatible type "**dict[str, int]"; expected "str" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
[builtins fixtures/dict.pyi]

[case testKwargsAllowedInDunderCall]
Expand Down Expand Up @@ -345,7 +356,8 @@ from typing import Mapping
class MappingSubclass(Mapping[str, str]): pass
def f(**kwargs: 'A') -> None: pass
d: MappingSubclass
f(**d) # E: Argument 1 to "f" has incompatible type "**MappingSubclass"; expected "A"
f(**d) # E: Argument 1 to "f" has incompatible type "**MappingSubclass"; expected "A" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
class A: pass
[builtins fixtures/dict.pyi]

Expand All @@ -372,7 +384,8 @@ def f(a: 'A', b: 'B') -> None: pass
d: Dict[str, Any]
f(**d)
d2: Dict[str, A]
f(**d2) # E: Argument 1 to "f" has incompatible type "**dict[str, A]"; expected "B"
f(**d2) # E: Argument 1 to "f" has incompatible type "**dict[str, A]"; expected "B" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
class A: pass
class B: pass
[builtins fixtures/dict.pyi]
Expand Down Expand Up @@ -449,7 +462,8 @@ f(**a) # okay

b = {'': ''}
f(b) # E: Argument 1 to "f" has incompatible type "dict[str, str]"; expected "int"
f(**b) # E: Argument 1 to "f" has incompatible type "**dict[str, str]"; expected "int"
f(**b) # E: Argument 1 to "f" has incompatible type "**dict[str, str]"; expected "int" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict

c = {0: 0}
f(**c) # E: Argument after ** must have string keys
Expand Down Expand Up @@ -506,11 +520,13 @@ def g(arg: int = 0, **kwargs: object) -> None:

d = {} # type: Dict[str, object]
f(**d)
g(**d) # E: Argument 1 to "g" has incompatible type "**dict[str, object]"; expected "int"
g(**d) # E: Argument 1 to "g" has incompatible type "**dict[str, object]"; expected "int" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict

m = {} # type: Mapping[str, object]
f(**m)
g(**m) # E: Argument 1 to "g" has incompatible type "**Mapping[str, object]"; expected "int"
g(**m) # E: Argument 1 to "g" has incompatible type "**Mapping[str, object]"; expected "int" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
[builtins fixtures/dict.pyi]

[case testPassingEmptyDictWithStars]
Expand Down Expand Up @@ -577,10 +593,13 @@ foo(**good2)
foo(**good3)
[out]
main:36: error: Argument 1 to "foo" has incompatible type "**A[str, str]"; expected "float"
main:36: note: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
main:37: error: Argument 1 to "foo" has incompatible type "**B[str, str]"; expected "float"
main:37: note: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
main:38: error: Argument after ** must be a mapping, not "C[str, float]"
main:39: error: Argument after ** must be a mapping, not "D"
main:41: error: Argument 1 to "foo" has incompatible type "**dict[str, str]"; expected "float"
main:41: note: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
[builtins fixtures/dict.pyi]

[case testLiteralKwargs]
Expand Down
9 changes: 6 additions & 3 deletions test-data/unit/check-parameter-specification.test
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,8 @@ def c3(f: Callable[P, int], *args, **kwargs) -> int: ...
def c4(f: Callable[P, int], *args: int, **kwargs: str) -> int:
# but not ok to call:
f(*args, **kwargs) # E: Argument 1 has incompatible type "*tuple[int, ...]"; expected "P.args" \
# E: Argument 2 has incompatible type "**dict[str, str]"; expected "P.kwargs"
# E: Argument 2 has incompatible type "**dict[str, str]"; expected "P.kwargs" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
return 1

def f1(f: Callable[P, int], *args, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs"
Expand Down Expand Up @@ -1306,7 +1307,8 @@ def c3(f: Callable[Concatenate[int, P], int], *args, **kwargs) -> int: ...
def c4(f: Callable[Concatenate[int, P], int], *args: int, **kwargs: str) -> int:
# but not ok to call:
f(1, *args, **kwargs) # E: Argument 2 has incompatible type "*tuple[int, ...]"; expected "P.args" \
# E: Argument 3 has incompatible type "**dict[str, str]"; expected "P.kwargs"
# E: Argument 3 has incompatible type "**dict[str, str]"; expected "P.kwargs" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
return 1

def f1(f: Callable[Concatenate[int, P], int], *args, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs"
Expand Down Expand Up @@ -2417,7 +2419,8 @@ def run3(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwar
func2 = partial(func, 1, *args)
d = {"":""}
func2(**d) # E: Too few arguments \
# E: Argument 1 has incompatible type "**dict[str, str]"; expected "P.kwargs"
# E: Argument 1 has incompatible type "**dict[str, str]"; expected "P.kwargs" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
return func2(**kwargs)

def run4(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T:
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-varargs.test
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,8 @@ Weird = TypedDict("Weird", {"@": int})
def foo(**kwargs: Unpack[Weird]) -> None:
reveal_type(kwargs["@"]) # N: Revealed type is "builtins.int"
foo(**{"@": 42})
foo(**{"no": "way"}) # E: Argument 1 to "foo" has incompatible type "**dict[str, str]"; expected "int"
foo(**{"no": "way"}) # E: Argument 1 to "foo" has incompatible type "**dict[str, str]"; expected "int" \
# N: Consider annotating the ** argument as "**kwargs: Any" or using a TypedDict
[builtins fixtures/dict.pyi]
[typing fixtures/typing-typeddict.pyi]

Expand Down
Loading