Skip to content

Commit 9e768ae

Browse files
committed
Don't use a heuristic for ParamSpec inference from arguments
The fallout is worse lambda inference, but this is more correct
1 parent da42901 commit 9e768ae

File tree

2 files changed

+28
-12
lines changed

2 files changed

+28
-12
lines changed

Diff for: mypy/constraints.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -228,18 +228,13 @@ def infer_constraints_for_callable(
228228
if param_spec and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2):
229229
# If actual arguments are mapped to ParamSpec type, we can't infer individual
230230
# constraints, instead store them and infer single constraint at the end.
231-
# It is impossible to map actual kind to formal kind, so use some heuristic.
232-
# This inference is used as a fallback, so relying on heuristic should be OK.
233231
if not incomplete_star_mapping:
234232
param_spec_arg_types.append(
235233
mapper.expand_actual_type(
236234
actual_arg_type, arg_kinds[actual], None, arg_kinds[actual]
237235
)
238236
)
239-
actual_kind = arg_kinds[actual]
240-
param_spec_arg_kinds.append(
241-
ARG_POS if actual_kind not in (ARG_STAR, ARG_STAR2) else actual_kind
242-
)
237+
param_spec_arg_kinds.append(arg_kinds[actual])
243238
param_spec_arg_names.append(arg_names[actual] if arg_names else None)
244239
else:
245240
actual_type = mapper.expand_actual_type(

Diff for: test-data/unit/check-parameter-specification.test

+27-6
Original file line numberDiff line numberDiff line change
@@ -439,13 +439,16 @@ def f(x: int) -> None: pass
439439
def g(x: int, y: str) -> None: pass
440440

441441
reveal_type(register(lambda: f(1))) # N: Revealed type is "def ()"
442-
reveal_type(register(lambda x: f(x), x=1)) # N: Revealed type is "def (x: Literal[1]?)"
442+
reveal_type(register(lambda x: f(x), x=1)) # N: Revealed type is "def (*, x: Literal[1]?)" \
443+
# E: Cannot infer type of lambda
443444
register(lambda x: f(x)) # E: Cannot infer type of lambda \
444445
# E: Argument 1 to "register" has incompatible type "Callable[[Any], None]"; expected "Callable[[], None]"
445-
register(lambda x: f(x), y=1) # E: Argument 1 to "register" has incompatible type "Callable[[Arg(int, 'x')], None]"; expected "Callable[[Arg(int, 'y')], None]"
446+
register(lambda x: f(x), y=1) # E: Cannot infer type of lambda \
447+
# E: Argument 1 to "register" has incompatible type "Callable[[Any], None]"; expected "Callable[[NamedArg(int, 'y')], None]"
446448
reveal_type(register(lambda x: f(x), 1)) # N: Revealed type is "def (Literal[1]?)"
447449
reveal_type(register(lambda x, y: g(x, y), 1, "a")) # N: Revealed type is "def (Literal[1]?, Literal['a']?)"
448-
reveal_type(register(lambda x, y: g(x, y), 1, y="a")) # N: Revealed type is "def (Literal[1]?, y: Literal['a']?)"
450+
reveal_type(register(lambda x, y: g(x, y), 1, y="a")) # N: Revealed type is "def (Literal[1]?, *, y: Literal['a']?)" \
451+
# E: Cannot infer type of lambda
449452
[builtins fixtures/dict.pyi]
450453

451454
[case testParamSpecInvalidCalls]
@@ -1677,7 +1680,7 @@ class Foo(Generic[P]):
16771680
def test(*args: P.args, **kwargs: P.kwargs) -> Foo[P]: ...
16781681

16791682
reveal_type(test(1, 2)) # N: Revealed type is "__main__.Foo[[Literal[1]?, Literal[2]?]]"
1680-
reveal_type(test(x=1, y=2)) # N: Revealed type is "__main__.Foo[[x: Literal[1]?, y: Literal[2]?]]"
1683+
reveal_type(test(x=1, y=2)) # N: Revealed type is "__main__.Foo[[*, x: Literal[1]?, y: Literal[2]?]]"
16811684
ints = [1, 2, 3]
16821685
reveal_type(test(*ints)) # N: Revealed type is "__main__.Foo[[*builtins.int]]"
16831686
[builtins fixtures/paramspec.pyi]
@@ -1732,7 +1735,7 @@ apply(apply, test2, 42, "yes")
17321735
apply(apply, test2, "no", 42) # E: Argument 1 to "apply" has incompatible type "Callable[[Callable[P, T], **P], None]"; expected "Callable[[Callable[[int, str], None], str, int], None]"
17331736
apply(apply, test2, x=42, y="yes")
17341737
apply(apply, test2, y="yes", x=42)
1735-
apply(apply, test2, y=42, x="no") # E: Argument 1 to "apply" has incompatible type "Callable[[Callable[P, T], **P], None]"; expected "Callable[[Callable[[int, str], None], int, str], None]"
1738+
apply(apply, test2, y=42, x="no") # E: Argument 1 to "apply" has incompatible type "Callable[[Callable[P, T], **P], None]"; expected "Callable[[Callable[[int, str], None], NamedArg(int, 'y'), NamedArg(str, 'x')], None]"
17361739
[builtins fixtures/paramspec.pyi]
17371740

17381741
[case testParamSpecApplyPosVsNamedOptional]
@@ -2157,7 +2160,7 @@ reveal_type(submit( # N: Revealed type is "__main__.Result"
21572160
backend="asyncio",
21582161
))
21592162
submit(
2160-
run, # E: Argument 1 to "submit" has incompatible type "Callable[[Callable[[], R], VarArg(object), DefaultNamedArg(str, 'backend')], R]"; expected "Callable[[Callable[[], Result], int], Result]"
2163+
run, # E: Argument 1 to "submit" has incompatible type "Callable[[Callable[[], R], VarArg(object), DefaultNamedArg(str, 'backend')], R]"; expected "Callable[[Callable[[], Result], NamedArg(int, 'backend')], Result]"
21612164
run_portal,
21622165
backend=int(),
21632166
)
@@ -2532,3 +2535,21 @@ class GenericWrapper(Generic[P]):
25322535
def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ...
25332536
def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ...
25342537
[builtins fixtures/paramspec.pyi]
2538+
2539+
[case testParamSpecInferenceFromArgs]
2540+
from typing_extensions import ParamSpec
2541+
from typing import Any, Callable, Union
2542+
2543+
P = ParamSpec("P")
2544+
2545+
def into(f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None:
2546+
return None
2547+
2548+
class C:
2549+
def f(self, y: bool = False, *, x: int = 42) -> None:
2550+
return None
2551+
2552+
ex: Union[C, Any] = C()
2553+
2554+
into(ex.f, x=-1)
2555+
[builtins fixtures/paramspec.pyi]

0 commit comments

Comments
 (0)