Skip to content

Commit 1cb8cc4

Browse files
authored
Do not prioritize ParamSpec signatures during overload resolution (#18033)
Closes #18027. Var-args and star-args overloads are handled first to handle functions like `zip` correctly in cases like `zip(*[[1],[2],[3]])`. That does not seem to be necessary in case of `ParamSpec` overloads (or at least such use case is much less obvious). So this PR prevents `ParamSpec` overloads from floating up in the list of overload targets.
1 parent 376e2ad commit 1cb8cc4

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

mypy/checkexpr.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -2781,7 +2781,7 @@ def plausible_overload_call_targets(
27812781
) -> list[CallableType]:
27822782
"""Returns all overload call targets that having matching argument counts.
27832783
2784-
If the given args contains a star-arg (*arg or **kwarg argument, including
2784+
If the given args contains a star-arg (*arg or **kwarg argument, except for
27852785
ParamSpec), this method will ensure all star-arg overloads appear at the start
27862786
of the list, instead of their usual location.
27872787
@@ -2816,7 +2816,9 @@ def has_shape(typ: Type) -> bool:
28162816
# ParamSpec can be expanded in a lot of different ways. We may try
28172817
# to expand it here instead, but picking an impossible overload
28182818
# is safe: it will be filtered out later.
2819-
star_matches.append(typ)
2819+
# Unlike other var-args signatures, ParamSpec produces essentially
2820+
# a fixed signature, so there's no need to push them to the top.
2821+
matches.append(typ)
28202822
elif self.check_argument_count(
28212823
typ, arg_types, arg_kinds, arg_names, formal_to_actual, None
28222824
):

test-data/unit/check-parameter-specification.test

+35
Original file line numberDiff line numberDiff line change
@@ -2303,3 +2303,38 @@ reveal_type(capture(fn)) # N: Revealed type is "Union[builtins.str, builtins.in
23032303
reveal_type(capture(err)) # N: Revealed type is "builtins.int"
23042304

23052305
[builtins fixtures/paramspec.pyi]
2306+
2307+
[case testRunParamSpecOverlappingOverloadsOrder]
2308+
from typing import Any, Callable, overload
2309+
from typing_extensions import ParamSpec
2310+
2311+
P = ParamSpec("P")
2312+
2313+
class Base:
2314+
pass
2315+
class Child(Base):
2316+
def __call__(self) -> str: ...
2317+
class NotChild:
2318+
def __call__(self) -> str: ...
2319+
2320+
@overload
2321+
def handle(func: Base) -> int: ...
2322+
@overload
2323+
def handle(func: Callable[P, str], *args: P.args, **kwargs: P.kwargs) -> str: ...
2324+
def handle(func: Any, *args: Any, **kwargs: Any) -> Any:
2325+
return func(*args, **kwargs)
2326+
2327+
@overload
2328+
def handle_reversed(func: Callable[P, str], *args: P.args, **kwargs: P.kwargs) -> str: ...
2329+
@overload
2330+
def handle_reversed(func: Base) -> int: ...
2331+
def handle_reversed(func: Any, *args: Any, **kwargs: Any) -> Any:
2332+
return func(*args, **kwargs)
2333+
2334+
reveal_type(handle(Child())) # N: Revealed type is "builtins.int"
2335+
reveal_type(handle(NotChild())) # N: Revealed type is "builtins.str"
2336+
2337+
reveal_type(handle_reversed(Child())) # N: Revealed type is "builtins.str"
2338+
reveal_type(handle_reversed(NotChild())) # N: Revealed type is "builtins.str"
2339+
2340+
[builtins fixtures/paramspec.pyi]

0 commit comments

Comments
 (0)