diff --git a/mypy/constraints.py b/mypy/constraints.py index 697e793cb11d..a8f04094ca63 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -619,6 +619,17 @@ def visit_instance(self, template: Instance) -> list[Constraint]: actual.item, template, subtype, template, class_obj=True ) ) + if self.direction == SUPERTYPE_OF: + # Infer constraints for Type[T] via metaclass of T when it makes sense. + a_item = actual.item + if isinstance(a_item, TypeVarType): + a_item = get_proper_type(a_item.upper_bound) + if isinstance(a_item, Instance) and a_item.type.metaclass_type: + res.extend( + self.infer_constraints_from_protocol_members( + a_item.type.metaclass_type, template, actual, template + ) + ) if isinstance(actual, Overloaded) and actual.fallback is not None: actual = actual.fallback diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index e490457ff25c..96b3a484f56a 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -3977,3 +3977,24 @@ class C: DEFAULT: ClassVar[C] x: P = C() + +[case testInferenceViaTypeTypeMetaclass] +from typing import Iterator, Iterable, TypeVar, Type + +M = TypeVar("M") + +class Meta(type): + def __iter__(self: Type[M]) -> Iterator[M]: ... +class Foo(metaclass=Meta): ... + +T = TypeVar("T") +def test(x: Iterable[T]) -> T: ... + +reveal_type(test(Foo)) # N: Revealed type is "__main__.Foo" +t_foo: Type[Foo] +reveal_type(test(t_foo)) # N: Revealed type is "__main__.Foo" + +TF = TypeVar("TF", bound=Foo) +def outer(cls: Type[TF]) -> TF: + reveal_type(test(cls)) # N: Revealed type is "TF`-1" + return cls() diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 3520b5874018..b414eba9f679 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1858,3 +1858,22 @@ _testTupleWithDifferentArgsPy310.py:20: note: Revealed type is "builtins.list[Tu _testTupleWithDifferentArgsPy310.py:26: error: Invalid type: try using Literal[1] instead? _testTupleWithDifferentArgsPy310.py:27: error: Unexpected "..." _testTupleWithDifferentArgsPy310.py:30: note: Revealed type is "builtins.tuple[builtins.object, ...]" + +[case testEnumIterMetaInference] +import socket +from enum import Enum +from typing import Iterable, Iterator, Type, TypeVar + +_E = TypeVar("_E", bound=Enum) + +def enum_iter(cls: Type[_E]) -> Iterable[_E]: + reveal_type(iter(cls)) + reveal_type(next(iter(cls))) + return iter(cls) + +for value in enum_iter(socket.SocketKind): + reveal_type(value) +[out] +_testEnumIterMetaInference.py:8: note: Revealed type is "typing.Iterator[_E`-1]" +_testEnumIterMetaInference.py:9: note: Revealed type is "_E`-1" +_testEnumIterMetaInference.py:13: note: Revealed type is "socket.SocketKind"