From 88485fd3d5da80a95e7445b6fd94c3859fb827b4 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 4 Jun 2023 10:39:32 +0300 Subject: [PATCH 1/5] Check tuples of abstract types, refs #15264 --- mypy/checkexpr.py | 28 +++++++++++++++++++--------- test-data/unit/check-abstract.test | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index cd0ff11001831..2da6ff4987d08 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2166,15 +2166,7 @@ def check_arg( if isinstance(caller_type, DeletedType): self.msg.deleted_as_rvalue(caller_type, context) # Only non-abstract non-protocol class can be given where Type[...] is expected... - elif ( - isinstance(caller_type, CallableType) - and isinstance(callee_type, TypeType) - and caller_type.is_type_obj() - and (caller_type.type_object().is_abstract or caller_type.type_object().is_protocol) - and isinstance(callee_type.item, Instance) - and (callee_type.item.type.is_abstract or callee_type.item.type.is_protocol) - and not self.chk.allow_abstract_call - ): + elif self.has_abstract_type_part(caller_type, callee_type): self.msg.concrete_only_call(callee_type, context) elif not is_subtype(caller_type, callee_type, options=self.chk.options): code = self.msg.incompatible_argument( @@ -5287,6 +5279,24 @@ def narrow_type_from_binder( return narrow_declared_type(known_type, restriction) return known_type + def has_abstract_type_part( + self, caller_type: ProperType, callee_type: ProperType + ) -> ProperType | None: + if isinstance(caller_type, TupleType) and isinstance(callee_type, TupleType): + return any( + self.has_abstract_type_part(caller, callee) + for caller, callee in zip(caller_type.items, callee_type.items) + ) + return ( + isinstance(caller_type, CallableType) + and isinstance(callee_type, TypeType) + and caller_type.is_type_obj() + and (caller_type.type_object().is_abstract or caller_type.type_object().is_protocol) + and isinstance(callee_type.item, Instance) + and (callee_type.item.type.is_abstract or callee_type.item.type.is_protocol) + and not self.chk.allow_abstract_call + ) + def has_any_type(t: Type, ignore_in_type_obj: bool = False) -> bool: """Whether t contains an Any type""" diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index 8a13e5cb57608..d5bb830aded26 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -196,6 +196,24 @@ x: Type[B] f(x) # OK [out] +[case testAbstractTypeInADict] +from typing import Dict, Type +from abc import abstractmethod + +class Class: + @abstractmethod + def method(self) -> None: + pass + +my_dict_init: Dict[int, Type[Class]] = {0: Class} # E: Only concrete class can be given where "Tuple[int, Type[Class]]" is expected + +class Child(Class): + def method(self) -> None: ... + +other_dict_init: Dict[int, Type[Class]] = {0: Child} # ok +[builtins fixtures/dict.pyi] +[out] + [case testInstantiationAbstractsInTypeForAliases] from typing import Type from abc import abstractmethod From 41f4dd7a5a9e3e68ca9e14105fdb567c82e32793 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 4 Jun 2023 10:46:02 +0300 Subject: [PATCH 2/5] Fix CI --- mypy/checkexpr.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 2da6ff4987d08..3460c027c3b83 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5279,9 +5279,7 @@ def narrow_type_from_binder( return narrow_declared_type(known_type, restriction) return known_type - def has_abstract_type_part( - self, caller_type: ProperType, callee_type: ProperType - ) -> ProperType | None: + def has_abstract_type_part(self, caller_type: ProperType, callee_type: ProperType) -> bool: if isinstance(caller_type, TupleType) and isinstance(callee_type, TupleType): return any( self.has_abstract_type_part(caller, callee) From 74984e1402d9c3cd0624fd9717bd45e5eab34e6a Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 4 Jun 2023 10:46:52 +0300 Subject: [PATCH 3/5] Fix CI --- mypy/checkexpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 3460c027c3b83..4d9bb8cf8000b 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5282,7 +5282,7 @@ def narrow_type_from_binder( def has_abstract_type_part(self, caller_type: ProperType, callee_type: ProperType) -> bool: if isinstance(caller_type, TupleType) and isinstance(callee_type, TupleType): return any( - self.has_abstract_type_part(caller, callee) + self.has_abstract_type_part(get_proper_type(caller), get_proper_type(callee)) for caller, callee in zip(caller_type.items, callee_type.items) ) return ( From 650c9793135403d59eef82c330026bbaf269d3d6 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 4 Jun 2023 11:27:55 +0300 Subject: [PATCH 4/5] Fix CI --- mypy/checkexpr.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4d9bb8cf8000b..4ee68897a24a8 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5282,9 +5282,12 @@ def narrow_type_from_binder( def has_abstract_type_part(self, caller_type: ProperType, callee_type: ProperType) -> bool: if isinstance(caller_type, TupleType) and isinstance(callee_type, TupleType): return any( - self.has_abstract_type_part(get_proper_type(caller), get_proper_type(callee)) + self.has_abstract_type(get_proper_type(caller), get_proper_type(callee)) for caller, callee in zip(caller_type.items, callee_type.items) ) + return self.has_abstract_type(caller_type, callee_type) + + def has_abstract_type(self, caller_type: ProperType, callee_type: ProperType) -> bool: return ( isinstance(caller_type, CallableType) and isinstance(callee_type, TypeType) From 798e1a7b7b960eb5e05f8cb25c8802ac62d20abe Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 12 Jul 2023 17:28:27 +0300 Subject: [PATCH 5/5] Update mypy/checkexpr.py --- mypy/checkexpr.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4ee68897a24a8..b180929ccf909 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5280,6 +5280,7 @@ def narrow_type_from_binder( return known_type def has_abstract_type_part(self, caller_type: ProperType, callee_type: ProperType) -> bool: + # TODO: support other possible types here if isinstance(caller_type, TupleType) and isinstance(callee_type, TupleType): return any( self.has_abstract_type(get_proper_type(caller), get_proper_type(callee))