Skip to content

Commit

Permalink
Fix ParamSpec inference for callback protocols (#15986)
Browse files Browse the repository at this point in the history
Fixes #15984

Fix is straightforward, `ParamSpec` inference special-casing should put
instances with `__call__` and callable types on same ground.
  • Loading branch information
ilevkivskyi authored and koogoro committed Sep 2, 2023
1 parent d7b2451 commit 807bd39
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 0 deletions.
4 changes: 4 additions & 0 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2185,6 +2185,10 @@ def get_arg_infer_passes(
# run(test, 1, 2)
# we will use `test` for inference, since it will allow to infer also
# argument *names* for P <: [x: int, y: int].
if isinstance(p_actual, Instance):
call_method = find_member("__call__", p_actual, p_actual, is_operator=True)
if call_method is not None:
p_actual = get_proper_type(call_method)
if (
isinstance(p_actual, CallableType)
and not p_actual.variables
Expand Down
15 changes: 15 additions & 0 deletions test-data/unit/check-parameter-specification.test
Original file line number Diff line number Diff line change
Expand Up @@ -1824,3 +1824,18 @@ class C(Generic[P]): ...
c: C[int, [int, str], str] # E: Nested parameter specifications are not allowed
reveal_type(c) # N: Revealed type is "__main__.C[Any]"
[builtins fixtures/paramspec.pyi]

[case testParamSpecInferenceWithCallbackProtocol]
from typing import Protocol, Callable, ParamSpec

class CB(Protocol):
def __call__(self, x: str, y: int) -> None: ...

P = ParamSpec('P')
def g(fn: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ...

cb: CB
g(cb, y=0, x='a') # OK
g(cb, y='a', x=0) # E: Argument "y" to "g" has incompatible type "str"; expected "int" \
# E: Argument "x" to "g" has incompatible type "int"; expected "str"
[builtins fixtures/paramspec.pyi]

0 comments on commit 807bd39

Please sign in to comment.