Skip to content

Commit a91ff26

Browse files
committed
merge kwargs without suggestions into one error line
1 parent a772b80 commit a91ff26

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
@@ -1117,48 +1117,78 @@ def no_variant_matches_arguments(
11171117
unexpected_kwargs.append((arg_name, arg_types[i]))
11181118

11191119
if unexpected_kwargs:
1120+
all_kwargs_confident = True
1121+
kwargs_with_suggestions: list[tuple[str, list[str]]] = []
1122+
kwargs_without_suggestions: list[str] = []
1123+
1124+
# Find suggestions for each unexpected kwarg, prioritizing type-matching args
11201125
for kwarg_name, kwarg_type in unexpected_kwargs:
11211126
matching_type_args: list[str] = []
11221127
not_matching_type_args: list[str] = []
1123-
matching_variant: CallableType | None = None
1128+
has_matching_variant = False
11241129

11251130
for item in overload.items:
1126-
has_type_match = False
1131+
item_has_type_match = False
11271132
for i, formal_type in enumerate(item.arg_types):
11281133
formal_name = item.arg_names[i]
11291134
if formal_name is not None and item.arg_kinds[i] != ARG_STAR:
11301135
if is_subtype(kwarg_type, formal_type):
11311136
if formal_name not in matching_type_args:
11321137
matching_type_args.append(formal_name)
1133-
has_type_match = True
1134-
else:
1135-
if formal_name not in not_matching_type_args:
1136-
not_matching_type_args.append(formal_name)
1137-
if has_type_match and matching_variant is None:
1138-
matching_variant = item
1138+
item_has_type_match = True
1139+
elif formal_name not in not_matching_type_args:
1140+
not_matching_type_args.append(formal_name)
1141+
if item_has_type_match:
1142+
has_matching_variant = True
11391143

11401144
matches = best_matches(kwarg_name, matching_type_args, n=3)
11411145
if not matches:
11421146
matches = best_matches(kwarg_name, not_matching_type_args, n=3)
11431147

1144-
msg = f'Unexpected keyword argument "{kwarg_name}"' + for_func
1145-
11461148
if matches:
1147-
msg += f"; did you mean {pretty_seq(matches, 'or')}?"
1148-
self.fail(msg, context, code=code)
1149+
kwargs_with_suggestions.append((kwarg_name, matches))
1150+
else:
1151+
kwargs_without_suggestions.append(kwarg_name)
11491152

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

1161-
return
1179+
if not all_kwargs_confident:
1180+
self.note(
1181+
f"Possible overload variant{plural_s(len(overload.items))}:",
1182+
context,
1183+
code=code,
1184+
)
1185+
for item in overload.items:
1186+
self.note(
1187+
pretty_callable(item, self.options), context, offset=4, code=code
1188+
)
1189+
1190+
if all_kwargs_confident and len(unexpected_kwargs) == len(arg_types):
1191+
return
11621192

11631193
arg_types_str = ", ".join(format_type(arg, self.options) for arg in arg_types)
11641194
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
@@ -2571,19 +2571,23 @@ def f(foobar: int) -> None: ...
25712571
@overload
25722572
def f(foobar: str) -> None: ...
25732573

2574-
def f(foobar: Union[int, str]) -> None:
2575-
pass
2574+
def f(foobar: Union[int, str]) -> None: pass
25762575

25772576
f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"?
25782577
f(random=[1,2,3]) # E: Unexpected keyword argument "random" for overloaded function "f" \
25792578
# N: Possible overload variants: \
25802579
# N: def f(foobar: int) -> None \
2581-
# N: def f(foobar: str) -> None
2580+
# N: def f(foobar: str) -> None \
2581+
# E: No overload variant of "f" matches argument type "list[int]"
25822582

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

2586-
f(foobar=1, invalid=2) # E: Unexpected keyword argument "invalid" for overloaded function "f"
2586+
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" \
2587+
# E: No overload variant of "f" matches argument types "int", "int", "int", "int", "int", "int" \
2588+
# N: Possible overload variants: \
2589+
# N: def f(foobar: int) -> None \
2590+
# N: def f(foobar: str) -> None
25872591

25882592
@overload
25892593
def g(x: int, y: int) -> int: ...
@@ -2594,5 +2598,26 @@ def g(x: str, y: str) -> str: ...
25942598
def g(x: Union[int, str], y: Union[int, str]) -> Union[int, str]:
25952599
return x
25962600

2597-
g([1, 2], z=3) # E: Unexpected keyword argument "z" for overloaded function "g"
2601+
f(fobar=1, other=[1,2,3]) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"? \
2602+
# E: Unexpected keyword argument "other" for overloaded function "f" \
2603+
# N: Possible overload variants: \
2604+
# N: def f(foobar: int) -> None \
2605+
# N: def f(foobar: str) -> None \
2606+
# E: No overload variant of "f" matches argument types "int", "list[int]"
2607+
2608+
g([1, 2], 3) # E: No overload variant of "g" matches argument types "list[int]", "int" \
2609+
# N: Possible overload variants: \
2610+
# N: def g(x: int, y: int) -> int \
2611+
# N: def g(x: str, y: str) -> str
2612+
2613+
g([1, 2], z=3) # E: Unexpected keyword argument "z" for overloaded function "g" \
2614+
# E: No overload variant of "g" matches argument types "list[int]", "int" \
2615+
# N: Possible overload variants: \
2616+
# N: def g(x: int, y: int) -> int \
2617+
# N: def g(x: str, y: str) -> str
2618+
2619+
g(x="hello", y=1) # E: No overload variant of "g" matches argument types "str", "int" \
2620+
# N: Possible overload variants: \
2621+
# N: def g(x: int, y: int) -> int \
2622+
# N: def g(x: str, y: str) -> str
25982623
[builtins fixtures/list.pyi]

0 commit comments

Comments
 (0)