Repository: python/mypy

Issue:
Unpacking enum type generates an error
**To Reproduce**

```python
from enum import Enum

class E(Enum):
    A = 1
    B = 2

A, B = E
```

**Actual Behavior**

This code generates an error:
```
$ mypy --strict proj/iter_enum.py
proj/iter_enum.py:7: error: "Type[E]" object is not iterable  [misc]
```

**Expected Behavior**

This should type check without errors. Note that mypy knows that `E` is actually iterable. For example,
```python
A, B = list(E)
```
works fine and so does iterating over `E` in a for loop.

**Your Environment**

- Mypy version used: `mypy 1.1.0+dev.c23e831ab0e7ec827c38cc830d3ebd3f4c43cd75`
- Mypy command-line flags: with and without `--strict`
- Python version used: 3.10.6



Discussion:

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -3632,7 +3632,9 @@ def check_multi_assignment_from_iterable(
         infer_lvalue_type: bool = True,
     ) -> None:
         rvalue_type = get_proper_type(rvalue_type)
-        if self.type_is_iterable(rvalue_type) and isinstance(rvalue_type, Instance):
+        if self.type_is_iterable(rvalue_type) and isinstance(
+            rvalue_type, (Instance, CallableType, TypeType, Overloaded)
+        ):
             item_type = self.iterable_item_type(rvalue_type)
             for lv in lvalues:
                 if isinstance(lv, StarExpr):
