diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 2c530f9ab2e5..76e963f43063 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1353,11 +1353,16 @@ def anytype() -> mypy.types.AnyType: ) # Try and look up a stub for the runtime object - stub = get_stub(type(runtime).__module__) + if isinstance(runtime, type): + runtime_type = runtime # This might be a class + else: + runtime_type = type(runtime) # Or an instance + + stub = get_stub(runtime_type.__module__) if stub is None: return None - type_name = type(runtime).__name__ - if type_name not in stub.names: + type_name = getattr(runtime_type, "__name__", None) + if not isinstance(type_name, str) or type_name not in stub.names: return None type_info = stub.names[type_name].node if isinstance(type_info, nodes.Var): @@ -1382,6 +1387,8 @@ def anytype() -> mypy.types.AnyType: elif isinstance(runtime, (bool, int, str)): value = runtime else: + if isinstance(runtime, type): + return mypy.types.TypeType(fallback) return fallback return mypy.types.LiteralType(value=value, fallback=fallback) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index bb20e335a035..d6b7affb72b6 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -707,6 +707,184 @@ def read_write_attr(self, val): self._val = val error=None, ) + @collect_cases + def test_class_var(self) -> Iterator[Case]: + # Regular classes: + yield Case(stub="class Z: ...", runtime="class Z: ...", error=None) + yield Case( + stub=""" + class A1: + a: type[Z] + """, + runtime=""" + class A1: + a = Z + """, + error=None, + ) + yield Case( + stub=""" + class A2: + a: type[Z] + """, + runtime=""" + class A2: + a = int + """, + error="A2.a", + ) + yield Case( + stub=""" + class A3: + a: Z + """, + runtime=""" + class A3: + a = Z + """, + error="A3.a", + ) + yield Case( + stub=""" + class A4: + a: type[Z] + """, + runtime=""" + class A4: + a = Z() + """, + error="A4.a", + ) + + @collect_cases + def test_class_var_meta(self) -> Iterator[Case]: + # Now, with metaclasses: + yield Case( + stub=""" + class Meta(type): ... + class Y(metaclass=Meta): ... + """, + runtime=""" + class Meta(type): ... + class Y(metaclass=Meta): ... + """, + error=None, + ) + yield Case( + stub=""" + class B1: + a: type[Y] + """, + runtime=""" + class B1: + a = Y + """, + error=None, + ) + yield Case( + stub=""" + class B2: + a: type[Y] + """, + runtime=""" + class B2: + a = int + """, + error="B2.a", + ) + yield Case( + stub=""" + class B3: + a: Meta + """, + runtime=""" + class B3: + a = Y + """, + error=None, + ) + yield Case( + stub=""" + class B4: + a: Meta + """, + runtime=""" + class B4: + a = int + """, + error="B4.a", + ) + yield Case( + stub=""" + class B5: + a: type[Y] + """, + runtime=""" + class B5: + a = Y() + """, + error="B5.a", + ) + yield Case( + stub=""" + class B6: + a: Y + """, + runtime=""" + class B6: + a = Y + """, + error="B6.a", + ) + yield Case( + stub=""" + class B7: + a: Meta + """, + runtime=""" + class B7: + a = Y() + """, + error="B7.a", + ) + + @collect_cases + def test_class_var_abc(self) -> Iterator[Case]: + # Now, with ABC metaclasses: + yield Case( + stub=""" + import abc + class Y(metaclass=abc.ABCMeta): ... + """, + runtime=""" + import abc + class Y(metaclass=abc.ABCMeta): ... + """, + error=None, + ) + yield Case( + stub=""" + class B1: + a: type[Y] + """, + runtime=""" + class B1: + a = Y + """, + error=None, + ) + yield Case( + stub=""" + class B2: + a: Y + """, + runtime=""" + class B2: + a = Y + """, + error="B2.a", + ) + @collect_cases def test_type_alias(self) -> Iterator[Case]: yield Case(