From b0ff0cc8881df144601688ca01c3264df08eb7b3 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 16 Nov 2016 11:31:26 +0100 Subject: [PATCH 1/6] Some ideas --- mypy/checkexpr.py | 14 ++++++++++++-- mypy/types.py | 3 +++ test-data/unit/check-abstract.test | 26 ++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index f54703997b53..4887f2baf4ad 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -351,7 +351,8 @@ def check_call(self, callee: Type, args: List[Expression], """ arg_messages = arg_messages or self.msg if isinstance(callee, CallableType): - if callee.is_concrete_type_obj() and callee.type_object().is_abstract: + if (callee.is_concrete_type_obj() and callee.type_object().is_abstract + and not callee.from_type_type): type = callee.type_object() self.msg.cannot_instantiate_abstract_class( callee.type_object().name(), type.abstract_attributes, @@ -437,7 +438,10 @@ def analyze_type_type_callee(self, item: Type, context: Context) -> Type: if isinstance(item, AnyType): return AnyType() if isinstance(item, Instance): - return type_object_type(item.type, self.named_type) + res = type_object_type(item.type, self.named_type) + if isinstance(res, CallableType): + res.from_type_type = True + return res if isinstance(item, UnionType): return UnionType([self.analyze_type_type_callee(item, context) for item in item.items], item.line) @@ -840,6 +844,12 @@ def check_arg(self, caller_type: Type, original_caller_type: Type, messages.does_not_return_value(caller_type, context) elif isinstance(caller_type, DeletedType): messages.deleted_as_rvalue(caller_type, context) + # Only non-abstract class could be given where Type[...] is expected + elif isinstance(caller_type, CallableType) and isinstance(callee_type, TypeType): + tp = caller_type.type_object() + if caller_type.is_concrete_type_obj() and tp.is_abstract: + messages.cannot_instantiate_abstract_class(tp.name(), + tp.abstract_attributes, context) elif not is_subtype(caller_type, callee_type): if self.chk.should_suppress_optional_error([caller_type, callee_type]): return diff --git a/mypy/types.py b/mypy/types.py index 5e2d1feda81f..faec9cefa82d 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -577,6 +577,8 @@ class CallableType(FunctionLike): # Defined for signatures that require special handling (currently only value is 'dict' # for a signature similar to 'dict') special_sig = None # type: Optional[str] + # Was this callable generated by analyzing Type[...] instantiation? + from_type_type = False # type: bool def __init__(self, arg_types: List[Type], @@ -612,6 +614,7 @@ def __init__(self, self.is_ellipsis_args = is_ellipsis_args self.implicit = implicit self.special_sig = special_sig + self.from_type_type = False super().__init__(line, column) def copy_modified(self, diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index 1caeabf7858f..d19a91d00103 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -157,6 +157,32 @@ class B(A): pass B()# E: Cannot instantiate abstract class 'B' with abstract attributes 'f' and 'g' [out] +[case testInstantiationAbstractsInType] +from typing import Type +from abc import abstractmethod + +class A: + @abstractmethod + def m(self) -> None: pass +class B(A): pass +class C(B): + def m(self) -> None: + pass + +def f(cls: Type[A]) -> A: + return cls() # OK + +def g() -> A: + return A() # Error, see out + +f(A) # E: Cannot instantiate abstract class 'A' with abstract attribute 'm' +f(B) # E: Cannot instantiate abstract class 'B' with abstract attribute 'm' +f(C) # OK +[out] +main: note: In function "g": +main:16: error: Cannot instantiate abstract class 'A' with abstract attribute 'm' +main: note: At top level: + [case testInstantiatingClassWithInheritedAbstractMethodAndSuppression] from abc import abstractmethod, ABCMeta import typing From 71ea62f24d084714ebf616267c705e3cebf01550 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 12 Feb 2017 10:32:51 +0100 Subject: [PATCH 2/6] Work on corner cases, more tests --- mypy/checker.py | 9 ++++++++- mypy/checkexpr.py | 12 ++++++------ mypy/meet.py | 3 ++- mypy/types.py | 13 ++++++++++--- test-data/unit/check-abstract.test | 21 +++++++++++++++------ 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 5e2c385dee66..e27f7c567a13 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1080,7 +1080,6 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type infer_lvalue_type) else: lvalue_type, index_lvalue, inferred = self.check_lvalue(lvalue) - if isinstance(lvalue, NameExpr): if self.check_compatibility_all_supers(lvalue, lvalue_type, rvalue): # We hit an error on this line; don't check for any others @@ -1128,6 +1127,14 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type else: rvalue_type = self.check_simple_assignment(lvalue_type, rvalue, lvalue) + # Special case + if (isinstance(rvalue_type, CallableType) and rvalue_type.is_type_obj() and + rvalue_type.type_object().is_abstract and + isinstance(lvalue_type, TypeType) and + isinstance(lvalue_type.item, Instance) and lvalue_type.item.type.is_abstract): + self.fail("Cannot only assign non-abstract classes" + " to a variable of type '{}'".format(lvalue_type), rvalue) + return if rvalue_type and infer_lvalue_type: self.binder.assign_type(lvalue, rvalue_type, diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4887f2baf4ad..a26ad9b21a77 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -351,7 +351,7 @@ def check_call(self, callee: Type, args: List[Expression], """ arg_messages = arg_messages or self.msg if isinstance(callee, CallableType): - if (callee.is_concrete_type_obj() and callee.type_object().is_abstract + if (callee.is_type_obj() and callee.type_object().is_abstract and not callee.from_type_type): type = callee.type_object() self.msg.cannot_instantiate_abstract_class( @@ -440,7 +440,7 @@ def analyze_type_type_callee(self, item: Type, context: Context) -> Type: if isinstance(item, Instance): res = type_object_type(item.type, self.named_type) if isinstance(res, CallableType): - res.from_type_type = True + res = res.copy_modified(from_type_type=True) return res if isinstance(item, UnionType): return UnionType([self.analyze_type_type_callee(item, context) @@ -846,10 +846,10 @@ def check_arg(self, caller_type: Type, original_caller_type: Type, messages.deleted_as_rvalue(caller_type, context) # Only non-abstract class could be given where Type[...] is expected elif isinstance(caller_type, CallableType) and isinstance(callee_type, TypeType): - tp = caller_type.type_object() - if caller_type.is_concrete_type_obj() and tp.is_abstract: - messages.cannot_instantiate_abstract_class(tp.name(), - tp.abstract_attributes, context) + if (caller_type.is_type_obj() and caller_type.type_object().is_abstract and + isinstance(callee_type.item, Instance) and callee_type.item.type.is_abstract): + messages.fail("Only non-abstract class can be given where '{}' is expected" + .format(callee_type), context) elif not is_subtype(caller_type, callee_type): if self.chk.should_suppress_optional_error([caller_type, callee_type]): return diff --git a/mypy/meet.py b/mypy/meet.py index 7aa479c0eefc..7f5e9296fc9c 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -100,11 +100,12 @@ class C(A, B): ... elif isinstance(t, TypeType) or isinstance(s, TypeType): # If exactly only one of t or s is a TypeType, check if one of them # is an `object` or a `type` and otherwise assume no overlap. + one = t if isinstance(t, TypeType) else s other = s if isinstance(t, TypeType) else t if isinstance(other, Instance): return other.type.fullname() in {'builtins.object', 'builtins.type'} else: - return False + return isinstance(other, CallableType) and is_subtype(other, one) if experiments.STRICT_OPTIONAL: if isinstance(t, NoneTyp) != isinstance(s, NoneTyp): # NoneTyp does not overlap with other non-Union types under strict Optional checking diff --git a/mypy/types.py b/mypy/types.py index faec9cefa82d..276b33a67100 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -595,6 +595,7 @@ def __init__(self, implicit: bool = False, is_classmethod_class: bool = False, special_sig: Optional[str] = None, + from_type_type: bool = False, ) -> None: if variables is None: variables = [] @@ -613,8 +614,9 @@ def __init__(self, self.variables = variables self.is_ellipsis_args = is_ellipsis_args self.implicit = implicit + self.is_classmethod_class = is_classmethod_class self.special_sig = special_sig - self.from_type_type = False + self.from_type_type = from_type_type super().__init__(line, column) def copy_modified(self, @@ -629,7 +631,8 @@ def copy_modified(self, line: int = _dummy, column: int = _dummy, is_ellipsis_args: bool = _dummy, - special_sig: Optional[str] = _dummy) -> 'CallableType': + special_sig: Optional[str] = _dummy, + from_type_type: bool = _dummy) -> 'CallableType': return CallableType( arg_types=arg_types if arg_types is not _dummy else self.arg_types, arg_kinds=arg_kinds if arg_kinds is not _dummy else self.arg_kinds, @@ -646,6 +649,7 @@ def copy_modified(self, implicit=self.implicit, is_classmethod_class=self.is_classmethod_class, special_sig=special_sig if special_sig is not _dummy else self.special_sig, + from_type_type=from_type_type if from_type_type is not _dummy else self.from_type_type, ) def is_type_obj(self) -> bool: @@ -660,7 +664,10 @@ def type_object(self) -> mypy.nodes.TypeInfo: ret = self.ret_type if isinstance(ret, TupleType): ret = ret.fallback - return cast(Instance, ret).type + if isinstance(ret, TypeVarType): + ret = ret.upper_bound + assert isinstance(ret, Instance) + return ret.type def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_callable_type(self) diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index d19a91d00103..a1c8b4977332 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -158,6 +158,7 @@ B()# E: Cannot instantiate abstract class 'B' with abstract attributes 'f' and ' [out] [case testInstantiationAbstractsInType] +# flags: --python-version 3.6 from typing import Type from abc import abstractmethod @@ -173,15 +174,23 @@ def f(cls: Type[A]) -> A: return cls() # OK def g() -> A: - return A() # Error, see out + return A() # E: Cannot instantiate abstract class 'A' with abstract attribute 'm' -f(A) # E: Cannot instantiate abstract class 'A' with abstract attribute 'm' -f(B) # E: Cannot instantiate abstract class 'B' with abstract attribute 'm' +f(A) # E: Only non-abstract class can be given where 'Type[__main__.A]' is expected +f(B) # E: Only non-abstract class can be given where 'Type[__main__.A]' is expected f(C) # OK + +Alias = A +GoodAlias = C +Alias() # E: Cannot instantiate abstract class 'A' with abstract attribute 'm' +GoodAlias() + +var: Type[A] +var() +var = A # E: Cannot only assign non-abstract classes to a variable of type 'Type[__main__.A]' +var = B # E: Cannot only assign non-abstract classes to a variable of type 'Type[__main__.A]' +var = C # OK [out] -main: note: In function "g": -main:16: error: Cannot instantiate abstract class 'A' with abstract attribute 'm' -main: note: At top level: [case testInstantiatingClassWithInheritedAbstractMethodAndSuppression] from abc import abstractmethod, ABCMeta From ec22e4d3759335a36fdf4f1f8baf4bffb5b0924e Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 12 Feb 2017 11:36:02 +0100 Subject: [PATCH 3/6] Fix minor issues and lint --- mypy/checker.py | 8 ++++++-- mypy/checkexpr.py | 13 +++++++------ mypy/checkmember.py | 1 - test-data/unit/check-abstract.test | 2 ++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index e27f7c567a13..d30d3b27bc69 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -579,6 +579,8 @@ def is_implicit_any(t: Type) -> bool: self.fail("The erased type of self '{}' " "is not a supertype of its class '{}'" .format(erased, ref_type), defn) + if defn.is_class and isinstance(arg_type, CallableType): + arg_type.is_classmethod_class = True elif isinstance(arg_type, TypeVarType): # Refuse covariant parameter type variables # TODO: check recursively for inner type variables @@ -1128,10 +1130,12 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type rvalue_type = self.check_simple_assignment(lvalue_type, rvalue, lvalue) # Special case - if (isinstance(rvalue_type, CallableType) and rvalue_type.is_type_obj() and + if ( + isinstance(rvalue_type, CallableType) and rvalue_type.is_type_obj() and rvalue_type.type_object().is_abstract and isinstance(lvalue_type, TypeType) and - isinstance(lvalue_type.item, Instance) and lvalue_type.item.type.is_abstract): + isinstance(lvalue_type.item, Instance) and lvalue_type.item.type.is_abstract + ): self.fail("Cannot only assign non-abstract classes" " to a variable of type '{}'".format(lvalue_type), rvalue) return diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index a26ad9b21a77..2145bb130f38 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -352,7 +352,8 @@ def check_call(self, callee: Type, args: List[Expression], arg_messages = arg_messages or self.msg if isinstance(callee, CallableType): if (callee.is_type_obj() and callee.type_object().is_abstract - and not callee.from_type_type): + # Exceptions for Type[...] and classmethod first argument + and not callee.from_type_type and not callee.is_classmethod_class): type = callee.type_object() self.msg.cannot_instantiate_abstract_class( callee.type_object().name(), type.abstract_attributes, @@ -845,11 +846,11 @@ def check_arg(self, caller_type: Type, original_caller_type: Type, elif isinstance(caller_type, DeletedType): messages.deleted_as_rvalue(caller_type, context) # Only non-abstract class could be given where Type[...] is expected - elif isinstance(caller_type, CallableType) and isinstance(callee_type, TypeType): - if (caller_type.is_type_obj() and caller_type.type_object().is_abstract and - isinstance(callee_type.item, Instance) and callee_type.item.type.is_abstract): - messages.fail("Only non-abstract class can be given where '{}' is expected" - .format(callee_type), context) + elif (isinstance(caller_type, CallableType) and isinstance(callee_type, TypeType) and + caller_type.is_type_obj() and caller_type.type_object().is_abstract and + isinstance(callee_type.item, Instance) and callee_type.item.type.is_abstract): + messages.fail("Only non-abstract class can be given where '{}' is expected" + .format(callee_type), context) elif not is_subtype(caller_type, callee_type): if self.chk.should_suppress_optional_error([caller_type, callee_type]): return diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 7390bb7baf93..e9fdc8d6185a 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -518,7 +518,6 @@ def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance, ret_type=fill_typevars(info), fallback=type_type, name=None, variables=variables, special_sig=special_sig) c = callable_type.with_name('"{}"'.format(info.name())) - c.is_classmethod_class = True return c diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index a1c8b4977332..c3b2a607cb7b 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -184,6 +184,8 @@ Alias = A GoodAlias = C Alias() # E: Cannot instantiate abstract class 'A' with abstract attribute 'm' GoodAlias() +f(Alias) # E: Only non-abstract class can be given where 'Type[__main__.A]' is expected +f(GoodAlias) var: Type[A] var() From 80c82e7acdc9c18b587e33162d17ed1bc058fd4d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 12 Feb 2017 11:49:24 +0100 Subject: [PATCH 4/6] Formatting --- mypy/checker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/checker.py b/mypy/checker.py index d30d3b27bc69..9c666592f742 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1082,6 +1082,7 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type infer_lvalue_type) else: lvalue_type, index_lvalue, inferred = self.check_lvalue(lvalue) + if isinstance(lvalue, NameExpr): if self.check_compatibility_all_supers(lvalue, lvalue_type, rvalue): # We hit an error on this line; don't check for any others From 65eab95fdb766c43b94f466be31e50ed909d5cb2 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 21 Mar 2017 09:53:31 +0100 Subject: [PATCH 5/6] Apply review comments --- mypy/checker.py | 5 ++- mypy/checkexpr.py | 6 ++- test-data/unit/check-abstract.test | 63 +++++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 6b50e365faba..b20788091892 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1151,14 +1151,15 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type else: rvalue_type = self.check_simple_assignment(lvalue_type, rvalue, lvalue) - # Special case + # Special case: only non-abstract classes can be assigned to variables + # with explicit type Type[A]. if ( isinstance(rvalue_type, CallableType) and rvalue_type.is_type_obj() and rvalue_type.type_object().is_abstract and isinstance(lvalue_type, TypeType) and isinstance(lvalue_type.item, Instance) and lvalue_type.item.type.is_abstract ): - self.fail("Cannot only assign non-abstract classes" + self.fail("Can only assign non-abstract classes" " to a variable of type '{}'".format(lvalue_type), rvalue) return if rvalue_type and infer_lvalue_type: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index f5e00aa67b49..3d744612547c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -845,10 +845,12 @@ def check_arg(self, caller_type: Type, original_caller_type: Type, messages.does_not_return_value(caller_type, context) elif isinstance(caller_type, DeletedType): messages.deleted_as_rvalue(caller_type, context) - # Only non-abstract class could be given where Type[...] is expected + # Only non-abstract 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 and - isinstance(callee_type.item, Instance) and callee_type.item.type.is_abstract): + isinstance(callee_type.item, Instance) and callee_type.item.type.is_abstract and + # ...except for classmethod first argument + not caller_type.is_classmethod_class): messages.fail("Only non-abstract class can be given where '{}' is expected" .format(callee_type), context) elif not is_subtype(caller_type, callee_type): diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index c3b2a607cb7b..47ca10654067 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -157,8 +157,7 @@ class B(A): pass B()# E: Cannot instantiate abstract class 'B' with abstract attributes 'f' and 'g' [out] -[case testInstantiationAbstractsInType] -# flags: --python-version 3.6 +[case testInstantiationAbstractsInTypeForFunctions] from typing import Type from abc import abstractmethod @@ -172,13 +171,30 @@ class C(B): def f(cls: Type[A]) -> A: return cls() # OK - def g() -> A: return A() # E: Cannot instantiate abstract class 'A' with abstract attribute 'm' f(A) # E: Only non-abstract class can be given where 'Type[__main__.A]' is expected f(B) # E: Only non-abstract class can be given where 'Type[__main__.A]' is expected f(C) # OK +x: Type[B] +f(x) # OK +[out] + +[case testInstantiationAbstractsInTypeForAliases] +from typing import Type +from abc import abstractmethod + +class A: + @abstractmethod + def m(self) -> None: pass +class B(A): pass +class C(B): + def m(self) -> None: + pass + +def f(cls: Type[A]) -> A: + return cls() # OK Alias = A GoodAlias = C @@ -186,12 +202,49 @@ Alias() # E: Cannot instantiate abstract class 'A' with abstract attribute 'm' GoodAlias() f(Alias) # E: Only non-abstract class can be given where 'Type[__main__.A]' is expected f(GoodAlias) +[out] + +[case testInstantiationAbstractsInTypeForVariables] +from typing import Type +from abc import abstractmethod + +class A: + @abstractmethod + def m(self) -> None: pass +class B(A): pass +class C(B): + def m(self) -> None: + pass var: Type[A] var() -var = A # E: Cannot only assign non-abstract classes to a variable of type 'Type[__main__.A]' -var = B # E: Cannot only assign non-abstract classes to a variable of type 'Type[__main__.A]' +var = A # E: Can only assign non-abstract classes to a variable of type 'Type[__main__.A]' +var = B # E: Can only assign non-abstract classes to a variable of type 'Type[__main__.A]' var = C # OK + +var_old = None # type: Type[A] # Old syntax for variable annotations +var_old() +var_old = A # E: Can only assign non-abstract classes to a variable of type 'Type[__main__.A]' +var_old = B # E: Can only assign non-abstract classes to a variable of type 'Type[__main__.A]' +var_old = C # OK +[out] + +[case testInstantiationAbstractsInTypeForClassMethods] +from typing import Type +from abc import abstractmethod + +class Logger: + @staticmethod + def log(a: Type[C]): + pass +class C: + @classmethod + def action(cls) -> None: + cls() #OK for classmethods + Logger.log(cls) #OK for classmethods + @abstractmethod + def m(self) -> None: + pass [out] [case testInstantiatingClassWithInheritedAbstractMethodAndSuppression] From 4d2e6fd9b7381c4355dab65404afc73c7a752ec4 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 21 Mar 2017 10:29:30 +0100 Subject: [PATCH 6/6] Add test fixture; fix formatting --- mypy/checker.py | 11 +++++------ test-data/unit/check-abstract.test | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index b20788091892..ad6b1874f439 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1153,12 +1153,11 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type # Special case: only non-abstract classes can be assigned to variables # with explicit type Type[A]. - if ( - isinstance(rvalue_type, CallableType) and rvalue_type.is_type_obj() and - rvalue_type.type_object().is_abstract and - isinstance(lvalue_type, TypeType) and - isinstance(lvalue_type.item, Instance) and lvalue_type.item.type.is_abstract - ): + if (isinstance(rvalue_type, CallableType) and rvalue_type.is_type_obj() and + rvalue_type.type_object().is_abstract and + isinstance(lvalue_type, TypeType) and + isinstance(lvalue_type.item, Instance) and + lvalue_type.item.type.is_abstract): self.fail("Can only assign non-abstract classes" " to a variable of type '{}'".format(lvalue_type), rvalue) return diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index 47ca10654067..f69f21e61e0e 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -245,6 +245,7 @@ class C: @abstractmethod def m(self) -> None: pass +[builtins fixtures/classmethod.pyi] [out] [case testInstantiatingClassWithInheritedAbstractMethodAndSuppression]