@@ -6387,15 +6389,16 @@ def note(
             return
         self.msg.note(msg, context, offset=offset, code=code)
 
-    def iterable_item_type(self, instance: Instance) -> Type:
-        iterable = map_instance_to_supertype(instance, self.lookup_typeinfo("typing.Iterable"))
-        item_type = iterable.args[0]
-        if not isinstance(get_proper_type(item_type), AnyType):
-            # This relies on 'map_instance_to_supertype' returning 'Iterable[Any]'
-            # in case there is no explicit base class.
-            return item_type
+    def iterable_item_type(self, it: Instance | CallableType | TypeType | Overloaded) -> Type:
+        if isinstance(it, Instance):
+            iterable = map_instance_to_supertype(it, self.lookup_typeinfo("typing.Iterable"))
+            item_type = iterable.args[0]
+            if not isinstance(get_proper_type(item_type), AnyType):
+                # This relies on 'map_instance_to_supertype' returning 'Iterable[Any]'
+                # in case there is no explicit base class.
+                return item_type
         # Try also structural typing.
-        return self.analyze_iterable_item_type_without_expression(instance, instance)[1]
+        return self.analyze_iterable_item_type_without_expression(it, it)[1]
 
     def function_type(self, func: FuncBase) -> FunctionLike:
         return function_type(func, self.named_type("builtins.function"))


Test patch:
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -270,6 +270,120 @@ def f() -> None:
 class A: pass
 [out]
 
+[case testClassObjectsNotUnpackableWithoutIterableMetaclass]
+from typing import Type
+
+class Foo: ...
+A: Type[Foo] = Foo
+a, b = Foo  # E: "Type[Foo]" object is not iterable
+c, d = A  # E: "Type[Foo]" object is not iterable
+
+class Meta(type): ...
+class Bar(metaclass=Meta): ...
+B: Type[Bar] = Bar
+e, f = Bar  # E: "Type[Bar]" object is not iterable
+g, h = B  # E: "Type[Bar]" object is not iterable
+
+reveal_type(a)  # E: Cannot determine type of "a"  # N: Revealed type is "Any"
+reveal_type(b)  # E: Cannot determine type of "b"  # N: Revealed type is "Any"
+reveal_type(c)  # E: Cannot determine type of "c"  # N: Revealed type is "Any"
+reveal_type(d)  # E: Cannot determine type of "d"  # N: Revealed type is "Any"
+reveal_type(e)  # E: Cannot determine type of "e"  # N: Revealed type is "Any"
+reveal_type(f)  # E: Cannot determine type of "f"  # N: Revealed type is "Any"
+reveal_type(g)  # E: Cannot determine type of "g"  # N: Revealed type is "Any"
+reveal_type(h)  # E: Cannot determine type of "h"  # N: Revealed type is "Any"
+[out]
+
+[case testInferringLvarTypesUnpackedFromIterableClassObject]
+from typing import Iterator, Type, TypeVar, Union, overload
+class Meta(type):
+    def __iter__(cls) -> Iterator[int]:
+        yield from [1, 2, 3]
+
+class Meta2(type):
+    def __iter__(cls) -> Iterator[str]:
+        yield from ["foo", "bar", "baz"]
+
+class Meta3(type): ...
+
+class Foo(metaclass=Meta): ...
+class Bar(metaclass=Meta2): ...
+class Baz(metaclass=Meta3): ...
+class Spam: ...
+
+class Eggs(metaclass=Meta):
+    @overload
+    def __init__(self, x: int) -> None: ...
+    @overload
+    def __init__(self, x: int, y: int, z: int) -> None: ...
+    def __init__(self, x: int, y: int = ..., z: int = ...) -> None: ...
+
+A: Type[Foo] = Foo
+B: Type[Union[Foo, Bar]] = Foo
+C: Union[Type[Foo], Type[Bar]] = Foo
+D: Type[Union[Foo, Baz]] = Foo
+E: Type[Union[Foo, Spam]] = Foo
+F: Type[Eggs] = Eggs
+G: Type[Union[Foo, Eggs]] = Foo
+
+a, b, c = Foo
+d, e, f = A
+g, h, i = B
+j, k, l = C
+m, n, o = D  # E: "Type[Baz]" object is not iterable
+p, q, r = E  # E: "Type[Spam]" object is not iterable
+s, t, u = Eggs
+v, w, x = F
+y, z, aa = G
+
+for var in [a, b, c, d, e, f, s, t, u, v, w, x, y, z, aa]:
+    reveal_type(var)  # N: Revealed type is "builtins.int"
+
+for var2 in [g, h, i, j, k, l]:
+    reveal_type(var2)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+
+for var3 in [m, n, o, p, q, r]:
+    reveal_type(var3)  # N: Revealed type is "Union[builtins.int, Any]"
+
+T = TypeVar("T", bound=Type[Foo])
+
+def check(x: T) -> T:
+    a, b, c = x
+    for var in [a, b, c]:
+        reveal_type(var)  # N: Revealed type is "builtins.int"
+    return x
+
+T2 = TypeVar("T2", bound=Type[Union[Foo, Bar]])
+
+def check2(x: T2) -> T2:
+    a, b, c = x
+    for var in [a, b, c]:
+        reveal_type(var)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+    return x
+
+T3 = TypeVar("T3", bound=Union[Type[Foo], Type[Bar]])
+
+def check3(x: T3) -> T3:
+    a, b, c = x
+    for var in [a, b, c]:
+        reveal_type(var)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+    return x
+[out]
+
+[case testInferringLvarTypesUnpackedFromIterableClassObjectWithGenericIter]
+from typing import Iterator, Type, TypeVar
+
+T = TypeVar("T")
+class Meta(type):
+    def __iter__(self: Type[T]) -> Iterator[T]: ...
+class Foo(metaclass=Meta): ...
+
+A, B, C = Foo
+reveal_type(A)  # N: Revealed type is "__main__.Foo"
+reveal_type(B)  # N: Revealed type is "__main__.Foo"
+reveal_type(C)  # N: Revealed type is "__main__.Foo"
+[out]
+
 [case testInferringLvarTypesInMultiDefWithInvalidTuple]
 from typing import Tuple
 t = None # type: Tuple[object, object, object]
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -1878,6 +1878,23 @@ _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"
 
+[case testEnumUnpackedViaMetaclass]
+from enum import Enum
+
+class FooEnum(Enum):
+    A = 1
+    B = 2
+    C = 3
+
+a, b, c = FooEnum
+reveal_type(a)
+reveal_type(b)
+reveal_type(c)
+[out]
+_testEnumUnpackedViaMetaclass.py:9: note: Revealed type is "_testEnumUnpackedViaMetaclass.FooEnum"
+_testEnumUnpackedViaMetaclass.py:10: note: Revealed type is "_testEnumUnpackedViaMetaclass.FooEnum"
+_testEnumUnpackedViaMetaclass.py:11: note: Revealed type is "_testEnumUnpackedViaMetaclass.FooEnum"
+
 [case testNativeIntTypes]
 # Spot check various native int operations with full stubs.
 from mypy_extensions import i64, i32