Skip to content

Commit

Permalink
Allow protocols with untyped "__slots__" (#9314)
Browse files Browse the repository at this point in the history
Closes #7290.

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
  • Loading branch information
sobolevn and hauntsaninja authored Aug 28, 2020
1 parent 96db3a0 commit 55cffc1
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 2 deletions.
17 changes: 15 additions & 2 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2320,8 +2320,8 @@ def process_type_annotation(self, s: AssignmentStmt) -> None:
if isinstance(lvalue.node, Var):
lvalue.node.is_abstract_var = True
else:
if (any(isinstance(lv, NameExpr) and lv.is_inferred_def for lv in s.lvalues) and
self.type and self.type.is_protocol and not self.is_func_scope()):
if (self.type and self.type.is_protocol and
self.is_annotated_protocol_member(s) and not self.is_func_scope()):
self.fail('All protocol members must have explicitly declared types', s)
# Set the type if the rvalue is a simple literal (even if the above error occurred).
if len(s.lvalues) == 1 and isinstance(s.lvalues[0], RefExpr):
Expand All @@ -2332,6 +2332,19 @@ def process_type_annotation(self, s: AssignmentStmt) -> None:
for lvalue in s.lvalues:
self.store_declared_types(lvalue, s.type)

def is_annotated_protocol_member(self, s: AssignmentStmt) -> bool:
"""Check whether a protocol member is annotated.
There are some exceptions that can be left unannotated, like ``__slots__``."""
return any(
(
isinstance(lv, NameExpr)
and lv.name != '__slots__'
and lv.is_inferred_def
)
for lv in s.lvalues
)

def analyze_simple_literal_type(self, rvalue: Expression, is_final: bool) -> Optional[Type]:
"""Return builtins.int if rvalue is an int literal, etc.
Expand Down
8 changes: 8 additions & 0 deletions test-data/unit/check-protocols.test
Original file line number Diff line number Diff line change
Expand Up @@ -2499,3 +2499,11 @@ reveal_type(abs(3)) # N: Revealed type is 'builtins.int*'
reveal_type(abs(ALL)) # N: Revealed type is 'builtins.int*'
[builtins fixtures/float.pyi]
[typing fixtures/typing-full.pyi]

[case testProtocolWithSlots]
from typing import Protocol

class A(Protocol):
__slots__ = ()

[builtins fixtures/tuple.pyi]

0 comments on commit 55cffc1

Please sign in to comment.