diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 57051acaf4a65..d1b08ed162b52 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1816,10 +1816,14 @@ def check_callable_call( if ( callee.is_type_obj() - and (len(arg_types) == 1) + and len(arg_types) == 1 and is_equivalent(callee.ret_type, self.named_type("builtins.type")) ): - callee = callee.copy_modified(ret_type=TypeType.make_normalized(arg_types[0])) + proper_arg = get_proper_type(arg_types[0]) + if isinstance(proper_arg, Instance) and proper_arg.type.is_protocol: + callee = callee.copy_modified(ret_type=self.named_type("builtins.type")) + else: + callee = callee.copy_modified(ret_type=TypeType.make_normalized(arg_types[0])) if callable_node: # Store the inferred callable type. diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index fd7f0c3449daf..f83f82ef3b2e6 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -4238,6 +4238,35 @@ def g4(a: Input[bytes], b: Output[str]) -> None: [builtins fixtures/tuple.pyi] +[case testTypeCallOnProtocol] +from typing import Any, Protocol, cast +import types + +class P(Protocol): + def foo(self) -> None: ... + +import mod + +a: P = mod +value = type(a) +reveal_type(value) # N: Revealed type is "builtins.type" +reveal_type(value.foo) # E: "type" 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 "builtins.type" +reveal_type(value.foo) # E: "type" has no attribute "foo" \ + # N: Revealed type is "Any" +[file mod.py] +def foo() -> None: ... + +[builtins fixtures/tuple.pyi] + [case testOverloadProtocolSubtyping] from typing import Protocol, Self, overload