From b55b5918957d809aab6ec5fe9683a07da10b8a0f Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Thu, 20 Apr 2017 01:06:10 -0700 Subject: [PATCH 1/6] Standardize Type[Union...]] -> Union[Type[...]] * Add more tests for Type[Union[...]] * Force TypeType.make() to be used everywhere instead of TypeType() * Accidentally fixed testTypeUsingTypeCClassMethodUnion --- mypy/checker.py | 9 ++---- mypy/checkexpr.py | 4 +-- mypy/checkmember.py | 4 +-- mypy/erasetype.py | 2 +- mypy/expandtype.py | 2 +- mypy/join.py | 2 +- mypy/meet.py | 2 +- mypy/semanal.py | 2 +- mypy/subtypes.py | 4 +-- mypy/test/testtypes.py | 2 +- mypy/typeanal.py | 6 ++-- mypy/typefixture.py | 12 +++---- mypy/types.py | 28 +++++++++++++++-- test-data/unit/check-classes.test | 7 ++--- test-data/unit/check-isinstance.test | 47 +++++++++++++++++++++++++++- 15 files changed, 98 insertions(+), 35 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 488f3185e897..2c58b5a50637 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -663,7 +663,7 @@ def is_implicit_any(t: Type) -> bool: and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2]): isclass = defn.is_class or defn.name() in ('__new__', '__init_subclass__') if isclass: - ref_type = mypy.types.TypeType(ref_type) + ref_type = mypy.types.TypeType.make(ref_type) erased = erase_to_bound(arg_type) if not is_subtype_ignoring_tvars(ref_type, erased): note = None @@ -2680,13 +2680,10 @@ def convert_to_typetype(type_map: TypeMap) -> TypeMap: if type_map is None: return None for expr, typ in type_map.items(): - if isinstance(typ, UnionType): - converted_type_map[expr] = UnionType([TypeType(t) for t in typ.items]) - elif isinstance(typ, Instance): - converted_type_map[expr] = TypeType(typ) - else: + if not isinstance(typ, (UnionType, Instance)): # unknown type; error was likely reported earlier return {} + converted_type_map[expr] = TypeType.make(typ) return converted_type_map diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 9e44142302fa..e53e553fec72 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -400,7 +400,7 @@ def check_call(self, callee: Type, args: List[Expression], if (callee.is_type_obj() and (len(arg_types) == 1) and is_equivalent(callee.ret_type, self.named_type('builtins.type'))): - callee = callee.copy_modified(ret_type=TypeType(arg_types[0])) + callee = callee.copy_modified(ret_type=TypeType.make(arg_types[0])) if callable_node: # Store the inferred callable type. @@ -1070,7 +1070,7 @@ def analyze_descriptor_access(self, instance_type: Type, descriptor_type: Type, owner_type = instance_type _, inferred_dunder_get_type = self.check_call( - dunder_get_type, [TempNode(instance_type), TempNode(TypeType(owner_type))], + dunder_get_type, [TempNode(instance_type), TempNode(TypeType.make(owner_type))], [nodes.ARG_POS, nodes.ARG_POS], context) if isinstance(inferred_dunder_get_type, AnyType): diff --git a/mypy/checkmember.py b/mypy/checkmember.py index e5a1bdb837b8..b4d9ec4414f9 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -633,7 +633,7 @@ def expand(target: Type) -> Type: ret_type = func.ret_type variables = func.variables if isinstance(original_type, CallableType) and original_type.is_type_obj(): - original_type = TypeType(original_type.ret_type) + original_type = TypeType.make(original_type.ret_type) res = func.copy_modified(arg_types=arg_types, arg_kinds=func.arg_kinds[1:], arg_names=func.arg_names[1:], @@ -648,5 +648,5 @@ def erase_to_bound(t: Type) -> Type: return t.upper_bound if isinstance(t, TypeType): if isinstance(t.item, TypeVarType): - return TypeType(t.item.upper_bound) + return TypeType.make(t.item.upper_bound) return t diff --git a/mypy/erasetype.py b/mypy/erasetype.py index 49035accf976..5670cc0e76e6 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -75,7 +75,7 @@ def visit_union_type(self, t: UnionType) -> Type: return UnionType.make_simplified_union(erased_items) def visit_type_type(self, t: TypeType) -> Type: - return TypeType(t.item.accept(self), line=t.line) + return TypeType.make(t.item.accept(self), line=t.line) def erase_typevars(t: Type, ids_to_erase: Optional[Container[TypeVarId]] = None) -> Type: diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 18301191948b..a1d9714a38ac 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -124,7 +124,7 @@ def visit_type_type(self, t: TypeType) -> Type: # union of instances or Any). Sadly we can't report errors # here yet. item = t.item.accept(self) - return TypeType(item) + return TypeType.make(item) def expand_types(self, types: Iterable[Type]) -> List[Type]: a = [] # type: List[Type] diff --git a/mypy/join.py b/mypy/join.py index b4866bdbc691..25cd3af902db 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -245,7 +245,7 @@ def visit_partial_type(self, t: PartialType) -> Type: def visit_type_type(self, t: TypeType) -> Type: if isinstance(self.s, TypeType): - return TypeType(self.join(t.item, self.s.item), line=t.line) + return TypeType.make(self.join(t.item, self.s.item), line=t.line) elif isinstance(self.s, Instance) and self.s.type.fullname() == 'builtins.type': return self.s else: diff --git a/mypy/meet.py b/mypy/meet.py index feda9bea1854..f30bfe4d3ee7 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -273,7 +273,7 @@ def visit_type_type(self, t: TypeType) -> Type: if isinstance(self.s, TypeType): typ = self.meet(t.item, self.s.item) if not isinstance(typ, NoneTyp): - typ = TypeType(typ, line=t.line) + typ = TypeType.make(typ, line=t.line) return typ elif isinstance(self.s, Instance) and self.s.type.fullname() == 'builtins.type': return t diff --git a/mypy/semanal.py b/mypy/semanal.py index 9668818da15d..f4fc4acc026b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2086,7 +2086,7 @@ def add_method(funcname: str, is_classmethod: bool = False, ) -> None: if is_classmethod: - first = [Argument(Var('cls'), TypeType(selftype), None, ARG_POS)] + first = [Argument(Var('cls'), TypeType.make(selftype), None, ARG_POS)] else: first = [Argument(Var('self'), selftype, None, ARG_POS)] args = first + args diff --git a/mypy/subtypes.py b/mypy/subtypes.py index d122923155bf..9a67005a1118 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -70,9 +70,9 @@ def is_subtype(left: Type, right: Type, # otherwise, fall through # Treat builtins.type the same as Type[Any] elif is_named_instance(left, 'builtins.type'): - return is_subtype(TypeType(AnyType()), right) + return is_subtype(TypeType.make(AnyType()), right) elif is_named_instance(right, 'builtins.type'): - return is_subtype(left, TypeType(AnyType())) + return is_subtype(left, TypeType.make(AnyType())) return left.accept(SubtypeVisitor(right, type_parameter_checker, ignore_pos_arg_names=ignore_pos_arg_names)) diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index a11a82d973a2..338283026e3b 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -580,7 +580,7 @@ def test_type_type(self) -> None: self.assert_join(self.fx.type_b, self.fx.type_any, self.fx.type_any) self.assert_join(self.fx.type_b, self.fx.type_type, self.fx.type_type) self.assert_join(self.fx.type_b, self.fx.type_c, self.fx.type_a) - self.assert_join(self.fx.type_c, self.fx.type_d, TypeType(self.fx.o)) + self.assert_join(self.fx.type_c, self.fx.type_d, TypeType.make(self.fx.o)) self.assert_join(self.fx.type_type, self.fx.type_any, self.fx.type_type) self.assert_join(self.fx.type_b, self.fx.anyt, self.fx.anyt) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 877714581080..89a53d6ab089 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -174,11 +174,11 @@ def visit_unbound_type(self, t: UnboundType) -> Type: return self.analyze_callable_type(t) elif fullname == 'typing.Type': if len(t.args) == 0: - return TypeType(AnyType(), line=t.line) + return TypeType.make(AnyType(), line=t.line) if len(t.args) != 1: self.fail('Type[...] must have exactly one type argument', t) item = self.anal_type(t.args[0]) - return TypeType(item, line=t.line) + return TypeType.make(item, line=t.line) elif fullname == 'typing.ClassVar': if self.nesting_level > 0: self.fail('Invalid type: ClassVar nested inside other type', t) @@ -368,7 +368,7 @@ def visit_ellipsis_type(self, t: EllipsisType) -> Type: return AnyType() def visit_type_type(self, t: TypeType) -> Type: - return TypeType(self.anal_type(t.item), line=t.line) + return TypeType.make(self.anal_type(t.item), line=t.line) def analyze_callable_type(self, t: UnboundType) -> Type: fallback = self.builtin_type('builtins.function') diff --git a/mypy/typefixture.py b/mypy/typefixture.py index 40211b09a52a..a74f30e686ab 100644 --- a/mypy/typefixture.py +++ b/mypy/typefixture.py @@ -145,12 +145,12 @@ def make_type_var(name: str, id: int, values: List[Type], upper_bound: Type, self.lsta = Instance(self.std_listi, [self.a]) # List[A] self.lstb = Instance(self.std_listi, [self.b]) # List[B] - self.type_a = TypeType(self.a) - self.type_b = TypeType(self.b) - self.type_c = TypeType(self.c) - self.type_d = TypeType(self.d) - self.type_t = TypeType(self.t) - self.type_any = TypeType(self.anyt) + self.type_a = TypeType.make(self.a) + self.type_b = TypeType.make(self.b) + self.type_c = TypeType.make(self.c) + self.type_d = TypeType.make(self.d) + self.type_t = TypeType.make(self.t) + self.type_any = TypeType.make(self.anyt) # Helper methods diff --git a/mypy/types.py b/mypy/types.py index 49c3660f19cc..27341e0ae37b 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1162,13 +1162,35 @@ class TypeType(Type): # a generic class instance, a union, Any, a type variable... item = None # type: Type + # this class should not be created directly + # use make method which may return either TypeType or UnionType + # this is to ensure Type[Union[A, B]] is always represented as Union[Type[A], Type[B]] def __init__(self, item: Type, *, line: int = -1, column: int = -1) -> None: + raise NotImplementedError + + def _init(self, item: Type, *, line: int = -1, column: int = -1) -> None: super().__init__(line, column) if isinstance(item, CallableType) and item.is_type_obj(): self.item = item.fallback else: self.item = item + def __new__(cls, *args, **kwargs): # type: ignore + instance = object.__new__(cls) + instance._init(*args, **kwargs) + return instance + + def __copy__(self) -> 'Type': + return TypeType.make(self.item) + + @staticmethod + def make(item: Type, *, line: int = -1, column: int = -1) -> Type: + if isinstance(item, UnionType): + return UnionType([TypeType.make(union_item) for union_item in item.items], + line=line, column=column) + else: + return TypeType.__new__(TypeType, item, line=line, column=column) + def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_type_type(self) @@ -1176,9 +1198,9 @@ def serialize(self) -> JsonDict: return {'.class': 'TypeType', 'item': self.item.serialize()} @classmethod - def deserialize(cls, data: JsonDict) -> 'TypeType': + def deserialize(cls, data: JsonDict) -> Type: assert data['.class'] == 'TypeType' - return TypeType(deserialize_type(data['item'])) + return TypeType.make(deserialize_type(data['item'])) # @@ -1360,7 +1382,7 @@ def visit_overloaded(self, t: Overloaded) -> Type: return Overloaded(items=items) def visit_type_type(self, t: TypeType) -> Type: - return TypeType(t.item.accept(self), line=t.line, column=t.column) + return TypeType.make(t.item.accept(self), line=t.line, column=t.column) class TypeStrVisitor(SyntheticTypeVisitor[str]): diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 6cb42d70eb86..afa20bc9a3a3 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2086,7 +2086,6 @@ def process(cls: Type[User]): [out] [case testTypeUsingTypeCClassMethodUnion] -# Ideally this would work, but not worth the effort; just don't crash from typing import Type, Union class User: @classmethod @@ -2095,11 +2094,11 @@ class User: class ProUser(User): pass class BasicUser(User): pass def process(cls: Type[Union[BasicUser, ProUser]]): - cls.foo() # E: Type[Union[BasicUser, ProUser]] has no attribute "foo" + cls.foo() obj = cls() - cls.bar(obj) # E: Type[Union[BasicUser, ProUser]] has no attribute "bar" + cls.bar(obj) cls.mro() # Defined in class type - cls.error # E: Type[Union[BasicUser, ProUser]] has no attribute "error" + cls.error # E: Some element of union has no attribute "error" [builtins fixtures/classmethod.pyi] [out] diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index f3f4ea429f56..0b9a207ec7ea 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -1443,7 +1443,7 @@ else: [builtins fixtures/isinstancelist.pyi] -[case testIssubclasDestructuringUnions] +[case testIssubclasDestructuringUnions1] from typing import Union, List, Tuple, Dict, Type def f(x: Union[Type[int], Type[str], Type[List]]) -> None: if issubclass(x, (str, (int,))): @@ -1465,6 +1465,51 @@ def f(x: Union[Type[int], Type[str], Type[List]]) -> None: [builtins fixtures/isinstancelist.pyi] +[case testIssubclasDestructuringUnions2] +from typing import Union, List, Tuple, Dict, Type +def f(x: Type[Union[int, str, List]]) -> None: + if issubclass(x, (str, (int,))): + reveal_type(x) # E: Revealed type is 'Union[Type[builtins.int], Type[builtins.str]]' + reveal_type(x()) # E: Revealed type is 'Union[builtins.int, builtins.str]' + x()[1] # E: Value of type "Union[int, str]" is not indexable + else: + reveal_type(x) # E: Revealed type is 'Type[builtins.list]' + reveal_type(x()) # E: Revealed type is 'builtins.list[]' + x()[1] + reveal_type(x) # E: Revealed type is 'Union[Type[builtins.int], Type[builtins.str], Type[builtins.list]]' + reveal_type(x()) # E: Revealed type is 'Union[builtins.int, builtins.str, builtins.list[]]' + if issubclass(x, (str, (list,))): + reveal_type(x) # E: Revealed type is 'Union[Type[builtins.str], Type[builtins.list[Any]]]' + reveal_type(x()) # E: Revealed type is 'Union[builtins.str, builtins.list[]]' + x()[1] + reveal_type(x) # E: Revealed type is 'Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]' + reveal_type(x()) # E: Revealed type is 'Union[builtins.int, builtins.str, builtins.list[]]' +[builtins fixtures/isinstancelist.pyi] + +[case testIssubclasDestructuringUnions3] +from typing import Union, List, Tuple, Dict, Type +def f(x: Type[Union[int, str, List]]) -> None: + reveal_type(x) # E: Revealed type is 'Union[Type[builtins.int], Type[builtins.str], Type[builtins.list]]' + reveal_type(x()) # E: Revealed type is 'Union[builtins.int, builtins.str, builtins.list[]]' + if issubclass(x, (str, (int,))): + reveal_type(x) # E: Revealed type is 'Union[Type[builtins.int], Type[builtins.str]]' + reveal_type(x()) # E: Revealed type is 'Union[builtins.int, builtins.str]' + x()[1] # E: Value of type "Union[int, str]" is not indexable + else: + reveal_type(x) # E: Revealed type is 'Type[builtins.list]' + reveal_type(x()) # E: Revealed type is 'builtins.list[]' + x()[1] + reveal_type(x) # E: Revealed type is 'Union[Type[builtins.int], Type[builtins.str], Type[builtins.list]]' + reveal_type(x()) # E: Revealed type is 'Union[builtins.int, builtins.str, builtins.list[]]' + if issubclass(x, (str, (list,))): + reveal_type(x) # E: Revealed type is 'Union[Type[builtins.str], Type[builtins.list[Any]]]' + reveal_type(x()) # E: Revealed type is 'Union[builtins.str, builtins.list[]]' + x()[1] + reveal_type(x) # E: Revealed type is 'Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]' + reveal_type(x()) # E: Revealed type is 'Union[builtins.int, builtins.str, builtins.list[]]' +[builtins fixtures/isinstancelist.pyi] + + [case testIssubclass] from typing import Type, ClassVar From f024e89b34f37bc1758c00654711721d8a51605a Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Wed, 26 Apr 2017 19:03:17 -0700 Subject: [PATCH 2/6] Apply CR comments --- mypy/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/types.py b/mypy/types.py index 27341e0ae37b..f5b4a8f5b915 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1186,7 +1186,7 @@ def __copy__(self) -> 'Type': @staticmethod def make(item: Type, *, line: int = -1, column: int = -1) -> Type: if isinstance(item, UnionType): - return UnionType([TypeType.make(union_item) for union_item in item.items], + return UnionType.make_union([TypeType.make(union_item) for union_item in item.items], line=line, column=column) else: return TypeType.__new__(TypeType, item, line=line, column=column) From 2a19a2bec2c8af08908e72275de1afed2d65979f Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Thu, 1 Jun 2017 13:05:51 -0700 Subject: [PATCH 3/6] CR: get rid of TypeType.__new__ --- mypy/types.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index feb18e27027a..db75739a6ae7 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1185,31 +1185,23 @@ class TypeType(Type): # This class must be created using make method which may return either TypeType or UnionType. # This is to ensure Type[Union[A, B]] is always represented as Union[Type[A], Type[B]]. - def __init__(self, item: Type, *, line: int = -1, column: int = -1) -> None: - raise NotImplementedError - - def _init(self, item: Type, *, line: int = -1, column: int = -1) -> None: + def __init__(self, item: Union[Instance, AnyType, TypeVarType, TupleType, NoneTyp, + CallableType], *, line: int = -1, column: int = -1) -> None: super().__init__(line, column) if isinstance(item, CallableType) and item.is_type_obj(): self.item = item.fallback else: self.item = item - def __new__(cls, *args, **kwargs): # type: ignore - instance = object.__new__(cls) - instance._init(*args, **kwargs) - return instance - - def __copy__(self) -> 'Type': - return TypeType.make(self.item) - @staticmethod def make(item: Type, *, line: int = -1, column: int = -1) -> Type: if isinstance(item, UnionType): return UnionType.make_union([TypeType.make(union_item) for union_item in item.items], line=line, column=column) + elif isinstance(item, (Instance, AnyType, TypeVarType, TupleType, NoneTyp, CallableType)): + return TypeType(item, line=line, column=column) else: - return TypeType.__new__(TypeType, item, line=line, column=column) + raise RuntimeError('Unexpected item type', type(item)) def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_type_type(self) From d729749b0d1e6234e5e4694e533beefe884d8d8b Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Thu, 1 Jun 2017 13:16:26 -0700 Subject: [PATCH 4/6] CR: Rename .make -> .make_normalized --- mypy/checker.py | 4 ++-- mypy/checkexpr.py | 5 +++-- mypy/checkmember.py | 4 ++-- mypy/erasetype.py | 2 +- mypy/expandtype.py | 2 +- mypy/join.py | 2 +- mypy/meet.py | 2 +- mypy/messages.py | 3 ++- mypy/semanal.py | 2 +- mypy/subtypes.py | 2 +- mypy/test/testtypes.py | 2 +- mypy/typeanal.py | 6 +++--- mypy/typefixture.py | 12 ++++++------ mypy/types.py | 23 ++++++++++++----------- 14 files changed, 37 insertions(+), 34 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 19675fe1ffdd..e740029ad639 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -667,7 +667,7 @@ def is_implicit_any(t: Type) -> bool: and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2]): isclass = defn.is_class or defn.name() in ('__new__', '__init_subclass__') if isclass: - ref_type = mypy.types.TypeType.make(ref_type) + ref_type = mypy.types.TypeType.make_normalized(ref_type) erased = erase_to_bound(arg_type) if not is_subtype_ignoring_tvars(ref_type, erased): note = None @@ -2719,7 +2719,7 @@ def convert_to_typetype(type_map: TypeMap) -> TypeMap: if not isinstance(typ, (UnionType, Instance)): # unknown type; error was likely reported earlier return {} - converted_type_map[expr] = TypeType.make(typ) + converted_type_map[expr] = TypeType.make_normalized(typ) return converted_type_map diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 8cf3ee7ea9c8..9dcb4c322205 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -432,7 +432,7 @@ def check_call(self, callee: Type, args: List[Expression], if (callee.is_type_obj() and (len(arg_types) == 1) and is_equivalent(callee.ret_type, self.named_type('builtins.type'))): - callee = callee.copy_modified(ret_type=TypeType.make(arg_types[0])) + callee = callee.copy_modified(ret_type=TypeType.make_normalized(arg_types[0])) if callable_node: # Store the inferred callable type. @@ -1108,7 +1108,8 @@ def analyze_descriptor_access(self, instance_type: Type, descriptor_type: Type, owner_type = instance_type _, inferred_dunder_get_type = self.check_call( - dunder_get_type, [TempNode(instance_type), TempNode(TypeType.make(owner_type))], + dunder_get_type, + [TempNode(instance_type), TempNode(TypeType.make_normalized(owner_type))], [nodes.ARG_POS, nodes.ARG_POS], context) if isinstance(inferred_dunder_get_type, AnyType): diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 0790dce9a8c6..66949144eed8 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -650,7 +650,7 @@ def expand(target: Type) -> Type: ret_type = func.ret_type variables = func.variables if isinstance(original_type, CallableType) and original_type.is_type_obj(): - original_type = TypeType.make(original_type.ret_type) + original_type = TypeType.make_normalized(original_type.ret_type) res = func.copy_modified(arg_types=arg_types, arg_kinds=func.arg_kinds[1:], arg_names=func.arg_names[1:], @@ -665,5 +665,5 @@ def erase_to_bound(t: Type) -> Type: return t.upper_bound if isinstance(t, TypeType): if isinstance(t.item, TypeVarType): - return TypeType.make(t.item.upper_bound) + return TypeType.make_normalized(t.item.upper_bound) return t diff --git a/mypy/erasetype.py b/mypy/erasetype.py index 5670cc0e76e6..910793b6b9d2 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -75,7 +75,7 @@ def visit_union_type(self, t: UnionType) -> Type: return UnionType.make_simplified_union(erased_items) def visit_type_type(self, t: TypeType) -> Type: - return TypeType.make(t.item.accept(self), line=t.line) + return TypeType.make_normalized(t.item.accept(self), line=t.line) def erase_typevars(t: Type, ids_to_erase: Optional[Container[TypeVarId]] = None) -> Type: diff --git a/mypy/expandtype.py b/mypy/expandtype.py index a1d9714a38ac..937ed8b73a04 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -124,7 +124,7 @@ def visit_type_type(self, t: TypeType) -> Type: # union of instances or Any). Sadly we can't report errors # here yet. item = t.item.accept(self) - return TypeType.make(item) + return TypeType.make_normalized(item) def expand_types(self, types: Iterable[Type]) -> List[Type]: a = [] # type: List[Type] diff --git a/mypy/join.py b/mypy/join.py index 9ef39a7af33a..aaaa99fa3798 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -245,7 +245,7 @@ def visit_partial_type(self, t: PartialType) -> Type: def visit_type_type(self, t: TypeType) -> Type: if isinstance(self.s, TypeType): - return TypeType.make(self.join(t.item, self.s.item), line=t.line) + return TypeType.make_normalized(self.join(t.item, self.s.item), line=t.line) elif isinstance(self.s, Instance) and self.s.type.fullname() == 'builtins.type': return self.s else: diff --git a/mypy/meet.py b/mypy/meet.py index 49895f72ec6d..62940b08d62b 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -278,7 +278,7 @@ def visit_type_type(self, t: TypeType) -> Type: if isinstance(self.s, TypeType): typ = self.meet(t.item, self.s.item) if not isinstance(typ, NoneTyp): - typ = TypeType.make(typ, line=t.line) + typ = TypeType.make_normalized(typ, line=t.line) return typ elif isinstance(self.s, Instance) and self.s.type.fullname() == 'builtins.type': return t diff --git a/mypy/messages.py b/mypy/messages.py index 129d048706ab..37d6a93e1b4d 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -192,7 +192,8 @@ def format(self, typ: Type, verbosity: int = 0) -> str: if func.is_type_obj(): # The type of a type object type can be derived from the # return type (this always works). - return self.format(TypeType.make(erase_type(func.items()[0].ret_type)), verbosity) + return self.format(TypeType.make_normalized(erase_type(func.items()[0].ret_type)), + verbosity) elif isinstance(func, CallableType): return_type = strip_quotes(self.format(func.ret_type)) if func.is_ellipsis_args: diff --git a/mypy/semanal.py b/mypy/semanal.py index 75eb3dd39064..b130804b4b36 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2168,7 +2168,7 @@ def add_method(funcname: str, is_classmethod: bool = False, ) -> None: if is_classmethod: - first = [Argument(Var('cls'), TypeType.make(selftype), None, ARG_POS)] + first = [Argument(Var('cls'), TypeType.make_normalized(selftype), None, ARG_POS)] else: first = [Argument(Var('self'), selftype, None, ARG_POS)] args = first + args diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 6acb67917f27..b03843fba9a4 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -154,7 +154,7 @@ def visit_instance(self, left: Instance) -> bool: if isinstance(item, TupleType): item = item.fallback if is_named_instance(left, 'builtins.type'): - return is_subtype(TypeType.make(AnyType()), right) + return is_subtype(TypeType(AnyType()), right) if left.type.is_metaclass(): if isinstance(item, AnyType): return True diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index c908162c71a2..78fa2e1a2698 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -580,7 +580,7 @@ def test_type_type(self) -> None: self.assert_join(self.fx.type_b, self.fx.type_any, self.fx.type_any) self.assert_join(self.fx.type_b, self.fx.type_type, self.fx.type_type) self.assert_join(self.fx.type_b, self.fx.type_c, self.fx.type_a) - self.assert_join(self.fx.type_c, self.fx.type_d, TypeType.make(self.fx.o)) + self.assert_join(self.fx.type_c, self.fx.type_d, TypeType.make_normalized(self.fx.o)) self.assert_join(self.fx.type_type, self.fx.type_any, self.fx.type_type) self.assert_join(self.fx.type_b, self.fx.anyt, self.fx.anyt) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index b26ecefc2d9e..ef50d40c46b9 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -185,11 +185,11 @@ def visit_unbound_type(self, t: UnboundType) -> Type: return self.analyze_callable_type(t) elif fullname == 'typing.Type': if len(t.args) == 0: - return TypeType.make(AnyType(), line=t.line) + return TypeType(AnyType(), line=t.line) if len(t.args) != 1: self.fail('Type[...] must have exactly one type argument', t) item = self.anal_type(t.args[0]) - return TypeType.make(item, line=t.line) + return TypeType.make_normalized(item, line=t.line) elif fullname == 'typing.ClassVar': if self.nesting_level > 0: self.fail('Invalid type: ClassVar nested inside other type', t) @@ -384,7 +384,7 @@ def visit_ellipsis_type(self, t: EllipsisType) -> Type: return AnyType() def visit_type_type(self, t: TypeType) -> Type: - return TypeType.make(self.anal_type(t.item), line=t.line) + return TypeType.make_normalized(self.anal_type(t.item), line=t.line) def analyze_callable_type(self, t: UnboundType) -> Type: fallback = self.builtin_type('builtins.function') diff --git a/mypy/typefixture.py b/mypy/typefixture.py index 6a27dd92231d..87ddd06d3ecc 100644 --- a/mypy/typefixture.py +++ b/mypy/typefixture.py @@ -145,12 +145,12 @@ def make_type_var(name: str, id: int, values: List[Type], upper_bound: Type, self.lsta = Instance(self.std_listi, [self.a]) # List[A] self.lstb = Instance(self.std_listi, [self.b]) # List[B] - self.type_a = TypeType.make(self.a) - self.type_b = TypeType.make(self.b) - self.type_c = TypeType.make(self.c) - self.type_d = TypeType.make(self.d) - self.type_t = TypeType.make(self.t) - self.type_any = TypeType.make(self.anyt) + self.type_a = TypeType.make_normalized(self.a) + self.type_b = TypeType.make_normalized(self.b) + self.type_c = TypeType.make_normalized(self.c) + self.type_d = TypeType.make_normalized(self.d) + self.type_t = TypeType.make_normalized(self.t) + self.type_any = TypeType.make_normalized(self.anyt) # Helper methods diff --git a/mypy/types.py b/mypy/types.py index db75739a6ae7..79cc29363275 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1183,10 +1183,11 @@ class TypeType(Type): # a generic class instance, a union, Any, a type variable... item = None # type: Type - # This class must be created using make method which may return either TypeType or UnionType. - # This is to ensure Type[Union[A, B]] is always represented as Union[Type[A], Type[B]]. def __init__(self, item: Union[Instance, AnyType, TypeVarType, TupleType, NoneTyp, CallableType], *, line: int = -1, column: int = -1) -> None: + """To ensure Type[Union[A, B]] is always represented as Union[Type[A], Type[B]], item of + type UnionType must be handled through make_normalized static method. + """ super().__init__(line, column) if isinstance(item, CallableType) and item.is_type_obj(): self.item = item.fallback @@ -1194,14 +1195,14 @@ def __init__(self, item: Union[Instance, AnyType, TypeVarType, TupleType, NoneTy self.item = item @staticmethod - def make(item: Type, *, line: int = -1, column: int = -1) -> Type: + def make_normalized(item: Type, *, line: int = -1, column: int = -1) -> Type: if isinstance(item, UnionType): - return UnionType.make_union([TypeType.make(union_item) for union_item in item.items], - line=line, column=column) - elif isinstance(item, (Instance, AnyType, TypeVarType, TupleType, NoneTyp, CallableType)): - return TypeType(item, line=line, column=column) - else: - raise RuntimeError('Unexpected item type', type(item)) + return UnionType.make_union( + [TypeType.make_normalized(union_item) for union_item in item.items], + line=line, column=column + ) + assert isinstance(item, (Instance, AnyType, TypeVarType, TupleType, NoneTyp, CallableType)) + return TypeType(item, line=line, column=column) def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_type_type(self) @@ -1212,7 +1213,7 @@ def serialize(self) -> JsonDict: @classmethod def deserialize(cls, data: JsonDict) -> Type: assert data['.class'] == 'TypeType' - return TypeType.make(deserialize_type(data['item'])) + return TypeType.make_normalized(deserialize_type(data['item'])) # @@ -1389,7 +1390,7 @@ def visit_overloaded(self, t: Overloaded) -> Type: return Overloaded(items=items) def visit_type_type(self, t: TypeType) -> Type: - return TypeType.make(t.item.accept(self), line=t.line, column=t.column) + return TypeType.make_normalized(t.item.accept(self), line=t.line, column=t.column) class TypeStrVisitor(SyntheticTypeVisitor[str]): From 1f557d52bf0409a8981680ed21581345df6aee88 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Thu, 1 Jun 2017 13:40:01 -0700 Subject: [PATCH 5/6] Replace assert with type ignore --- mypy/types.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index 79cc29363275..363afde6ffe1 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1201,8 +1201,7 @@ def make_normalized(item: Type, *, line: int = -1, column: int = -1) -> Type: [TypeType.make_normalized(union_item) for union_item in item.items], line=line, column=column ) - assert isinstance(item, (Instance, AnyType, TypeVarType, TupleType, NoneTyp, CallableType)) - return TypeType(item, line=line, column=column) + return TypeType(item, line=line, column=column) # type: ignore def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_type_type(self) From 7adac2890c8dfb4aae07ee648b5a05abec2682a3 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Sat, 3 Jun 2017 03:04:51 -0700 Subject: [PATCH 6/6] Revert typeshed update --- typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typeshed b/typeshed index 8b835f95001b..be80c368161e 160000 --- a/typeshed +++ b/typeshed @@ -1 +1 @@ -Subproject commit 8b835f95001b61734c6b147d3aa6eb4fbe7bce03 +Subproject commit be80c368161eeace47d93eb9908ddda1aacf3b86