From 9c0a6f9ba355b307cd87c861619040e4b4691af4 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 6 Jul 2024 23:57:05 +0100 Subject: [PATCH] Fix crash on self-type in callable protocol (#17499) Fixes https://github.com/python/mypy/issues/16629 This is really ad-hoc, but a proper fix would be much more hard, and this currently we have a crash in a relatively common scenario. --- mypy/solve.py | 12 ++++++++++++ test-data/unit/check-selftype.test | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/mypy/solve.py b/mypy/solve.py index bb87b6576ada..8a1495a9a246 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -553,6 +553,11 @@ def pre_validate_solutions( """ new_solutions: list[Type | None] = [] for t, s in zip(original_vars, solutions): + if is_callable_protocol(t.upper_bound): + # This is really ad-hoc, but a proper fix would be much more complex, + # and otherwise this may cause crash in a relatively common scenario. + new_solutions.append(s) + continue if s is not None and not is_subtype(s, t.upper_bound): bound_satisfies_all = True for c in constraints: @@ -567,3 +572,10 @@ def pre_validate_solutions( continue new_solutions.append(s) return new_solutions + + +def is_callable_protocol(t: Type) -> bool: + proper_t = get_proper_type(t) + if isinstance(proper_t, Instance) and proper_t.type.is_protocol: + return "__call__" in proper_t.type.protocol_members + return False diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 1480c83b2272..9601852ef823 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -2132,3 +2132,31 @@ class D: x: int x: Union[C, D] reveal_type(x.x) # N: Revealed type is "Union[__main__.C, builtins.int]" + +[case testCallableProtocolTypingSelf] +from typing import Protocol, Self + +class MyProtocol(Protocol): + __name__: str + + def __call__( + self: Self, + ) -> None: ... + +def test() -> None: ... +value: MyProtocol = test + +[case testCallableProtocolOldSelf] +from typing import Protocol, TypeVar + +Self = TypeVar("Self", bound="MyProtocol") + +class MyProtocol(Protocol): + __name__: str + + def __call__( + self: Self, + ) -> None: ... + +def test() -> None: ... +value: MyProtocol = test