Skip to content

Commit 34aeea1

Browse files
committed
merge kwargs without suggestions into one error line
1 parent 75e0a26 commit 34aeea1

2 files changed

Lines changed: 80 additions & 25 deletions

File tree

mypy/messages.py

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,48 +1123,78 @@ def no_variant_matches_arguments(
11231123
unexpected_kwargs.append((arg_name, arg_types[i]))
11241124

11251125
if unexpected_kwargs:
1126+
all_kwargs_confident = True
1127+
kwargs_with_suggestions: list[tuple[str, list[str]]] = []
1128+
kwargs_without_suggestions: list[str] = []
1129+
1130+
# Find suggestions for each unexpected kwarg, prioritizing type-matching args
11261131
for kwarg_name, kwarg_type in unexpected_kwargs:
11271132
matching_type_args: list[str] = []
11281133
not_matching_type_args: list[str] = []
1129-
matching_variant: CallableType | None = None
1134+
has_matching_variant = False
11301135

11311136
for item in overload.items:
1132-
has_type_match = False
1137+
item_has_type_match = False
11331138
for i, formal_type in enumerate(item.arg_types):
11341139
formal_name = item.arg_names[i]
11351140
if formal_name is not None and item.arg_kinds[i] != ARG_STAR:
11361141
if is_subtype(kwarg_type, formal_type):
11371142
if formal_name not in matching_type_args:
11381143
matching_type_args.append(formal_name)
1139-
has_type_match = True
1140-
else:
1141-
if formal_name not in not_matching_type_args:
1142-
not_matching_type_args.append(formal_name)
1143-
if has_type_match and matching_variant is None:
1144-
matching_variant = item
1144+
item_has_type_match = True
1145+
elif formal_name not in not_matching_type_args:
1146+
not_matching_type_args.append(formal_name)
1147+
if item_has_type_match:
1148+
has_matching_variant = True
11451149

11461150
matches = best_matches(kwarg_name, matching_type_args, n=3)
11471151
if not matches:
11481152
matches = best_matches(kwarg_name, not_matching_type_args, n=3)
11491153

1150-
msg = f'Unexpected keyword argument "{kwarg_name}"' + for_func
1151-
11521154
if matches:
1153-
msg += f"; did you mean {pretty_seq(matches, 'or')}?"
1154-
self.fail(msg, context, code=code)
1155+
kwargs_with_suggestions.append((kwarg_name, matches))
1156+
else:
1157+
kwargs_without_suggestions.append(kwarg_name)
11551158

1156-
if matching_variant is None:
1157-
self.note(
1158-
f"Possible overload variant{plural_s(len(overload.items))}:",
1159+
if not has_matching_variant:
1160+
all_kwargs_confident = False
1161+
1162+
for kwarg_name, matches in kwargs_with_suggestions:
1163+
self.fail(
1164+
f'Unexpected keyword argument "{kwarg_name}"'
1165+
f"{for_func}; did you mean {pretty_seq(matches, 'or')}?",
1166+
context,
1167+
code=code,
1168+
)
1169+
1170+
if kwargs_without_suggestions:
1171+
if len(kwargs_without_suggestions) == 1:
1172+
self.fail(
1173+
f'Unexpected keyword argument "{kwargs_without_suggestions[0]}"{for_func}',
1174+
context,
1175+
code=code,
1176+
)
1177+
else:
1178+
quoted_names = ", ".join(f'"{n}"' for n in kwargs_without_suggestions)
1179+
self.fail(
1180+
f"Unexpected keyword arguments {quoted_names}{for_func}",
11591181
context,
11601182
code=code,
11611183
)
1162-
for item in overload.items:
1163-
self.note(
1164-
pretty_callable(item, self.options), context, offset=4, code=code
1165-
)
11661184

1167-
return
1185+
if not all_kwargs_confident:
1186+
self.note(
1187+
f"Possible overload variant{plural_s(len(overload.items))}:",
1188+
context,
1189+
code=code,
1190+
)
1191+
for item in overload.items:
1192+
self.note(
1193+
pretty_callable(item, self.options), context, offset=4, code=code
1194+
)
1195+
1196+
if all_kwargs_confident and len(unexpected_kwargs) == len(arg_types):
1197+
return
11681198

11691199
arg_types_str = ", ".join(format_type(arg, self.options) for arg in arg_types)
11701200
num_args = len(arg_types)

test-data/unit/check-expressions.test

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2592,19 +2592,23 @@ def f(foobar: int) -> None: ...
25922592
@overload
25932593
def f(foobar: str) -> None: ...
25942594

2595-
def f(foobar: Union[int, str]) -> None:
2596-
pass
2595+
def f(foobar: Union[int, str]) -> None: pass
25972596

25982597
f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"?
25992598
f(random=[1,2,3]) # E: Unexpected keyword argument "random" for overloaded function "f" \
26002599
# N: Possible overload variants: \
26012600
# N: def f(foobar: int) -> None \
2602-
# N: def f(foobar: str) -> None
2601+
# N: def f(foobar: str) -> None \
2602+
# E: No overload variant of "f" matches argument type "list[int]"
26032603

26042604
f(fobar=1, baz=2) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"? \
26052605
# E: Unexpected keyword argument "baz" for overloaded function "f"
26062606

2607-
f(foobar=1, invalid=2) # E: Unexpected keyword argument "invalid" for overloaded function "f"
2607+
f(foobar=1, a=2, b=3, c=4, d=5, e=6) # E: Unexpected keyword arguments "a", "b", "c", "d", "e" for overloaded function "f" \
2608+
# E: No overload variant of "f" matches argument types "int", "int", "int", "int", "int", "int" \
2609+
# N: Possible overload variants: \
2610+
# N: def f(foobar: int) -> None \
2611+
# N: def f(foobar: str) -> None
26082612

26092613
@overload
26102614
def g(x: int, y: int) -> int: ...
@@ -2615,5 +2619,26 @@ def g(x: str, y: str) -> str: ...
26152619
def g(x: Union[int, str], y: Union[int, str]) -> Union[int, str]:
26162620
return x
26172621

2618-
g([1, 2], z=3) # E: Unexpected keyword argument "z" for overloaded function "g"
2622+
f(fobar=1, other=[1,2,3]) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"? \
2623+
# E: Unexpected keyword argument "other" for overloaded function "f" \
2624+
# N: Possible overload variants: \
2625+
# N: def f(foobar: int) -> None \
2626+
# N: def f(foobar: str) -> None \
2627+
# E: No overload variant of "f" matches argument types "int", "list[int]"
2628+
2629+
g([1, 2], 3) # E: No overload variant of "g" matches argument types "list[int]", "int" \
2630+
# N: Possible overload variants: \
2631+
# N: def g(x: int, y: int) -> int \
2632+
# N: def g(x: str, y: str) -> str
2633+
2634+
g([1, 2], z=3) # E: Unexpected keyword argument "z" for overloaded function "g" \
2635+
# E: No overload variant of "g" matches argument types "list[int]", "int" \
2636+
# N: Possible overload variants: \
2637+
# N: def g(x: int, y: int) -> int \
2638+
# N: def g(x: str, y: str) -> str
2639+
2640+
g(x="hello", y=1) # E: No overload variant of "g" matches argument types "str", "int" \
2641+
# N: Possible overload variants: \
2642+
# N: def g(x: int, y: int) -> int \
2643+
# N: def g(x: str, y: str) -> str
26192644
[builtins fixtures/list.pyi]

0 commit comments

Comments
 (0)