From 0dfd460c0918e24dbaeb8f0a56c4540811a945a4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 28 Nov 2016 12:14:21 -0800 Subject: [PATCH] Revert "For class variables, lookup type in base classes (#1338, #2022, #2211)" --- mypy/checker.py | 17 +--- mypy/checkexpr.py | 2 +- mypy/checkmember.py | 38 +------- mypy/semanal.py | 28 ++++-- mypy/typevars.py | 24 ----- test-data/unit/check-classes.test | 150 ------------------------------ 6 files changed, 25 insertions(+), 234 deletions(-) delete mode 100644 mypy/typevars.py diff --git a/mypy/checker.py b/mypy/checker.py index ce892abd2b95..26614523538e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -36,17 +36,14 @@ from mypy.sametypes import is_same_type from mypy.messages import MessageBuilder import mypy.checkexpr -from mypy.checkmember import ( - map_type_from_supertype, bind_self, erase_to_bound, find_type_from_bases -) +from mypy.checkmember import map_type_from_supertype, bind_self, erase_to_bound from mypy import messages from mypy.subtypes import ( is_subtype, is_equivalent, is_proper_subtype, is_more_precise, restrict_subtype_away, is_subtype_ignoring_tvars ) from mypy.maptype import map_instance_to_supertype -from mypy.typevars import fill_typevars -from mypy.semanal import set_callable_name, refers_to_fullname +from mypy.semanal import fill_typevars, set_callable_name, refers_to_fullname from mypy.erasetype import erase_typevars from mypy.expandtype import expand_type from mypy.visitor import NodeVisitor @@ -1116,16 +1113,6 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type else: lvalue_type, index_lvalue, inferred = self.check_lvalue(lvalue) if lvalue_type: - if isinstance(lvalue, NameExpr): - base_type = find_type_from_bases(lvalue) - - # If a type is known, validate the type is a subtype of the base type - if base_type: - self.check_subtype(lvalue_type, base_type, lvalue, - messages.INCOMPATIBLE_TYPES_IN_ASSIGNMENT, - 'expression has type', - 'variable has type') - if isinstance(lvalue_type, PartialType) and lvalue_type.type is None: # Try to infer a proper type for a variable with a partial None type. rvalue_type = self.accept(rvalue) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 2125f45b3d28..94db4701b6ff 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -38,7 +38,7 @@ from mypy.checkstrformat import StringFormatterChecker from mypy.expandtype import expand_type from mypy.util import split_module_names -from mypy.typevars import fill_typevars +from mypy.semanal import fill_typevars from mypy import experiments diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 4de316f40845..d1e9ab1cc777 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -7,17 +7,14 @@ Overloaded, TypeVarType, UnionType, PartialType, DeletedType, NoneTyp, TypeType, function_type ) -from mypy.nodes import ( - TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context, MypyFile, MDEF, - NameExpr -) +from mypy.nodes import TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context, MypyFile from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2 from mypy.nodes import Decorator, OverloadedFuncDef from mypy.messages import MessageBuilder from mypy.maptype import map_instance_to_supertype from mypy.expandtype import expand_type_by_instance, expand_type from mypy.infer import infer_type_arguments -from mypy.typevars import fill_typevars, has_no_typevars +from mypy.semanal import fill_typevars from mypy import messages from mypy import subtypes MYPY = False @@ -619,34 +616,3 @@ def erase_to_bound(t: Type): if isinstance(t.item, TypeVarType): return TypeType(t.item.upper_bound) return t - - -def find_type_from_bases(e: NameExpr): - """For a NameExpr that is part of a class, walk all base classes and try - to find the first class that defines a Type for the same name.""" - - expr_node = e.node - if not (isinstance(expr_node, Var) and e.kind == MDEF and - len(expr_node.info.bases) > 0): - return None - - expr_name = expr_node.name() - expr_base = expr_node.info.bases[0] - - for base in expr_node.info.mro[1:]: - base_var = base.names.get(expr_name) - if base_var and base_var.type: - if has_no_typevars(base_var.type): - base_type = base_var.type - else: - itype = map_instance_to_supertype(expr_base, base) - base_type = expand_type_by_instance(base_var.type, itype) - - if isinstance(base_type, CallableType): - # If we are a property, return the Type of the return value, not the Callable - if isinstance(base_var.node, Decorator) and base_var.node.func.is_property: - base_type = base_type.ret_type - elif isinstance(base_var.node, FuncDef) and base_var.node.is_property: - base_type = base_type.ret_type - - return base_type diff --git a/mypy/semanal.py b/mypy/semanal.py index 89ea4e41a806..e8609fdadb1b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -48,7 +48,6 @@ List, Dict, Set, Tuple, cast, TypeVar, Union, Optional, Callable ) -from mypy.checkmember import find_type_from_bases from mypy.nodes import ( MypyFile, TypeInfo, Node, AssignmentStmt, FuncDef, OverloadedFuncDef, ClassDef, Var, GDEF, MODULE_REF, FuncItem, Import, Expression, Lvalue, @@ -68,7 +67,6 @@ IntExpr, FloatExpr, UnicodeExpr, EllipsisExpr, TempNode, COVARIANT, CONTRAVARIANT, INVARIANT, UNBOUND_IMPORTED, LITERAL_YES, ) -from mypy.typevars import has_no_typevars, fill_typevars from mypy.visitor import NodeVisitor from mypy.traverser import TraverserVisitor from mypy.errors import Errors, report_internal_error @@ -81,6 +79,7 @@ from mypy.typeanal import TypeAnalyser, TypeAnalyserPass3, analyze_type_alias from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.sametypes import is_same_type +from mypy.erasetype import erase_typevars from mypy.options import Options from mypy import join @@ -1163,15 +1162,11 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: s.type = self.anal_type(s.type, allow_tuple_literal) else: # For simple assignments, allow binding type aliases. - # Also use the type of the base class if available, or - # set the type if the rvalue is a simple literal. + # Also set the type if the rvalue is a simple literal. if (s.type is None and len(s.lvalues) == 1 and isinstance(s.lvalues[0], NameExpr)): if s.lvalues[0].is_def: - s.type = find_type_from_bases(s.lvalues[0]) - if s.type is None: - s.type = self.analyze_simple_literal_type(s.rvalue) - + s.type = self.analyze_simple_literal_type(s.rvalue) res = analyze_type_alias(s.rvalue, self.lookup_qualified, self.lookup_fully_qualified, @@ -3135,6 +3130,19 @@ def builtin_type(self, name: str, args: List[Type] = None) -> Instance: return Instance(sym.node, args or []) +def fill_typevars(typ: TypeInfo) -> Union[Instance, TupleType]: + """For a non-generic type, return instance type representing the type. + For a generic G type with parameters T1, .., Tn, return G[T1, ..., Tn]. + """ + tv = [] # type: List[Type] + for i in range(len(typ.type_vars)): + tv.append(TypeVarType(typ.defn.type_vars[i])) + inst = Instance(typ, tv) + if typ.tuple_type is None: + return inst + return typ.tuple_type.copy_modified(fallback=inst) + + def replace_implicit_first_type(sig: FunctionLike, new: Type) -> FunctionLike: if isinstance(sig, CallableType): return sig.copy_modified(arg_types=[new] + sig.arg_types[1:]) @@ -3506,3 +3514,7 @@ def find_fixed_callable_return(expr: Expression) -> Optional[CallableType]: if isinstance(t.ret_type, CallableType): return t.ret_type return None + + +def has_no_typevars(typ: Type) -> bool: + return is_same_type(typ, erase_typevars(typ)) diff --git a/mypy/typevars.py b/mypy/typevars.py deleted file mode 100644 index 1bdb1049ebed..000000000000 --- a/mypy/typevars.py +++ /dev/null @@ -1,24 +0,0 @@ -from typing import Union - -from mypy.nodes import TypeInfo - -from mypy.erasetype import erase_typevars -from mypy.sametypes import is_same_type -from mypy.types import Instance, TypeVarType, TupleType, Type - - -def fill_typevars(typ: TypeInfo) -> Union[Instance, TupleType]: - """For a non-generic type, return instance type representing the type. - For a generic G type with parameters T1, .., Tn, return G[T1, ..., Tn]. - """ - tv = [] # type: List[Type] - for i in range(len(typ.type_vars)): - tv.append(TypeVarType(typ.defn.type_vars[i])) - inst = Instance(typ, tv) - if typ.tuple_type is None: - return inst - return typ.tuple_type.copy_modified(fallback=inst) - - -def has_no_typevars(typ: Type) -> bool: - return is_same_type(typ, erase_typevars(typ)) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 6f5acccaa06e..e6f097972372 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2123,153 +2123,3 @@ class B(object, A): # E: Cannot determine consistent method resolution order (MR # flags: --fast-parser class C(metaclass=int()): # E: Dynamic metaclass not supported for 'C' pass - -[case testVariableSubclass] -class A: - a = 1 # type: int -class B(A): - a = 1 -[out] - -[case testVariableSubclassAssignMismatch] -class A: - a = 1 # type: int -class B(A): - a = "a" -[out] -main: note: In class "B": -main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") - -[case testVariableSubclassAssignment] -class A: - a = None # type: int -class B(A): - def __init__(self) -> None: - self.a = "a" -[out] -main: note: In member "__init__" of class "B": -main:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") - -[case testVariableSubclassTypeOverwrite] -class A: - a = None # type: int -class B(A): - a = None # type: str -class C(B): - a = "a" -[out] -main: note: In class "B": -main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") - -[case testVariableSubclassTypeOverwriteImplicit] -class A: - a = 1 -class B(A): - a = None # type: str -[out] -main: note: In class "B": -main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") - -[case testVariableSuperUsage] -class A: - a = [] # type: list -class B(A): - a = [1, 2] -class C(B): - a = B.a + [3] -[builtins fixtures/list.pyi] -[out] - -[case testVariableRvalue] -class A: - a = None -class B(A): - a = 1 -class C(B): - a = "a" -[out] -main: note: In class "A": -main:2: error: Need type annotation for variable -main: note: In class "C": -main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") - -[case testVariableTypeVar] -from typing import TypeVar, Generic -T = TypeVar('T') -class A(Generic[T]): - a = None # type: T -class B(A[int]): - a = 1 - -[case testVariableTypeVarInvalid] -from typing import TypeVar, Generic -T = TypeVar('T') -class A(Generic[T]): - a = None # type: T -class B(A[int]): - a = "abc" -[out] -main: note: In class "B": -main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") - -[case testVariableTypeVarIndirectly] -from typing import TypeVar, Generic -T = TypeVar('T') -class A(Generic[T]): - a = None # type: T -class B(A[int]): - pass -class C(B): - a = "a" -[out] -main: note: In class "C": -main:8: error: Incompatible types in assignment (expression has type "str", variable has type "int") - -[case testVariableTypeVarList] -from typing import List, TypeVar, Generic -T = TypeVar('T') -class A(Generic[T]): - a = None # type: List[T] - b = None # type: List[T] -class B(A[int]): - a = [1] - b = [''] -[builtins fixtures/list.pyi] -[out] -main: note: In class "B": -main:8: error: List item 0 has incompatible type "str" - -[case testVariableMethod] -class A: - def a(self) -> None: pass - b = 1 -class B(A): - a = 1 - def b(self) -> None: pass -[out] -main: note: In class "B": -main:5: error: Incompatible types in assignment (expression has type "int", variable has type Callable[[A], None]) -main:6: error: Signature of "b" incompatible with supertype "A" - -[case testVariableProperty] -class A: - @property - def a(self) -> bool: pass -class B(A): - a = None # type: bool -class C(A): - a = True -class D(A): - a = 1 -[builtins fixtures/property.pyi] -[out] -main: note: In class "D": -main:9: error: Incompatible types in assignment (expression has type "int", variable has type "bool") - -[case testVariableOverwriteAny] -from typing import Any -class A: - a = 1 -class B(A): - a = 'x' # type: Any -[out]