Repository: python/mypy

Issue:
collections.abc.Sequence considered typing.Sequence
```python
from collections.abc import Sequence

class Spam: pass

Sequence.register(Spam)
```

Produces this error when run through mypy:

```
test.py:5: error: "Type[Sequence[Any]]" has no attribute "register"
```

This is a regression in v0.650. This error was not produced in v0.641.


Discussion:
I wonder if this is due to a change in typeshed -- I get the same error with mypy at v0.641 but typeshed at the current rev, and I don't get the error with mypy at v0.650 but typeshed at the rev it was at in the mypy 0.641 release. (I hope you follow what I'm saying here.)

If you want to help fix this, it should be easy enough to track down the precise typeshed change using `git bisect`.
This is caused by PR https://github.com/python/typeshed/pull/2658, for some reasons `Sized` was the only base that has `metaclass=ABCMeta`, and now it's gone. A quick solution is to put the metaclass back where it belongs.

However, I think a better solution is automatically infer `ABCMeta` as a metaclass for protocols if no explicit metaclass is given in a class or its bases. The reason is that `Protocol` is an instance of `ABCMeta` at runtime, plus logically protocols are just fancier ABCs that type-checkers understand statically.
(set priority high, since this is a regression)

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -817,8 +817,8 @@ def analyze_class_body(self, defn: ClassDef) -> Iterator[bool]:
             else:
                 self.setup_class_def_analysis(defn)
                 self.analyze_base_classes(defn)
-                self.analyze_metaclass(defn)
                 defn.info.is_protocol = is_protocol
+                self.analyze_metaclass(defn)
                 defn.info.runtime_protocol = False
                 for decorator in defn.decorators:
                     self.analyze_class_decorator(defn, decorator)
@@ -1339,6 +1339,14 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
             assert isinstance(inst, Instance)
             defn.info.declared_metaclass = inst
         defn.info.metaclass_type = defn.info.calculate_metaclass_type()
+        if any(info.is_protocol for info in defn.info.mro):
+            if (not defn.info.metaclass_type or
+                    defn.info.metaclass_type.type.fullname() == 'builtins.type'):
+                # All protocols and their subclasses have ABCMeta metaclass by default.
+                # TODO: add a metaclass conflict check if there is another metaclass.
+                abc_meta = self.named_type_or_none('abc.ABCMeta', [])
+                if abc_meta is not None:  # May be None in tests with incomplete lib-stub.
+                    defn.info.metaclass_type = abc_meta
         if defn.info.metaclass_type is None:
             # Inconsistency may happen due to multiple baseclasses even in classes that
             # do not declare explicit metaclass, but it's harder to catch at this stage


Test patch:
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -2395,3 +2395,13 @@ reveal_type([a, b])  # E: Revealed type is 'builtins.list[def (x: def (__main__.
 reveal_type([b, a])  # E: Revealed type is 'builtins.list[def (x: def (__main__.B) -> __main__.B)]'
 [builtins fixtures/list.pyi]
 [out]
+
+[case testProtocolsAlwaysABCs]
+from typing import Protocol
+
+class P(Protocol): ...
+class C(P): ...
+
+reveal_type(C.register(int))  # E: Revealed type is 'def () -> builtins.int'
+[typing fixtures/typing-full.pyi]
+[out]
diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test
--- a/test-data/unit/check-serialize.test
+++ b/test-data/unit/check-serialize.test
@@ -378,6 +378,7 @@ class A(metaclass=ABCMeta):
     def f(self) -> None: pass
     @abstractproperty
     def x(self) -> int: return 0
+[typing fixtures/typing-full.pyi]
 [out2]
 tmp/a.py:2: error: Cannot instantiate abstract class 'A' with abstract attributes 'f' and 'x'
 tmp/a.py:9: error: Property "x" defined in "A" is read-only