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 @@ -803,6 +803,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):
if arg_type.type.fullname == "builtins.dict" and len(arg_type.args) >= 2:
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 @@ -3749,7 +3749,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
8 changes: 6 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 All @@ -225,6 +227,8 @@ def test_baz(xs: List[int]):
p3(1) # E: Too many arguments for "baz"




[builtins fixtures/dict.pyi]

[case testFunctoolsPartialGeneric]
Expand Down
27 changes: 21 additions & 6 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 @@ -372,7 +383,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 +461,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 @@ -509,7 +522,8 @@ 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)
Expand Down Expand Up @@ -584,6 +598,7 @@ main:37: error: Argument 1 to "foo" has incompatible type "**B[str, str]"; expec
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
17 changes: 14 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 All @@ -1289,6 +1290,7 @@ def f5(f: Callable[P, int], *args: P.args, extra_keyword_arg: int, **kwargs: P.k
P1 = ParamSpec('P1')

def m1(f: Callable[P1, int], *a, **k: P1.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs"

[builtins fixtures/paramspec.pyi]


Expand All @@ -1306,14 +1308,16 @@ 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"
def f2(f: Callable[Concatenate[int, P], int], *args: P.args, **kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs"
def f3(f: Callable[Concatenate[int, P], int], *args: P.args) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs"
def f4(f: Callable[Concatenate[int, P], int], **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs"
def f5(f: Callable[Concatenate[int, P], int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: Arguments not allowed after ParamSpec.args

[builtins fixtures/paramspec.pyi]


Expand Down Expand Up @@ -2417,7 +2421,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 Expand Up @@ -2454,6 +2459,12 @@ def run_bad4(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.
# E: Argument 1 has incompatible type "int"; expected "P.args"
return func2(**kwargs) # E: Too few arguments







[builtins fixtures/paramspec.pyi]

[case testOtherVarArgs]
Expand Down
11 changes: 11 additions & 0 deletions test-data/unit/check-tuples.test
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,17 @@ t5: Tuple[int, int] = (1, 2, "s", 4) # E: Incompatible types in assignment (exp
# long initializer assignment with mismatched pairs
t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str", 1, 1, 1, 1, 1) # E: Incompatible types in assignment (expression has type "tuple[int, int, ... <15 more items>]", variable has type "tuple[int, int, ... <10 more items>]")












[builtins fixtures/tuple.pyi]

[case testPropertyLongTupleReturnTypeMismatchUnion]
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