diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index aee8d58a69f6..46a5e35f320d 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2352,15 +2352,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( @@ -5484,6 +5476,26 @@ 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) -> 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)) + 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) + 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 1c85fae93dd4..dc64476beda6 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -198,6 +198,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