From e52f9c5f850ebec3e730c411c9bb5c820424876d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 4 May 2017 11:04:39 +0200 Subject: [PATCH 01/11] Fix NewType crash --- mypy/fixup.py | 2 +- mypy/semanal.py | 4 +++- mypy/typeanal.py | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mypy/fixup.py b/mypy/fixup.py index 993a0949b2f2..62b70185e205 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -153,7 +153,7 @@ def visit_instance(self, inst: Instance) -> None: node = lookup_qualified(self.modules, type_ref, self.quick_and_dirty) if isinstance(node, TypeInfo): inst.type = node - # TODO: Is this needed or redundant? + # TODO: Is this needed or redundant? I think yes, for NewType # Also fix up the bases, just in case. for base in inst.type.bases: if base.type is NOT_READY: diff --git a/mypy/semanal.py b/mypy/semanal.py index 729ed84bcc19..a0ab31821fa3 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3654,6 +3654,8 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.analyze(s.type) if isinstance(s.rvalue, IndexExpr) and isinstance(s.rvalue.analyzed, TypeAliasExpr): self.analyze(s.rvalue.analyzed.type) + if isinstance(s.rvalue, CallExpr) and isinstance(s.rvalue.analyzed, NewTypeExpr): + self.analyze(s.rvalue.analyzed.old_type) super().visit_assignment_stmt(s) def visit_cast_expr(self, e: CastExpr) -> None: @@ -3670,7 +3672,7 @@ def visit_type_application(self, e: TypeApplication) -> None: # Helpers - def analyze(self, type: Type) -> None: + def analyze(self, type: Optional[Type]) -> None: if type: analyzer = TypeAnalyserPass3(self.fail) type.accept(analyzer) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 2a506b7a5378..3b90464c3367 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -610,6 +610,9 @@ def visit_instance(self, t: Instance) -> None: arg, info.name(), tvar.upper_bound), t) for arg in t.args: arg.accept(self) + if info.is_newtype: + for base in info.bases: + base.accept(self) def check_type_var_values(self, type: TypeInfo, actuals: List[Type], valids: List[Type], arg_number: int, context: Context) -> None: From 554c6faa5a6e8d60fa305787043e2f9aa7ac3097 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 4 May 2017 11:38:34 +0200 Subject: [PATCH 02/11] Fix first kind of crashes in TypedDict and NamedTuple --- mypy/nodes.py | 1 + mypy/semanal.py | 28 ++++++++++++++++++++++------ mypy/typeanal.py | 2 ++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 76c94f820750..d4e3f8b7d838 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -730,6 +730,7 @@ class ClassDef(Statement): info = None # type: TypeInfo # Related TypeInfo metaclass = '' # type: Optional[str] decorators = None # type: List[Expression] + analyzed = None # type: Optional[Expression] has_incompatible_baseclass = False def __init__(self, diff --git a/mypy/semanal.py b/mypy/semanal.py index a0ab31821fa3..851d261c2304 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -815,8 +815,10 @@ def analyze_namedtuple_classdef(self, defn: ClassDef) -> bool: if node is not None: node.kind = GDEF # TODO in process_namedtuple_definition also applies here items, types, default_items = self.check_namedtuple_classdef(defn) - node.node = self.build_namedtuple_typeinfo( - defn.name, items, types, default_items) + info = self.build_namedtuple_typeinfo( + defn.name, items, types, default_items) + node.node = info + defn.analyzed = NamedTupleExpr(info) return True return False @@ -1103,7 +1105,9 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> bool: defn.base_type_exprs[0].fullname == 'mypy_extensions.TypedDict'): # Building a new TypedDict fields, types = self.check_typeddict_classdef(defn) - node.node = self.build_typeddict_typeinfo(defn.name, fields, types) + info = self.build_typeddict_typeinfo(defn.name, fields, types) + node.node = info + defn.analyzed = TypedDictExpr(info) return True # Extending/merging existing TypedDicts if any(not isinstance(expr, RefExpr) or @@ -1130,7 +1134,9 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> bool: fields, types = self.check_typeddict_classdef(defn, newfields) newfields.extend(fields) newtypes.extend(types) - node.node = self.build_typeddict_typeinfo(defn.name, newfields, newtypes) + info = self.build_typeddict_typeinfo(defn.name, newfields, newtypes) + node.node = info + defn.analyzed = TypedDictExpr(info) return True return False @@ -3598,6 +3604,11 @@ def visit_class_def(self, tdef: ClassDef) -> None: if tdef.info.mro: tdef.info.mro = [] # Force recomputation calculate_class_mro(tdef, self.fail_blocker) + if tdef.analyzed: + if isinstance(tdef.analyzed, TypedDictExpr): + self.analyze(tdef.analyzed.info.typeddict_type) + if isinstance(tdef.analyzed, NamedTupleExpr): + self.analyze(tdef.analyzed.info.tuple_type) super().visit_class_def(tdef) def visit_decorator(self, dec: Decorator) -> None: @@ -3654,8 +3665,13 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.analyze(s.type) if isinstance(s.rvalue, IndexExpr) and isinstance(s.rvalue.analyzed, TypeAliasExpr): self.analyze(s.rvalue.analyzed.type) - if isinstance(s.rvalue, CallExpr) and isinstance(s.rvalue.analyzed, NewTypeExpr): - self.analyze(s.rvalue.analyzed.old_type) + if isinstance(s.rvalue, CallExpr): + if isinstance(s.rvalue.analyzed, NewTypeExpr): + self.analyze(s.rvalue.analyzed.old_type) + if isinstance(s.rvalue.analyzed, TypedDictExpr): + self.analyze(s.rvalue.analyzed.info.typeddict_type) + if isinstance(s.rvalue.analyzed, NamedTupleExpr): + self.analyze(s.rvalue.analyzed.info.tuple_type) super().visit_assignment_stmt(s) def visit_cast_expr(self, e: CastExpr) -> None: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 3b90464c3367..d076e5109866 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -634,10 +634,12 @@ def visit_callable_type(self, t: CallableType) -> None: def visit_tuple_type(self, t: TupleType) -> None: for item in t.items: item.accept(self) + t.fallback.accept(self) def visit_typeddict_type(self, t: TypedDictType) -> None: for item_type in t.items.values(): item_type.accept(self) + t.fallback.accept(self) def visit_union_type(self, t: UnionType) -> None: for item in t.items: From 590d1da36a57cb893e98b13662ad27394a4ca501 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 4 May 2017 12:01:55 +0200 Subject: [PATCH 03/11] Fix second kind of crashes with TypedDict and NamedTuple --- mypy/semanal.py | 10 +++++----- mypy/typeanal.py | 11 +++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 851d261c2304..b0241ff7fd45 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -90,7 +90,6 @@ from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.sametypes import is_same_type from mypy.options import Options -from mypy import join T = TypeVar('T') @@ -2055,8 +2054,9 @@ def build_namedtuple_typeinfo(self, name: str, items: List[str], types: List[Typ ordereddictype = (self.named_type_or_none('builtins.dict', [strtype, AnyType()]) or self.object_type()) # 'builtins.tuple' has only one type parameter, the corresponding type argument - # in the fallback instance is a join of all item types. - fallback = self.named_type('__builtins__.tuple', [join.join_type_list(types)]) + # in the fallback instance should be a join of all item types. It will be calculated in + # third pass. + fallback = self.named_type('__builtins__.tuple', [AnyType()]) # Note: actual signature should accept an invariant version of Iterable[UnionType[types]]. # but it can't be expressed. 'new' and 'len' should be callable types. iterable_type = self.named_type_or_none('typing.Iterable', [AnyType()]) @@ -2245,9 +2245,9 @@ def fail_typeddict_arg(self, message: str, def build_typeddict_typeinfo(self, name: str, items: List[str], types: List[Type]) -> TypeInfo: - mapping_value_type = join.join_type_list(types) + # The fallback second argument will be calculated in third pass fallback = (self.named_type_or_none('typing.Mapping', - [self.str_type(), mapping_value_type]) + [self.str_type(), AnyType()]) or self.object_type()) info = self.basic_new_typeinfo(name, fallback) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d076e5109866..73930116546a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -26,6 +26,7 @@ from mypy.subtypes import is_subtype from mypy import nodes from mypy import experiments +from mypy import join T = TypeVar('T') @@ -634,12 +635,18 @@ def visit_callable_type(self, t: CallableType) -> None: def visit_tuple_type(self, t: TupleType) -> None: for item in t.items: item.accept(self) - t.fallback.accept(self) + if t.fallback.type.fullname().endswith('.tuple'): + t.fallback.args[0] = join.join_type_list(t.items) + else: + t.fallback.accept(self) def visit_typeddict_type(self, t: TypedDictType) -> None: for item_type in t.items.values(): item_type.accept(self) - t.fallback.accept(self) + if t.fallback.type.fullname().endswith('.Mapping'): + t.fallback.args[1] = join.join_type_list(list(t.items.values())) + else: + t.fallback.accept(self) def visit_union_type(self, t: UnionType) -> None: for item in t.items: From ff786ca4760420c303c4847b379ec4e15d3781f5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 4 May 2017 12:48:31 +0200 Subject: [PATCH 04/11] Fix tuple fallback expansion --- mypy/expandtype.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 18301191948b..759acf3ceef0 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -106,7 +106,11 @@ def visit_overloaded(self, t: Overloaded) -> Type: return Overloaded(items) def visit_tuple_type(self, t: TupleType) -> Type: - return t.copy_modified(items=self.expand_types(t.items)) + expanded = t.copy_modified(items=self.expand_types(t.items)) + if t.fallback.type.fullname().endswith('.tuple'): + # TODO: more precise expansion for fallback? + expanded.fallback = Instance(t.fallback.type, [AnyType()]) + return expanded def visit_typeddict_type(self, t: TypedDictType) -> Type: return t.copy_modified(item_types=self.expand_types(t.items.values())) From e2f42373128fa21a13b2de910a199d74b8f269d9 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 4 May 2017 12:56:53 +0200 Subject: [PATCH 05/11] Fix tuple fallback expansion also for generic aliases --- mypy/types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/types.py b/mypy/types.py index 8e5383311abc..a23cc8d48ba8 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1741,7 +1741,11 @@ def set_typ_args(tp: Type, new_args: List[Type], line: int = -1, column: int = - if isinstance(tp, Instance): return Instance(tp.type, new_args, line, column) if isinstance(tp, TupleType): - return tp.copy_modified(items=new_args) + res = tp.copy_modified(items=new_args) + if tp.fallback.type.fullname().endswith('.tuple'): + # TODO: more precise expansion for fallback? + res.fallback = Instance(tp.fallback.type, [AnyType()]) + return res if isinstance(tp, UnionType): return UnionType(new_args, line, column) if isinstance(tp, CallableType): From 3e0ef985af7be4bc4eb27a7255ae0674a6c234d5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 4 May 2017 13:06:04 +0200 Subject: [PATCH 06/11] Fix lint --- mypy/semanal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index b0241ff7fd45..9a4a7b3bd833 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -814,8 +814,8 @@ def analyze_namedtuple_classdef(self, defn: ClassDef) -> bool: if node is not None: node.kind = GDEF # TODO in process_namedtuple_definition also applies here items, types, default_items = self.check_namedtuple_classdef(defn) - info = self.build_namedtuple_typeinfo( - defn.name, items, types, default_items) + info = self.build_namedtuple_typeinfo(defn.name, items, + types, default_items) node.node = info defn.analyzed = NamedTupleExpr(info) return True From 310efe4e217cb31f99da9ac221ac21b3fd74691f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 4 May 2017 17:49:57 +0200 Subject: [PATCH 07/11] Add tests --- test-data/unit/check-classes.test | 93 +++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 1dd3353ec903..d355b6efd19c 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -3137,3 +3137,96 @@ class M(type): class A(metaclass=M): pass reveal_type(type(A).x) # E: Revealed type is 'builtins.int' + +-- Synthetic types crashes +-- ----------------------- + +[case testCrashInvalidArgsSyntheticClassSyntax] +from typing import List, NamedTuple +from mypy_extensions import TypedDict + +class NM(NamedTuple): + x: List[int, str] # E: "list" expects 1 type argument, but 2 given + +class TD(TypedDict): + x: List[int, str] # E: "list" expects 1 type argument, but 2 given + +# These two should never crash, reveals are in the next test +TD({'x': []}) +NM(x=[]) +[builtins fixtures/dict.pyi] +[out] + +[case testCrashInvalidArgsSyntheticClassSyntaxReveals] +from typing import List, NamedTuple +from mypy_extensions import TypedDict + +class NM(NamedTuple): + x: List[int, str] # E: "list" expects 1 type argument, but 2 given + +class TD(TypedDict): + x: List[int, str] # E: "list" expects 1 type argument, but 2 given + +x: TD +x1 = TD({'x': []}) +y: NM +y1 = NM(x=[]) +reveal_type(x) # E: Revealed type is 'TypedDict(x=builtins.list[Any], _fallback=__main__.TD)' +reveal_type(x1) # E: Revealed type is 'TypedDict(x=builtins.list[Any], _fallback=typing.Mapping[builtins.str, builtins.list[Any]])' +reveal_type(y) # E: Revealed type is 'Tuple[builtins.list[Any], fallback=__main__.NM]' +[builtins fixtures/dict.pyi] +[out] + +[case testCrashInvalidArgsSyntheticFunctionSyntax] +from typing import List, NewType, NamedTuple +from mypy_extensions import TypedDict + +NT = NewType('NT', List[int, str]) # E: "list" expects 1 type argument, but 2 given +NM = NamedTuple('NM', [('x', List[int, str])]) # E: "list" expects 1 type argument, but 2 given +TD = TypedDict('TD', {'x': List[int, str]}) # E: "list" expects 1 type argument, but 2 given + +# These three should not crash +TD({'x': []}) +NM(x=[]) +NT([]) +[builtins fixtures/dict.pyi] +[out] + +[case testCrashForwardSyntheticClassSyntax] +from typing import NamedTuple +from mypy_extensions import TypedDict + +class A1(NamedTuple): + b: 'B' + x: int + +class A2(TypedDict): + b: 'B' + x: int + +class B: + pass + +x: A1 +y: A2 +reveal_type(x.b) # E: Revealed type is '__main__.B' +reveal_type(y['b']) # E: Revealed type is '__main__.B' +[builtins fixtures/dict.pyi] +[out] + +[case testCrashForwardSyntheticFunctionSyntax] +from typing import NamedTuple +from mypy_extensions import TypedDict + +A1 = NamedTuple('A1', [('b', 'B'), ('x', int)]) +A2 = TypedDict('A2', {'b': 'B', 'x': int}) + +class B: + pass + +x: A1 +y: A2 +reveal_type(x.b) # E: Revealed type is '__main__.B' +reveal_type(y['b']) # E: Revealed type is '__main__.B' +[builtins fixtures/dict.pyi] +[out] From 3a14a5365118b240d4f45973cf4d34d0d530da39 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 9 May 2017 13:12:16 +0200 Subject: [PATCH 08/11] Revert some controversial changes --- mypy/expandtype.py | 6 +----- mypy/fixup.py | 2 +- mypy/semanal.py | 10 +++++----- mypy/typeanal.py | 9 --------- mypy/types.py | 6 +----- test-data/unit/check-classes.test | 6 ++++-- 6 files changed, 12 insertions(+), 27 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 759acf3ceef0..18301191948b 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -106,11 +106,7 @@ def visit_overloaded(self, t: Overloaded) -> Type: return Overloaded(items) def visit_tuple_type(self, t: TupleType) -> Type: - expanded = t.copy_modified(items=self.expand_types(t.items)) - if t.fallback.type.fullname().endswith('.tuple'): - # TODO: more precise expansion for fallback? - expanded.fallback = Instance(t.fallback.type, [AnyType()]) - return expanded + return t.copy_modified(items=self.expand_types(t.items)) def visit_typeddict_type(self, t: TypedDictType) -> Type: return t.copy_modified(item_types=self.expand_types(t.items.values())) diff --git a/mypy/fixup.py b/mypy/fixup.py index 62b70185e205..993a0949b2f2 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -153,7 +153,7 @@ def visit_instance(self, inst: Instance) -> None: node = lookup_qualified(self.modules, type_ref, self.quick_and_dirty) if isinstance(node, TypeInfo): inst.type = node - # TODO: Is this needed or redundant? I think yes, for NewType + # TODO: Is this needed or redundant? # Also fix up the bases, just in case. for base in inst.type.bases: if base.type is NOT_READY: diff --git a/mypy/semanal.py b/mypy/semanal.py index 9a51deb427ac..500497ddec0d 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2061,9 +2061,9 @@ def build_namedtuple_typeinfo(self, name: str, items: List[str], types: List[Typ or self.object_type()) # 'builtins.tuple' has only one type parameter. # - # The corresponding type argument in the fallback instance should be a join of - # all item types, but we can't do joins during this pass of semantic analysis - # and we are using Any as a workaround. It will be replaced by a proper type in third pass + # TODO: The corresponding type argument in the fallback instance should be a join of + # all item types, but we can't do joins during this pass of semantic analysis + # and we are using Any as a workaround. fallback = self.named_type('__builtins__.tuple', [AnyType()]) # Note: actual signature should accept an invariant version of Iterable[UnionType[types]]. # but it can't be expressed. 'new' and 'len' should be callable types. @@ -2253,9 +2253,9 @@ def fail_typeddict_arg(self, message: str, def build_typeddict_typeinfo(self, name: str, items: List[str], types: List[Type]) -> TypeInfo: - # The fallback second argument will be calculated in third pass + mapping_value_type = join.join_type_list(types) fallback = (self.named_type_or_none('typing.Mapping', - [self.str_type(), AnyType()]) + [self.str_type(), mapping_value_type]) or self.object_type()) info = self.basic_new_typeinfo(name, fallback) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 73930116546a..3b90464c3367 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -26,7 +26,6 @@ from mypy.subtypes import is_subtype from mypy import nodes from mypy import experiments -from mypy import join T = TypeVar('T') @@ -635,18 +634,10 @@ def visit_callable_type(self, t: CallableType) -> None: def visit_tuple_type(self, t: TupleType) -> None: for item in t.items: item.accept(self) - if t.fallback.type.fullname().endswith('.tuple'): - t.fallback.args[0] = join.join_type_list(t.items) - else: - t.fallback.accept(self) def visit_typeddict_type(self, t: TypedDictType) -> None: for item_type in t.items.values(): item_type.accept(self) - if t.fallback.type.fullname().endswith('.Mapping'): - t.fallback.args[1] = join.join_type_list(list(t.items.values())) - else: - t.fallback.accept(self) def visit_union_type(self, t: UnionType) -> None: for item in t.items: diff --git a/mypy/types.py b/mypy/types.py index a09daf03a5f4..8de44cc69da7 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1741,11 +1741,7 @@ def set_typ_args(tp: Type, new_args: List[Type], line: int = -1, column: int = - if isinstance(tp, Instance): return Instance(tp.type, new_args, line, column) if isinstance(tp, TupleType): - res = tp.copy_modified(items=new_args) - if tp.fallback.type.fullname().endswith('.tuple'): - # TODO: more precise expansion for fallback? - res.fallback = Instance(tp.fallback.type, [AnyType()]) - return res + return tp.copy_modified(items=new_args) if isinstance(tp, UnionType): return UnionType(new_args, line, column) if isinstance(tp, CallableType): diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index d355b6efd19c..22fd2a8838b8 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -3192,7 +3192,9 @@ NT([]) [builtins fixtures/dict.pyi] [out] -[case testCrashForwardSyntheticClassSyntax] +-- The two tests below will not crash after +-- https://github.com/python/mypy/issues/3319 is fixed +[case testCrashForwardSyntheticClassSyntax-skip] from typing import NamedTuple from mypy_extensions import TypedDict @@ -3214,7 +3216,7 @@ reveal_type(y['b']) # E: Revealed type is '__main__.B' [builtins fixtures/dict.pyi] [out] -[case testCrashForwardSyntheticFunctionSyntax] +[case testCrashForwardSyntheticFunctionSyntax-skip] from typing import NamedTuple from mypy_extensions import TypedDict From e30e994a45d4dcf9992644abe753a589379884c5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 9 May 2017 13:16:54 +0200 Subject: [PATCH 09/11] Add accidentally removed import --- mypy/semanal.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index 500497ddec0d..0811c1cec407 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -90,6 +90,7 @@ from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.sametypes import is_same_type from mypy.options import Options +from mypy import join T = TypeVar('T') From 32159cc07451b1a0ba797dab8823a4307a2c48ce Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 24 May 2017 15:30:00 +0200 Subject: [PATCH 10/11] Fix typo in a test comment --- test-data/unit/check-classes.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index dcf6c5319262..6218d32332d4 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -3232,7 +3232,7 @@ reveal_type(y['b']) # E: Revealed type is '__main__.B' [builtins fixtures/dict.pyi] [out] --- Dpecial support for six +-- Special support for six -- ----------------------- [case testSixWithMetaclass] From 70e3f636eeda0f32b416eaedbe680fad3a93dc87 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 24 May 2017 21:35:29 +0200 Subject: [PATCH 11/11] Address review comments --- mypy/nodes.py | 2 +- mypy/semanal.py | 4 ++-- test-data/unit/check-classes.test | 25 +++++++------------------ 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index c74bb9520625..a5cb96007750 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -754,7 +754,7 @@ def is_generic(self) -> bool: return self.info.is_generic() def serialize(self) -> JsonDict: - # Not serialized: defs, base_type_exprs, decorators + # Not serialized: defs, base_type_exprs, decorators, analyzed (for named tuples etc.) return {'.class': 'ClassDef', 'name': self.name, 'fullname': self.fullname, diff --git a/mypy/semanal.py b/mypy/semanal.py index d81c812cd5a1..e79c80a9b1c5 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3691,10 +3691,10 @@ def visit_class_def(self, tdef: ClassDef) -> None: if tdef.info.mro: tdef.info.mro = [] # Force recomputation calculate_class_mro(tdef, self.fail_blocker) - if tdef.analyzed: + if tdef.analyzed is not None: if isinstance(tdef.analyzed, TypedDictExpr): self.analyze(tdef.analyzed.info.typeddict_type) - if isinstance(tdef.analyzed, NamedTupleExpr): + elif isinstance(tdef.analyzed, NamedTupleExpr): self.analyze(tdef.analyzed.info.tuple_type) super().visit_class_def(tdef) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 6218d32332d4..f86b7efffab2 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -3143,12 +3143,10 @@ reveal_type(type(A).x) # E: Revealed type is 'builtins.int' [case testCrashInvalidArgsSyntheticClassSyntax] from typing import List, NamedTuple from mypy_extensions import TypedDict - -class NM(NamedTuple): - x: List[int, str] # E: "list" expects 1 type argument, but 2 given - class TD(TypedDict): x: List[int, str] # E: "list" expects 1 type argument, but 2 given +class NM(NamedTuple): + x: List[int, str] # E: "list" expects 1 type argument, but 2 given # These two should never crash, reveals are in the next test TD({'x': []}) @@ -3159,12 +3157,10 @@ NM(x=[]) [case testCrashInvalidArgsSyntheticClassSyntaxReveals] from typing import List, NamedTuple from mypy_extensions import TypedDict - -class NM(NamedTuple): - x: List[int, str] # E: "list" expects 1 type argument, but 2 given - class TD(TypedDict): x: List[int, str] # E: "list" expects 1 type argument, but 2 given +class NM(NamedTuple): + x: List[int, str] # E: "list" expects 1 type argument, but 2 given x: TD x1 = TD({'x': []}) @@ -3173,16 +3169,16 @@ y1 = NM(x=[]) reveal_type(x) # E: Revealed type is 'TypedDict(x=builtins.list[Any], _fallback=__main__.TD)' reveal_type(x1) # E: Revealed type is 'TypedDict(x=builtins.list[Any], _fallback=typing.Mapping[builtins.str, builtins.list[Any]])' reveal_type(y) # E: Revealed type is 'Tuple[builtins.list[Any], fallback=__main__.NM]' +reveal_type(y1) # E: Revealed type is 'Tuple[builtins.list[Any], fallback=__main__.NM]' [builtins fixtures/dict.pyi] [out] [case testCrashInvalidArgsSyntheticFunctionSyntax] from typing import List, NewType, NamedTuple from mypy_extensions import TypedDict - -NT = NewType('NT', List[int, str]) # E: "list" expects 1 type argument, but 2 given -NM = NamedTuple('NM', [('x', List[int, str])]) # E: "list" expects 1 type argument, but 2 given TD = TypedDict('TD', {'x': List[int, str]}) # E: "list" expects 1 type argument, but 2 given +NM = NamedTuple('NM', [('x', List[int, str])]) # E: "list" expects 1 type argument, but 2 given +NT = NewType('NT', List[int, str]) # E: "list" expects 1 type argument, but 2 given # These three should not crash TD({'x': []}) @@ -3196,18 +3192,14 @@ NT([]) [case testCrashForwardSyntheticClassSyntax-skip] from typing import NamedTuple from mypy_extensions import TypedDict - class A1(NamedTuple): b: 'B' x: int - class A2(TypedDict): b: 'B' x: int - class B: pass - x: A1 y: A2 reveal_type(x.b) # E: Revealed type is '__main__.B' @@ -3218,13 +3210,10 @@ reveal_type(y['b']) # E: Revealed type is '__main__.B' [case testCrashForwardSyntheticFunctionSyntax-skip] from typing import NamedTuple from mypy_extensions import TypedDict - A1 = NamedTuple('A1', [('b', 'B'), ('x', int)]) A2 = TypedDict('A2', {'b': 'B', 'x': int}) - class B: pass - x: A1 y: A2 reveal_type(x.b) # E: Revealed type is '__main__.B'