From 3d3eca3abdf79baa81523f6d6ee2cb21f8b63c28 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 21 Oct 2016 01:18:46 +0300 Subject: [PATCH 1/4] kill all references to builtinclass --- mypy/checker.py | 10 +++++++++- mypy/meet.py | 11 +---------- mypy/nodes.py | 4 ---- mypy/semanal.py | 11 ----------- mypy/strconv.py | 2 -- mypy/treetransform.py | 1 - test-data/unit/check-overloading.test | 16 +++++++--------- test-data/unit/semanal-classes.test | 14 -------------- test-data/unit/typexport-basic.test | 2 -- 9 files changed, 17 insertions(+), 54 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index ee0e1b62a2110..acb5a7916a4ce 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -50,7 +50,7 @@ from mypy.visitor import NodeVisitor from mypy.join import join_types from mypy.treetransform import TransformVisitor -from mypy.meet import meet_simple, nearest_builtin_ancestor, is_overlapping_types +from mypy.meet import meet_simple, is_overlapping_types from mypy.binder import ConditionalTypeBinder from mypy.options import Options @@ -2717,3 +2717,11 @@ def is_valid_inferred_type(typ: Type) -> bool: if not is_valid_inferred_type(item): return False return True + + +def nearest_builtin_ancestor(type: TypeInfo) -> TypeInfo: + for base in type.mro: + if base.defn.fullname == 'builtins.object': + return base + else: + return None diff --git a/mypy/meet.py b/mypy/meet.py index 63e3aaee8bce2..18796ae2491cb 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -1,4 +1,4 @@ -from typing import cast, List +from typing import List from mypy.join import is_similar_callables, combine_similar_callables from mypy.types import ( @@ -7,7 +7,6 @@ DeletedType, UninhabitedType, TypeType ) from mypy.subtypes import is_subtype -from mypy.nodes import TypeInfo from mypy import experiments @@ -114,14 +113,6 @@ class C(A, B): ... return True -def nearest_builtin_ancestor(type: TypeInfo) -> TypeInfo: - for base in type.mro: - if base.defn.is_builtinclass: - return base - else: - return None - - class TypeMeetVisitor(TypeVisitor[Type]): def __init__(self, s: Type) -> None: self.s = s diff --git a/mypy/nodes.py b/mypy/nodes.py index 7cc2d44d401ef..36cb0a2a2210b 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -694,8 +694,6 @@ class ClassDef(Statement): info = None # type: TypeInfo # Related TypeInfo metaclass = '' decorators = None # type: List[Expression] - # Built-in/extension class? (single implementation inheritance only) - is_builtinclass = False has_incompatible_baseclass = False def __init__(self, @@ -724,7 +722,6 @@ def serialize(self) -> JsonDict: 'fullname': self.fullname, 'type_vars': [v.serialize() for v in self.type_vars], 'metaclass': self.metaclass, - 'is_builtinclass': self.is_builtinclass, } @classmethod @@ -736,7 +733,6 @@ def deserialize(self, data: JsonDict) -> 'ClassDef': metaclass=data['metaclass'], ) res.fullname = data['fullname'] - res.is_builtinclass = data['is_builtinclass'] return res diff --git a/mypy/semanal.py b/mypy/semanal.py index dccd260b3669d..71d4dda12fe97 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -559,8 +559,6 @@ def visit_class_def(self, defn: ClassDef) -> None: self.enter_class(defn) - self.setup_is_builtinclass(defn) - # Analyze class body. defn.defs.accept(self) @@ -606,15 +604,6 @@ def unbind_class_type_vars(self) -> None: def analyze_class_decorator(self, defn: ClassDef, decorator: Expression) -> None: decorator.accept(self) - def setup_is_builtinclass(self, defn: ClassDef) -> None: - for decorator in defn.decorators: - if refers_to_fullname(decorator, 'typing.builtinclass'): - defn.is_builtinclass = True - if defn.fullname == 'builtins.object': - # Only 'object' is marked as a built-in class, as otherwise things elsewhere - # would break. We need a better way of dealing with built-in classes. - defn.is_builtinclass = True - def calculate_abstract_status(self, typ: TypeInfo) -> None: """Calculate abstract status of a class. diff --git a/mypy/strconv.py b/mypy/strconv.py index bb167f1ef8b67..ba01ea88e628c 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -141,8 +141,6 @@ def visit_class_def(self, o: 'mypy.nodes.ClassDef') -> str: a.insert(1, 'Metaclass({})'.format(o.metaclass)) if o.decorators: a.insert(1, ('Decorators', o.decorators)) - if o.is_builtinclass: - a.insert(1, 'Builtinclass') if o.info and o.info._promote: a.insert(1, 'Promote({})'.format(o.info._promote)) if o.info and o.info.tuple_type: diff --git a/mypy/treetransform.py b/mypy/treetransform.py index c030f0e1e5dc6..31644a5700e8f 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -190,7 +190,6 @@ def visit_class_def(self, node: ClassDef) -> ClassDef: new.info = node.info new.decorators = [self.expr(decorator) for decorator in node.decorators] - new.is_builtinclass = node.is_builtinclass return new def visit_global_decl(self, node: GlobalDecl) -> GlobalDecl: diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index a7e11597c70dc..8fdfda49a71ee 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -411,10 +411,10 @@ A() + B() A() + '' # E: No overload variant of "__add__" of "A" matches argument types [builtins.str] [case testOverrideOverloadedMethodWithMoreGeneralArgumentTypes] -from typing import overload, builtinclass -@builtinclass +from typing import overload + class IntSub(int): pass -@builtinclass + class StrSub(str): pass class A: @overload @@ -429,10 +429,10 @@ class B(A): [out] [case testOverrideOverloadedMethodWithMoreSpecificArgumentTypes] -from typing import overload, builtinclass -@builtinclass +from typing import overload + class IntSub(int): pass -@builtinclass + class StrSub(str): pass class A: @overload @@ -461,12 +461,10 @@ main: note: In class "C": main:17: error: Signature of "f" incompatible with supertype "A" [case testOverloadingAndDucktypeCompatibility] -from typing import overload, _promote, builtinclass +from typing import overload, _promote -@builtinclass class A: pass -@builtinclass @_promote(A) class B: pass diff --git a/test-data/unit/semanal-classes.test b/test-data/unit/semanal-classes.test index 5d04ef6818c5c..431261fa3a182 100644 --- a/test-data/unit/semanal-classes.test +++ b/test-data/unit/semanal-classes.test @@ -518,20 +518,6 @@ MypyFile:1( NameExpr(object [builtins.object])) PassStmt:3())) -[case testBuiltinclassDecorator] -from typing import builtinclass -@builtinclass -class A: pass -[out] -MypyFile:1( - ImportFrom:1(typing, [builtinclass]) - ClassDef:2( - A - Builtinclass - Decorators( - NameExpr(builtinclass [typing.builtinclass])) - PassStmt:3())) - [case testClassAttributeAsMethodDefaultArgumentValue] import typing class A: diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index 2c668f8d557d4..e7915cc60f8ad 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -136,8 +136,6 @@ import typing 8 > 3 4 < 6 > 2 [file builtins.py] -from typing import builtinclass -@builtinclass class object: def __init__(self) -> None: pass class int: From fc97c4b960cf6ddaefc17d46df1ef2609a9a2cc0 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 21 Oct 2016 12:23:55 +0300 Subject: [PATCH 2/4] check for builtinclasses, don't skip tests The class in the test inherits from function to avoid warnings about compatibility of __add__ --- mypy/checker.py | 22 ++++++++++++++++------ test-data/unit/check-classes.test | 8 ++++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index acb5a7916a4ce..abfe979c19de5 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -948,12 +948,12 @@ def visit_class_def(self, defn: ClassDef) -> Type: self.enter_partial_types() old_binder = self.binder self.binder = ConditionalTypeBinder() - with self.binder.top_frame_context(): - self.accept(defn.defs) - self.binder = old_binder if not defn.has_incompatible_baseclass: # Otherwise we've already found errors; more errors are not useful self.check_multiple_inheritance(typ) + with self.binder.top_frame_context(): + self.accept(defn.defs) + self.binder = old_binder self.leave_partial_types() self.errors.pop_type() @@ -2719,9 +2719,19 @@ def is_valid_inferred_type(typ: Type) -> bool: return True +def is_builtin_class(fullname: str): + exceptions = [] # type: List[str] + # TODO: add exceptions to builtins.* ? + *path, name = fullname.split('.') + if path[0] == 'builtins' and name.islower() and name not in exceptions: + return True + # TODO: add builtin classes outside builtins + return False + + def nearest_builtin_ancestor(type: TypeInfo) -> TypeInfo: for base in type.mro: - if base.defn.fullname == 'builtins.object': + if is_builtin_class(base.defn.fullname): return base - else: - return None + # type.mro must contain builtins.object + assert False diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index e63dbce9d6379..4112f27ba015c 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -899,17 +899,17 @@ a.f.x # E: "int" has no attribute "x" -- ------------------------------------------------------- -[case testInvalidMultipleInheritanceFromBuiltins-skip] +[case testInvalidMultipleInheritanceFromBuiltins] import typing -class A(int, str): pass # E: Instance layout conflict in multiple inheritance +class A(int, function): pass # E: Instance layout conflict in multiple inheritance [out] main: note: In class "A": -[case testInvalidMultipleInheritanceFromBuiltins-skip] +[case testInvalidMultipleInheritanceFromBuiltins] import typing class S(str): pass class A(S, str): pass -class B(S, int): pass # E: Instance layout conflict in multiple inheritance +class B(S, function): pass # E: Instance layout conflict in multiple inheritance [out] main: note: In class "B": From b2eaef5e9e464c02b4a7231640445cf6be1c56da Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Sat, 22 Oct 2016 21:04:12 +0300 Subject: [PATCH 3/4] don't check for layout conflict --- mypy/checker.py | 26 -------------------------- mypy/messages.py | 1 - test-data/unit/check-classes.test | 20 -------------------- 3 files changed, 47 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index abfe979c19de5..58991248a2d0e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -959,7 +959,6 @@ def visit_class_def(self, defn: ClassDef) -> Type: def check_multiple_inheritance(self, typ: TypeInfo) -> None: """Check for multiple inheritance related errors.""" - if len(typ.bases) <= 1: # No multiple inheritance. return @@ -973,13 +972,6 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None: # checks suffice (these are implemented elsewhere). if name in base2.names and base2 not in base.mro: self.check_compatibility(name, base, base2, typ) - # Verify that base class layouts are compatible. - builtin_bases = [nearest_builtin_ancestor(base.type) - for base in typ.bases] - for base1 in builtin_bases: - for base2 in builtin_bases: - if not (base1 in base2.mro or base2 in base1.mro): - self.fail(messages.INSTANCE_LAYOUT_CONFLICT, typ) def check_compatibility(self, name: str, base1: TypeInfo, base2: TypeInfo, ctx: Context) -> None: @@ -2717,21 +2709,3 @@ def is_valid_inferred_type(typ: Type) -> bool: if not is_valid_inferred_type(item): return False return True - - -def is_builtin_class(fullname: str): - exceptions = [] # type: List[str] - # TODO: add exceptions to builtins.* ? - *path, name = fullname.split('.') - if path[0] == 'builtins' and name.islower() and name not in exceptions: - return True - # TODO: add builtin classes outside builtins - return False - - -def nearest_builtin_ancestor(type: TypeInfo) -> TypeInfo: - for base in type.mro: - if is_builtin_class(base.defn.fullname): - return base - # type.mro must contain builtins.object - assert False diff --git a/mypy/messages.py b/mypy/messages.py index e39a424fbf406..432f1ddaed7a8 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -76,7 +76,6 @@ 'Overloaded method has both abstract and non-abstract variants' READ_ONLY_PROPERTY_OVERRIDES_READ_WRITE = \ 'Read-only property cannot override read-write property' -INSTANCE_LAYOUT_CONFLICT = 'Instance layout conflict in multiple inheritance' FORMAT_REQUIRES_MAPPING = 'Format requires a mapping' GENERIC_TYPE_NOT_VALID_AS_EXPRESSION = \ "Generic type is prohibited as a runtime expression (use a type alias or '# type:' comment)" diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 4112f27ba015c..4812eaf16ca08 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -894,26 +894,6 @@ a.f = a.f # E: Property "f" defined in "A" is read-only a.f.x # E: "int" has no attribute "x" [builtins fixtures/property.pyi] - --- Multiple inheritance, non-object built-in class as base --- ------------------------------------------------------- - - -[case testInvalidMultipleInheritanceFromBuiltins] -import typing -class A(int, function): pass # E: Instance layout conflict in multiple inheritance -[out] -main: note: In class "A": - -[case testInvalidMultipleInheritanceFromBuiltins] -import typing -class S(str): pass -class A(S, str): pass -class B(S, function): pass # E: Instance layout conflict in multiple inheritance -[out] -main: note: In class "B": - - -- _promote decorators -- ------------------- From ef76c37f8780c5f3bb43b19f6e6b6c61ada43e5b Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Sat, 22 Oct 2016 21:45:52 +0300 Subject: [PATCH 4/4] undo swap --- mypy/checker.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 58991248a2d0e..0d586fa987e77 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -948,12 +948,12 @@ def visit_class_def(self, defn: ClassDef) -> Type: self.enter_partial_types() old_binder = self.binder self.binder = ConditionalTypeBinder() - if not defn.has_incompatible_baseclass: - # Otherwise we've already found errors; more errors are not useful - self.check_multiple_inheritance(typ) with self.binder.top_frame_context(): self.accept(defn.defs) self.binder = old_binder + if not defn.has_incompatible_baseclass: + # Otherwise we've already found errors; more errors are not useful + self.check_multiple_inheritance(typ) self.leave_partial_types() self.errors.pop_type()