diff --git a/mypy/checker.py b/mypy/checker.py index 3fce1adfbb45..bc9bdf5390ec 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -409,6 +409,11 @@ def __init__( # of giving a note on possibly missing "await". It is used to avoid infinite recursion. self.checking_missing_await = False + # While this is True, allow passing an abstract class where Type[T] is expected. + # although this is technically unsafe, this is desirable in some context, for + # example when type-checking class decorators. + self.allow_abstract_call = False + @property def type_context(self) -> List[Optional[Type]]: return self.expr_checker.type_context @@ -2027,9 +2032,12 @@ def visit_class_def(self, defn: ClassDef) -> None: # TODO: Figure out how to have clearer error messages. # (e.g. "class decorator must be a function that accepts a type." + old_allow_abstract_call = self.allow_abstract_call + self.allow_abstract_call = True sig, _ = self.expr_checker.check_call( dec, [temp], [nodes.ARG_POS], defn, callable_name=fullname ) + self.allow_abstract_call = old_allow_abstract_call # TODO: Apply the sig to the actual TypeInfo so we can handle decorators # that completely swap out the type. (e.g. Callable[[Type[A]], Type[B]]) if typ.is_protocol and typ.defn.type_vars: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 565e20b9c243..df349a3cb5bc 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1914,6 +1914,7 @@ def check_arg( 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 ): self.msg.concrete_only_call(callee_type, context) elif not is_subtype(caller_type, callee_type, options=self.chk.options): diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index beb2d9397e43..c782609f7ec0 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -1018,3 +1018,15 @@ d = my_abstract_types['B']() # E: Cannot instantiate abstract class "MyAbstract d.do() [builtins fixtures/dict.pyi] + +[case testAbstractClassesWorkWithGenericDecorators] +from abc import abstractmethod, ABCMeta +from typing import Type, TypeVar + +T = TypeVar("T") +def deco(cls: Type[T]) -> Type[T]: ... + +@deco +class A(metaclass=ABCMeta): + @abstractmethod + def foo(self, x: int) -> None: ...