Skip to content

Commit d64cd95

Browse files
committed
Improve positional/named pairing
1 parent 4b13114 commit d64cd95

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

mypy/join.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,7 @@ def combine_parameters_with(
716716
kwarg = arg_transformer(s_kw.typ, t_kw.typ)
717717
if isinstance(get_proper_type(kwarg), UninhabitedType):
718718
kwarg = None
719+
spent_names: set[str] = set()
719720
for s_kind, s_a in zip(s.arg_kinds, s.formal_arguments(include_star_args=True)):
720721
if vararg is not None and s_kind == ArgKind.ARG_STAR:
721722
args_meet.append(vararg)
@@ -731,17 +732,30 @@ def combine_parameters_with(
731732
continue
732733
if s_kind.is_star():
733734
continue
734-
t_a = t.argument_by_position(s_a.pos) or t.argument_by_name(s_a.name)
735-
if t_a is None:
735+
736+
candidates = [t.argument_by_position(s_a.pos)]
737+
if s_a.name is not None and s_a.name not in spent_names:
738+
candidates.append(t.argument_by_name(s_a.name))
739+
candidates = [c for c in candidates if c is not None]
740+
if not candidates:
736741
if s_a.required:
737742
return None
738743
continue
739-
typ = arg_transformer(s_a.typ, t_a.typ)
740-
if not allow_uninhabited and isinstance(get_proper_type(typ), UninhabitedType):
741-
return None
744+
745+
for t_a in candidates:
746+
typ = arg_transformer(s_a.typ, t_a.typ)
747+
if not isinstance(get_proper_type(typ), UninhabitedType):
748+
break
749+
else:
750+
if not s_a.required:
751+
continue
752+
if not allow_uninhabited:
753+
return None
754+
if t_a.name is not None:
755+
spent_names.add(t_a.name)
742756
args_meet.append(typ)
743757
arg_names.append(s_a.name if s_a.name == t_a.name else None)
744-
kinds = [ArgKind.ARG_POS, ArgKind.ARG_OPT, ArgKind.ARG_NAMED, ArgKind.ARG_NAMED_OPT]
758+
kinds = [ArgKind.ARG_OPT, ArgKind.ARG_POS, ArgKind.ARG_NAMED_OPT, ArgKind.ARG_NAMED]
745759
if s_a.pos != t_a.pos or s_a.pos is None or t_a.pos is None:
746760
kinds = [k for k in kinds if not k.is_positional()]
747761
if s_a.name != t_a.name or s_a.name is None or t_a.name is None:

test-data/unit/check-functions.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3712,3 +3712,27 @@ reveal_type(join(base, bad2)) # E: Value of type variable "C" of "join" cannot
37123712
reveal_type(join(base, bad3)) # E: Value of type variable "C" of "join" cannot be "function" \
37133713
# N: Revealed type is "builtins.function"
37143714
[builtins fixtures/tuple.pyi]
3715+
3716+
[case testCallableJoin5]
3717+
from typing import Callable, TypeVar
3718+
3719+
C = TypeVar("C", bound=Callable[..., None])
3720+
3721+
def join(t: C, val: C) -> C:
3722+
pass
3723+
3724+
def less1(code, lang, style=None): ...
3725+
def more1(code, lang, suffix="", style=None): ...
3726+
reveal_type(join(less1, more1)) # N: Revealed type is "def (code: Any, lang: Any, Any =) -> Any"
3727+
reveal_type(join(more1, less1)) # N: Revealed type is "def (code: Any, lang: Any, Any =) -> Any"
3728+
3729+
def less2(code, lang, style: int = 0): ...
3730+
def more2(code, lang, suffix: str = "", style: int = 0): ...
3731+
reveal_type(join(less2, more2)) # N: Revealed type is "def (code: Any, lang: Any, *, style: builtins.int =) -> Any"
3732+
reveal_type(join(more2, less2)) # N: Revealed type is "def (code: Any, lang: Any, *, style: builtins.int =) -> Any"
3733+
3734+
def less3(code, lang, style: int = 0): ...
3735+
def more3(code, lang, suffix: int = 0, style: str = ""): ...
3736+
reveal_type(join(less3, more3)) # N: Revealed type is "def (code: Any, lang: Any, builtins.int =) -> Any"
3737+
reveal_type(join(more3, less3)) # N: Revealed type is "def (code: Any, lang: Any, builtins.int =) -> Any"
3738+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)