From 5547c843f0dfa012481504e19b09da3b1ed77738 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 28 Oct 2016 00:49:02 +0200 Subject: [PATCH 01/14] For class variables, lookup type in base classes (#1338, #2022, #2211) --- mypy/semanal.py | 15 +++++++- test-data/unit/check-classes.test | 58 +++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 999c847cceb90..67cd922138978 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1161,7 +1161,20 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: if (s.type is None and len(s.lvalues) == 1 and isinstance(s.lvalues[0], NameExpr)): if s.lvalues[0].is_def: - s.type = self.analyze_simple_literal_type(s.rvalue) + if (isinstance(s.lvalues[0].node, Var) and + s.lvalues[0].kind == MDEF): + # Try if any base class already defines a + # type for this class variable. + var_node = s.lvalues[0].node + for base in var_node.info.mro[1:]: + base_var = base.names.get(var_node.name()) + if base_var and base_var.type: + s.type = base_var.type + break + + if s.type is None: + s.type = self.analyze_simple_literal_type(s.rvalue) + res = analyze_type_alias(s.rvalue, self.lookup_qualified, self.lookup_fully_qualified, diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 445bc9900f4c7..a198538cae678 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2055,3 +2055,61 @@ 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 = None +[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] + +[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") From dd2df987f4b6fc3ca6ef6c92ece57b17f7d2b685 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 29 Oct 2016 20:01:03 +0200 Subject: [PATCH 02/14] Ignore TypeVars from base class when looking up types of class variables --- mypy/semanal.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 67cd922138978..70181c2ef25b0 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1169,8 +1169,10 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: for base in var_node.info.mro[1:]: base_var = base.names.get(var_node.name()) if base_var and base_var.type: - s.type = base_var.type - break + # TODO -- Can we resolve/support TypeVars at this stage? + if not isinstance(base_var.type, TypeVarType): + s.type = base_var.type + break if s.type is None: s.type = self.analyze_simple_literal_type(s.rvalue) From d7373ab5025b2ce06981ae1c1c302af49fab7205 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 29 Oct 2016 20:19:27 +0200 Subject: [PATCH 03/14] Break even if the base type is a TypeVar This avoids looking into the base class of a base class while the type has been changed --- mypy/semanal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 70181c2ef25b0..1dc8a03f27b27 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1172,7 +1172,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: # TODO -- Can we resolve/support TypeVars at this stage? if not isinstance(base_var.type, TypeVarType): s.type = base_var.type - break + break if s.type is None: s.type = self.analyze_simple_literal_type(s.rvalue) From 450c12e5bb37be4441c3f6a0d696cfddd41c43ca Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 30 Oct 2016 01:15:47 +0200 Subject: [PATCH 04/14] Support TypeVar when lookup up class variables in a base class --- mypy/semanal.py | 8 ++++++-- test-data/unit/check-classes.test | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 1dc8a03f27b27..886ea0b9bbe30 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -79,6 +79,8 @@ from mypy.sametypes import is_same_type from mypy.erasetype import erase_typevars from mypy.options import Options +from mypy.expandtype import expand_type_by_instance +from mypy.maptype import map_instance_to_supertype T = TypeVar('T') @@ -1169,8 +1171,10 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: for base in var_node.info.mro[1:]: base_var = base.names.get(var_node.name()) if base_var and base_var.type: - # TODO -- Can we resolve/support TypeVars at this stage? - if not isinstance(base_var.type, TypeVarType): + if isinstance(base_var.type, TypeVarType): + itype = map_instance_to_supertype(var_node.info.bases[0], base) + s.type = expand_type_by_instance(base_var.type, itype) + else: s.type = base_var.type break diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index a198538cae678..02f583d7f9019 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2113,3 +2113,22 @@ 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") From 1744b9fa7a72d28f9467200c77dc767c71c71cf9 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 30 Oct 2016 12:15:18 +0100 Subject: [PATCH 05/14] An additional test-case to see if TypeVars are resolved with an extra subclass --- test-data/unit/check-classes.test | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 02f583d7f9019..f87824ba68029 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2132,3 +2132,16 @@ class B(A[int]): [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") From 150361aa518c3ae65326e5dd3afede9ccce83677 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Mon, 31 Oct 2016 19:07:35 +0100 Subject: [PATCH 06/14] Also handle TypeVars in, for example, a List --- mypy/semanal.py | 6 +++--- test-data/unit/check-classes.test | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 886ea0b9bbe30..87ed4628ff45f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1171,11 +1171,11 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: for base in var_node.info.mro[1:]: base_var = base.names.get(var_node.name()) if base_var and base_var.type: - if isinstance(base_var.type, TypeVarType): + if has_no_typevars(base_var.type): + s.type = base_var.type + else: itype = map_instance_to_supertype(var_node.info.bases[0], base) s.type = expand_type_by_instance(base_var.type, itype) - else: - s.type = base_var.type break if s.type is None: diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index f87824ba68029..141ece1fe4bdf 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2145,3 +2145,17 @@ class C(B): [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" From 01224adef9f8d65c6e9503393f4efefa86936782 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 5 Nov 2016 13:08:39 +0100 Subject: [PATCH 07/14] Fix initial test-case --- test-data/unit/check-classes.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 141ece1fe4bdf..2defba2c8d3d9 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2058,9 +2058,9 @@ class C(metaclass=int()): # E: Dynamic metaclass not supported for 'C' [case testVariableSubclass] class A: - a = 1 # type: int + a = 1 # type: int class B(A): - a = None + a = 1 [out] [case testVariableSubclassAssignMismatch] From 8a1469766d40891a8190911277ff0f9db20d8df3 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 5 Nov 2016 15:25:22 +0100 Subject: [PATCH 08/14] Validate if type is defined with an incompatible value against the type of a base class --- mypy/checker.py | 17 +++++++++++-- mypy/checkexpr.py | 2 +- mypy/checkmember.py | 31 +++++++++++++++++++++-- mypy/semanal.py | 41 ++++--------------------------- test-data/unit/check-classes.test | 11 +++++++++ 5 files changed, 61 insertions(+), 41 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 0835133fc112d..b25556cbe8798 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -36,14 +36,17 @@ 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 +from mypy.checkmember import ( + map_type_from_supertype, bind_self, erase_to_bound, find_type_from_bases +) 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.semanal import fill_typevars, set_callable_name, refers_to_fullname +from mypy.typevars import fill_typevars +from mypy.semanal import set_callable_name, refers_to_fullname from mypy.erasetype import erase_typevars from mypy.expandtype import expand_type from mypy.visitor import NodeVisitor @@ -1112,6 +1115,16 @@ 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 6feda60a79791..fca760603a0f6 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -37,7 +37,7 @@ from mypy.checkstrformat import StringFormatterChecker from mypy.expandtype import expand_type from mypy.util import split_module_names -from mypy.semanal import fill_typevars +from mypy.typevars import fill_typevars from mypy import experiments diff --git a/mypy/checkmember.py b/mypy/checkmember.py index b57cb57ba0167..5c420e09440f7 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -7,14 +7,17 @@ Overloaded, TypeVarType, UnionType, PartialType, DeletedType, NoneTyp, TypeType, function_type ) -from mypy.nodes import TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context, MypyFile +from mypy.nodes import ( + TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context, MypyFile, MDEF, + NameExpr +) 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.semanal import fill_typevars +from mypy.typevars import fill_typevars, has_no_typevars from mypy import messages from mypy import subtypes MYPY = False @@ -615,3 +618,27 @@ 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) + + return base_type diff --git a/mypy/semanal.py b/mypy/semanal.py index de5813947176f..3d3cce565dce4 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -47,6 +47,7 @@ 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, @@ -66,6 +67,7 @@ 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 @@ -77,10 +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.expandtype import expand_type_by_instance -from mypy.maptype import map_instance_to_supertype T = TypeVar('T') @@ -1161,25 +1160,12 @@ 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 set the type if the rvalue is a simple literal. + # Also use the type of the base class if available, or + # 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: - if (isinstance(s.lvalues[0].node, Var) and - s.lvalues[0].kind == MDEF): - # Try if any base class already defines a - # type for this class variable. - var_node = s.lvalues[0].node - for base in var_node.info.mro[1:]: - base_var = base.names.get(var_node.name()) - if base_var and base_var.type: - if has_no_typevars(base_var.type): - s.type = base_var.type - else: - itype = map_instance_to_supertype(var_node.info.bases[0], base) - s.type = expand_type_by_instance(base_var.type, itype) - break - + s.type = find_type_from_bases(s.lvalues[0]) if s.type is None: s.type = self.analyze_simple_literal_type(s.rvalue) @@ -3132,19 +3118,6 @@ 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:]) @@ -3516,7 +3489,3 @@ 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/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 2defba2c8d3d9..d07f03c63f315 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2090,6 +2090,17 @@ class B(A): 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: From 08cd7043371ffdeeb9adc9ad6e78c8fcd715814b Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 5 Nov 2016 15:29:50 +0100 Subject: [PATCH 09/14] Additional test case to ensure methods/variables don't overwrite each other --- test-data/unit/check-classes.test | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index d07f03c63f315..a9abb3dc54b9f 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2170,3 +2170,15 @@ class B(A[int]): [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" From afab148902c61e31ba37cfa828453a50c8612703 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 5 Nov 2016 16:09:54 +0100 Subject: [PATCH 10/14] Also support properties --- mypy/checkmember.py | 7 +++++++ test-data/unit/check-classes.test | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 5c420e09440f7..66857e3add88d 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -641,4 +641,11 @@ def find_type_from_bases(e: NameExpr): 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/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index a9abb3dc54b9f..102ae1a2b0c60 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2182,3 +2182,18 @@ class B(A): 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") From 8ab3463be6e903d81e1a51446fe0038e145fc1fd Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 5 Nov 2016 16:13:29 +0100 Subject: [PATCH 11/14] Add missing file --- mypy/typevars.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 mypy/typevars.py diff --git a/mypy/typevars.py b/mypy/typevars.py new file mode 100644 index 0000000000000..1bdb1049ebed8 --- /dev/null +++ b/mypy/typevars.py @@ -0,0 +1,24 @@ +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)) From e20372e627d13de205e613502d308d84d914653e Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 6 Nov 2016 01:28:01 +0100 Subject: [PATCH 12/14] Sync typeshed --- typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typeshed b/typeshed index 5a2a46d3bd43f..cecb64b59fbd3 160000 --- a/typeshed +++ b/typeshed @@ -1 +1 @@ -Subproject commit 5a2a46d3bd43f5fffb9a6216fe6dedf59ce2f7a1 +Subproject commit cecb64b59fbd3b74a4354f2e3156368e92237ec7 From 007b303ec2d215ef455fec408e7d466ee759ba0e Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 8 Nov 2016 08:31:07 +0100 Subject: [PATCH 13/14] Sync typeshed --- typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typeshed b/typeshed index cecb64b59fbd3..970b6d5eb0142 160000 --- a/typeshed +++ b/typeshed @@ -1 +1 @@ -Subproject commit cecb64b59fbd3b74a4354f2e3156368e92237ec7 +Subproject commit 970b6d5eb01424e59d95842243520a51ff2e304e From 3518c9467df2d69c2c116a962c81d2895f38dbc0 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 27 Nov 2016 12:48:40 +0100 Subject: [PATCH 14/14] Add test case to show class variable type can be overwritten with Any --- test-data/unit/check-classes.test | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index e0d8fd6999b09..6f5acccaa06eb 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2265,3 +2265,11 @@ class D(A): [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]