From 527f16702ce3ce93e2a4236c41bc96b883ebf59e Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 16 Aug 2020 12:29:03 +0300 Subject: [PATCH 1/6] Fixes protocols with untyped slots --- mypy/semanal.py | 17 +++++++++++++++-- test-data/unit/check-protocols.test | 8 ++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index c3b9f8e91817..173fba28eade 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2320,8 +2320,10 @@ 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()): + for lv in s.lvalues: + print(lv) + print(isinstance(lv, NameExpr), lv.is_inferred_def) 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): @@ -2332,6 +2334,17 @@ 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: AssertStmt) -> bool: + """Used to check whethere or not protocol member is annotated.""" + 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. diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index b78becc88be4..cdafca134e1a 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -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] From 5a053d28fa21229cb4c645a1c612b45f8203fc70 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 16 Aug 2020 12:34:22 +0300 Subject: [PATCH 2/6] Fixes protocols with untyped slots --- mypy/semanal.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 173fba28eade..dfec94746784 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2321,9 +2321,6 @@ def process_type_annotation(self, s: AssignmentStmt) -> None: lvalue.node.is_abstract_var = True else: if (self.type and self.type.is_protocol and self.is_annotated_protocol_member(s) and not self.is_func_scope()): - for lv in s.lvalues: - print(lv) - print(isinstance(lv, NameExpr), lv.is_inferred_def) 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): From 91ea98d719eee601da6faca855427ed49ddcaeb7 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 16 Aug 2020 12:46:51 +0300 Subject: [PATCH 3/6] Fixes protocols with untyped slots --- mypy/semanal.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index dfec94746784..db21c0bf053b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2320,7 +2320,8 @@ def process_type_annotation(self, s: AssignmentStmt) -> None: if isinstance(lvalue.node, Var): lvalue.node.is_abstract_var = True else: - if (self.type and self.type.is_protocol and self.is_annotated_protocol_member(s) 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): @@ -2331,7 +2332,7 @@ 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: AssertStmt) -> bool: + def is_annotated_protocol_member(self, s: AssignmentStmt) -> bool: """Used to check whethere or not protocol member is annotated.""" return any( ( From 66d7637d5d69af138e05fa7959db509c98a6c5e8 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 17 Aug 2020 15:05:47 +0300 Subject: [PATCH 4/6] Update mypy/semanal.py Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/semanal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index db21c0bf053b..063449ed5afe 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2333,7 +2333,7 @@ def process_type_annotation(self, s: AssignmentStmt) -> None: self.store_declared_types(lvalue, s.type) def is_annotated_protocol_member(self, s: AssignmentStmt) -> bool: - """Used to check whethere or not protocol member is annotated.""" + """Check whether a protocol member is annotated.""" return any( ( isinstance(lv, NameExpr) From d01a56f6c5d2be8121345ab5a6ef1861eb300829 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 24 Aug 2020 15:37:21 +0300 Subject: [PATCH 5/6] Update semanal.py --- mypy/semanal.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 063449ed5afe..be9d8b4e3021 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2333,7 +2333,9 @@ def process_type_annotation(self, s: AssignmentStmt) -> None: self.store_declared_types(lvalue, s.type) def is_annotated_protocol_member(self, s: AssignmentStmt) -> bool: - """Check whether a protocol member is annotated.""" + """Check whether a protocol member is annotated. + + There are some exceptions that can be left unannotated, like ``__slots__``.""" return any( ( isinstance(lv, NameExpr) From d398a205857e7188aff6a1b370b9a4217632fb68 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 24 Aug 2020 15:38:10 +0300 Subject: [PATCH 6/6] Update semanal.py --- mypy/semanal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index be9d8b4e3021..51a3becad23f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2334,7 +2334,7 @@ def process_type_annotation(self, s: AssignmentStmt) -> None: 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( (