Skip to content

Commit

Permalink
All protocols and their explicit subclasses are ABCs (#6042)
Browse files Browse the repository at this point in the history
Fixes #6036
  • Loading branch information
ilevkivskyi committed Dec 10, 2018
1 parent dcfebd7 commit 99d4c6b
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 3 deletions.
10 changes: 9 additions & 1 deletion mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions test-data/unit/check-protocols.test
Original file line number Diff line number Diff line change
Expand Up @@ -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]
1 change: 1 addition & 0 deletions test-data/unit/check-serialize.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion test-data/unit/lib-stub/abc.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
class ABCMeta(type): pass
from typing import Type, Any, TypeVar

T = TypeVar('T', bound=Type[Any])

class ABCMeta(type):
def register(cls, tp: T) -> T: pass
abstractmethod = object()
abstractproperty = object()
2 changes: 1 addition & 1 deletion test-data/unit/lib-stub/typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Union = 0
Optional = 0
TypeVar = 0
Generic = 0
Protocol = 0 # This is not yet defined in typeshed, see PR typeshed/#1220
Protocol = 0
Tuple = 0
Callable = 0
NamedTuple = 0
Expand Down

0 comments on commit 99d4c6b

Please sign in to comment.