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] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy