diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index c8cfd69eec7f..cfb983f99c37 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1807,8 +1807,15 @@ def check_callable_call( ): proper_arg = get_proper_type(arg_types[0]) if isinstance(proper_arg, Instance) and proper_arg.type.is_protocol: - self.msg.fail("Calling type() on a protocol class is unsound", context) - callee = callee.copy_modified(ret_type=self.named_type("builtins.object")) + callee = callee.copy_modified( + ret_type=UnionType( + [ + self.named_type("builtins.type"), + self.named_type("types.ModuleType"), + AnyType(TypeOfAny.special_form), + ] + ) + ) else: callee = callee.copy_modified(ret_type=TypeType.make_normalized(arg_types[0])) diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index fb81d0f24c52..b9a66cf34792 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -4217,19 +4217,29 @@ def g4(a: Input[bytes], b: Output[str]) -> None: [builtins fixtures/tuple.pyi] [case testTypeCallOnProtocol] -from typing import Protocol - -import mod +from typing import Any, Protocol, cast +import types class P(Protocol): def foo(self) -> None: ... +import mod + a: P = mod -value = type(a) # E: Calling type() on a protocol class is unsound -reveal_type(value) # N: Revealed type is "builtins.object" -reveal_type(value.foo) # E: "object" has no attribute "foo" \ +value = type(a) +reveal_type(value) # N: Revealed type is "Union[builtins.type, types.ModuleType, Any]" +reveal_type(value.foo) # E: Item "type" of "Union[type, Module, Any]" has no attribute "foo" \ # N: Revealed type is "Any" +class Namespace: ... +n = Namespace() +n.foo = lambda: None # E: "Namespace" has no attribute "foo" + +b: P = cast(Any, n) +value = type(b) +reveal_type(value) # N: Revealed type is "Union[builtins.type, types.ModuleType, Any]" +reveal_type(value.foo) # E: Item "type" of "Union[type, Module, Any]" has no attribute "foo" \ + # N: Revealed type is "Any" [file mod.py] def foo() -> None: ...