def visit_instance(self, t: Instance) -> Type: if isinstance(self.s, Instance): si = self.s if t.type == si.type: if is_subtype(t, self.s) or is_subtype(self.s, t): # Combine type arguments. We could have used join below # equivalently. args = [] # type: List[Type] for i in range(len(t.args)): args.append(self.meet(t.args[i], si.args[i])) return Instance(t.type, args) else: if experiments.STRICT_OPTIONAL: return UninhabitedType() else: return NoneTyp() else: if is_subtype(t, self.s): return t elif is_subtype(self.s, t): # See also above comment. return self.s else: if experiments.STRICT_OPTIONAL: return UninhabitedType() else: return NoneTyp() elif isinstance(self.s, TypeType): return meet_types(t, self.s) elif isinstance(self.s, TupleType): return meet_types(t, self.s) else: return self.default(self.s)
def narrow_declared_type(declared: Type, narrowed: Type) -> Type: """Return the declared type narrowed down to another type.""" if declared == narrowed: return declared if isinstance(declared, UnionType): return UnionType.make_simplified_union([ narrow_declared_type(x, narrowed) for x in declared.relevant_items() ]) elif not is_overlapping_types( declared, narrowed, prohibit_none_typevar_overlap=True): if state.strict_optional: return UninhabitedType() else: return NoneTyp() elif isinstance(narrowed, UnionType): return UnionType.make_simplified_union([ narrow_declared_type(declared, x) for x in narrowed.relevant_items() ]) elif isinstance(narrowed, AnyType): return narrowed elif isinstance(declared, (Instance, TupleType)): return meet_types(declared, narrowed) elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType): return TypeType.make_normalized( narrow_declared_type(declared.item, narrowed.item)) return narrowed
def true_only(t: Type) -> ProperType: """ Restricted version of t with only True-ish values """ t = get_proper_type(t) if not t.can_be_true: # All values of t are False-ish, so there are no true values in it return UninhabitedType(line=t.line, column=t.column) elif not t.can_be_false: # All values of t are already True-ish, so true_only is idempotent in this case return t elif isinstance(t, UnionType): # The true version of a union type is the union of the true versions of its components new_items = [true_only(item) for item in t.items] can_be_true_items = [item for item in new_items if item.can_be_true] return make_simplified_union(can_be_true_items, line=t.line, column=t.column) else: ret_type = _get_type_special_method_bool_ret_type(t) if ret_type and ret_type.can_be_false and not ret_type.can_be_true: new_t = copy_type(t) new_t.can_be_true = False return new_t new_t = copy_type(t) new_t.can_be_false = False return new_t
def visit_starred_pattern(self, o: StarredPattern) -> PatternType: captures: Dict[Expression, Type] = {} if o.capture is not None: list_type = self.chk.named_generic_type('builtins.list', [self.type_context[-1]]) captures[o.capture] = list_type return PatternType(self.type_context[-1], UninhabitedType(), captures)
def test_false_only_tuple(self) -> None: with strict_optional_set(False): fo = false_only(self.tuple(self.fx.a)) assert_equal(fo, NoneType()) with strict_optional_set(True): fo = false_only(self.tuple(self.fx.a)) assert_equal(fo, UninhabitedType())
def solve_constraints(vars: List[TypeVarId], constraints: List[Constraint], strict: bool =True) -> List[Optional[Type]]: """Solve type constraints. Return the best type(s) for type variables; each type can be None if the value of the variable could not be solved. If a variable has no constraints, if strict=True then arbitrarily pick NoneTyp as the value of the type variable. If strict=False, pick AnyType. """ # Collect a list of constraints for each type variable. cmap = defaultdict(list) # type: Dict[TypeVarId, List[Constraint]] for con in constraints: cmap[con.type_var].append(con) res = [] # type: List[Optional[Type]] # Solve each type variable separately. for tvar in vars: bottom = None # type: Optional[Type] top = None # type: Optional[Type] candidate = None # type: Optional[Type] # Process each constraint separately, and calculate the lower and upper # bounds based on constraints. Note that we assume that the constraint # targets do not have constraint references. for c in cmap.get(tvar, []): if c.op == SUPERTYPE_OF: if bottom is None: bottom = c.target else: bottom = join_types(bottom, c.target) else: if top is None: top = c.target else: top = meet_types(top, c.target) if isinstance(top, AnyType) or isinstance(bottom, AnyType): res.append(AnyType()) continue elif bottom is None: if top: candidate = top else: # No constraints for type variable -- 'UninhabitedType' is the most specific type. if strict: candidate = UninhabitedType() else: candidate = AnyType() elif top is None: candidate = bottom elif is_subtype(bottom, top): candidate = bottom else: candidate = None res.append(candidate) return res
def false_only(t: Type) -> ProperType: """ Restricted version of t with only False-ish values """ t = get_proper_type(t) if not t.can_be_false: if state.strict_optional: # All values of t are True-ish, so there are no false values in it return UninhabitedType(line=t.line) else: # When strict optional checking is disabled, everything can be # False-ish since anything can be None return NoneType(line=t.line) elif not t.can_be_true: # All values of t are already False-ish, so false_only is idempotent in this case return t elif isinstance(t, UnionType): # The false version of a union type is the union of the false versions of its components new_items = [false_only(item) for item in t.items] can_be_false_items = [item for item in new_items if item.can_be_false] return make_simplified_union(can_be_false_items, line=t.line, column=t.column) else: ret_type = _get_type_special_method_bool_ret_type(t) if ret_type and ret_type.can_be_true and not ret_type.can_be_false: new_t = copy_type(t) new_t.can_be_false = False return new_t new_t = copy_type(t) new_t.can_be_true = False return new_t
def visit_mapping_pattern(self, o: MappingPattern) -> PatternType: current_type = get_proper_type(self.type_context[-1]) can_match = True captures: Dict[Expression, Type] = {} for key, value in zip(o.keys, o.values): inner_type = self.get_mapping_item_type(o, current_type, key) if inner_type is None: can_match = False inner_type = self.chk.named_type("builtins.object") pattern_type = self.accept(value, inner_type) if is_uninhabited(pattern_type.type): can_match = False else: self.update_type_map(captures, pattern_type.captures) if o.rest is not None: mapping = self.chk.named_type("typing.Mapping") if is_subtype(current_type, mapping) and isinstance(current_type, Instance): mapping_inst = map_instance_to_supertype(current_type, mapping.type) dict_typeinfo = self.chk.lookup_typeinfo("builtins.dict") rest_type = Instance(dict_typeinfo, mapping_inst.args) else: object_type = self.chk.named_type("builtins.object") rest_type = self.chk.named_generic_type("builtins.dict", [object_type, object_type]) captures[o.rest] = rest_type if can_match: # We can't narrow the type here, as Mapping key is invariant. new_type = self.type_context[-1] else: new_type = UninhabitedType() return PatternType(new_type, current_type, captures)
def narrow_declared_type(declared: Type, narrowed: Type) -> Type: """Return the declared type narrowed down to another type.""" # TODO: check infinite recursion for aliases here. declared = get_proper_type(declared) narrowed = get_proper_type(narrowed) if declared == narrowed: return declared if isinstance(declared, UnionType): return make_simplified_union([narrow_declared_type(x, narrowed) for x in declared.relevant_items()]) elif not is_overlapping_types(declared, narrowed, prohibit_none_typevar_overlap=True): if state.strict_optional: return UninhabitedType() else: return NoneType() elif isinstance(narrowed, UnionType): return make_simplified_union([narrow_declared_type(declared, x) for x in narrowed.relevant_items()]) elif isinstance(narrowed, AnyType): return narrowed elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType): return TypeType.make_normalized(narrow_declared_type(declared.item, narrowed.item)) elif isinstance(declared, (Instance, TupleType, TypeType, LiteralType)): return meet_types(declared, narrowed) elif isinstance(declared, TypedDictType) and isinstance(narrowed, Instance): # Special case useful for selecting TypedDicts from unions using isinstance(x, dict). if (narrowed.type.fullname == 'builtins.dict' and all(isinstance(t, AnyType) for t in get_proper_types(narrowed.args))): return declared return meet_types(declared, narrowed) return narrowed
def narrow_declared_type(declared: Type, narrowed: Type) -> Type: """Return the declared type narrowed down to another type.""" if declared == narrowed: return declared if isinstance(declared, UnionType): return UnionType.make_simplified_union([ narrow_declared_type(x, narrowed) for x in declared.relevant_items() ]) elif not is_overlapping_types(declared, narrowed, use_promotions=True): if experiments.STRICT_OPTIONAL: return UninhabitedType() else: return NoneTyp() elif isinstance(narrowed, UnionType): return UnionType.make_simplified_union([ narrow_declared_type(declared, x) for x in narrowed.relevant_items() ]) elif isinstance(narrowed, AnyType): return narrowed elif isinstance(declared, (Instance, TupleType)): return meet_types(declared, narrowed) elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType): return TypeType.make_normalized( narrow_declared_type(declared.item, narrowed.item)) return narrowed
def visit_instance(self, t: Instance) -> ProperType: if isinstance(self.s, Instance): si = self.s if t.type == si.type: if is_subtype(t, self.s) or is_subtype(self.s, t): # Combine type arguments. We could have used join below # equivalently. args = [] # type: List[Type] # N.B: We use zip instead of indexing because the lengths might have # mismatches during daemon reprocessing. for ta, sia in zip(t.args, si.args): args.append(self.meet(ta, sia)) return Instance(t.type, args) else: if state.strict_optional: return UninhabitedType() else: return NoneType() else: if is_subtype(t, self.s): return t elif is_subtype(self.s, t): # See also above comment. return self.s else: if state.strict_optional: return UninhabitedType() else: return NoneType() elif isinstance(self.s, FunctionLike) and t.type.is_protocol: call = unpack_callback_protocol(t) if call: return meet_types(call, self.s) elif isinstance(self.s, FunctionLike) and self.s.is_type_obj( ) and t.type.is_metaclass(): if is_subtype(self.s.fallback, t): return self.s return self.default(self.s) elif isinstance(self.s, TypeType): return meet_types(t, self.s) elif isinstance(self.s, TupleType): return meet_types(t, self.s) elif isinstance(self.s, LiteralType): return meet_types(t, self.s) elif isinstance(self.s, TypedDictType): return meet_types(t, self.s) return self.default(self.s)
def default(self, typ: Type) -> Type: if isinstance(typ, UnboundType): return AnyType(TypeOfAny.special_form) else: if experiments.STRICT_OPTIONAL: return UninhabitedType() else: return NoneTyp()
def get_non_partial_lvalue_type(self, lvalue: RefExpr) -> Type: if lvalue not in self.type_map: # Likely a block considered unreachable during type checking. return UninhabitedType() lvalue_type = self.type_map[lvalue] if isinstance(lvalue_type, PartialType): if isinstance(lvalue.node, Var) and lvalue.node.type: lvalue_type = lvalue.node.type else: # Probably a secondary, non-definition assignment that doesn't # result in a non-partial type. We won't be able to infer any # dependencies from this so just return something. (The first, # definition assignment with a partial type is handled # differently, in the semantic analyzer.) assert not lvalue.is_new_def return UninhabitedType() return lvalue_type
def default(self, typ: Type) -> ProperType: if isinstance(typ, UnboundType): return AnyType(TypeOfAny.special_form) else: if state.strict_optional: return UninhabitedType() else: return NoneType()
def visit_instance(self, t: Instance) -> ProperType: if isinstance(self.s, Instance): si = self.s if t.type == si.type: if is_subtype(t, self.s) or is_subtype(self.s, t): # Combine type arguments. We could have used join below # equivalently. args = [] # type: List[Type] for i in range(len(t.args)): args.append(self.meet(t.args[i], si.args[i])) return Instance(t.type, args) else: if state.strict_optional: return UninhabitedType() else: return NoneType() else: if is_subtype(t, self.s): return t elif is_subtype(self.s, t): # See also above comment. return self.s else: if state.strict_optional: return UninhabitedType() else: return NoneType() elif isinstance(self.s, FunctionLike) and t.type.is_protocol: call = unpack_callback_protocol(t) if call: return meet_types(call, self.s) elif isinstance(self.s, FunctionLike) and self.s.is_type_obj( ) and t.type.is_metaclass(): if is_subtype(self.s.fallback, t): return self.s return self.default(self.s) elif isinstance(self.s, TypeType): return meet_types(t, self.s) elif isinstance(self.s, TupleType): return meet_types(t, self.s) elif isinstance(self.s, LiteralType): return meet_types(t, self.s) elif isinstance(self.s, TypedDictType): return meet_types(t, self.s) return self.default(self.s)
def visit_none_type(self, t: NoneType) -> Type: if state.strict_optional: if isinstance(self.s, NoneType) or (isinstance(self.s, Instance) and self.s.type.fullname() == 'builtins.object'): return t else: return UninhabitedType() else: return t
def visit_none_type(self, t: NoneTyp) -> Type: if experiments.STRICT_OPTIONAL: if isinstance(self.s, NoneTyp) or (isinstance(self.s, Instance) and self.s.type.fullname() == 'builtins.object'): return t else: return UninhabitedType() else: return t
def join_type_list(types: List[Type]) -> Type: if not types: # This is a little arbitrary but reasonable. Any empty tuple should be compatible # with all variable length tuples, and this makes it possible. return UninhabitedType() joined = types[0] for t in types[1:]: joined = join_types(joined, t) return joined
def default(self, typ): if isinstance(typ, UnboundType): return AnyType() elif isinstance(typ, Void) or isinstance(typ, ErrorType): return ErrorType() else: if experiments.STRICT_OPTIONAL: return UninhabitedType() else: return NoneTyp()
def trivial_meet(s: Type, t: Type) -> ProperType: """Return one of types (expanded) if it is a subtype of other, otherwise bottom type.""" if is_subtype(s, t): return get_proper_type(s) elif is_subtype(t, s): return get_proper_type(t) else: if state.strict_optional: return UninhabitedType() else: return NoneType()
def narrow_declared_type(declared: Type, narrowed: Type) -> Type: """Return the declared type narrowed down to another type.""" # TODO: check infinite recursion for aliases here. if isinstance(narrowed, TypeGuardedType): # type: ignore[misc] # A type guard forces the new type even if it doesn't overlap the old. return narrowed.type_guard declared = get_proper_type(declared) narrowed = get_proper_type(narrowed) if declared == narrowed: return declared if isinstance(declared, UnionType): return make_simplified_union([ narrow_declared_type(x, narrowed) for x in declared.relevant_items() ]) if is_enum_overlapping_union(declared, narrowed): return narrowed elif not is_overlapping_types( declared, narrowed, prohibit_none_typevar_overlap=True): if state.strict_optional: return UninhabitedType() else: return NoneType() elif isinstance(narrowed, UnionType): return make_simplified_union([ narrow_declared_type(declared, x) for x in narrowed.relevant_items() ]) elif isinstance(narrowed, AnyType): return narrowed elif isinstance(narrowed, TypeVarType) and is_subtype( narrowed.upper_bound, declared): return narrowed elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType): return TypeType.make_normalized( narrow_declared_type(declared.item, narrowed.item)) elif (isinstance(declared, TypeType) and isinstance(narrowed, Instance) and narrowed.type.is_metaclass()): # We'd need intersection types, so give up. return declared elif isinstance(declared, (Instance, TupleType, TypeType, LiteralType)): return meet_types(declared, narrowed) elif isinstance(declared, TypedDictType) and isinstance( narrowed, Instance): # Special case useful for selecting TypedDicts from unions using isinstance(x, dict). if (narrowed.type.fullname == 'builtins.dict' and all( isinstance(t, AnyType) for t in get_proper_types(narrowed.args))): return declared return meet_types(declared, narrowed) return narrowed
def join_type_list(types: List[Type]) -> Type: if not types: # This is a little arbitrary but reasonable. Any empty tuple should be compatible # with all variable length tuples, and this makes it possible. A better approach # would be to use a special bottom type, which we do when strict Optional # checking is enabled. if experiments.STRICT_OPTIONAL: return UninhabitedType() else: return NoneTyp() joined = types[0] for t in types[1:]: joined = join_types(joined, t) return joined
def visit_or_pattern(self, o: OrPattern) -> PatternType: current_type = self.type_context[-1] # # Check all the subpatterns # pattern_types = [] for pattern in o.patterns: pattern_type = self.accept(pattern, current_type) pattern_types.append(pattern_type) current_type = pattern_type.rest_type # # Collect the final type # types = [] for pattern_type in pattern_types: if not is_uninhabited(pattern_type.type): types.append(pattern_type.type) # # Check the capture types # capture_types: Dict[Var, List[Tuple[Expression, Type]]] = defaultdict(list) # Collect captures from the first subpattern for expr, typ in pattern_types[0].captures.items(): node = get_var(expr) capture_types[node].append((expr, typ)) # Check if other subpatterns capture the same names for i, pattern_type in enumerate(pattern_types[1:]): vars = {get_var(expr) for expr, _ in pattern_type.captures.items()} if capture_types.keys() != vars: self.msg.fail(message_registry.OR_PATTERN_ALTERNATIVE_NAMES, o.patterns[i]) for expr, typ in pattern_type.captures.items(): node = get_var(expr) capture_types[node].append((expr, typ)) captures: Dict[Expression, Type] = {} for var, capture_list in capture_types.items(): typ = UninhabitedType() for _, other in capture_list: typ = join_types(typ, other) captures[capture_list[0][0]] = typ union_type = make_simplified_union(types) return PatternType(union_type, current_type, captures)
def visit_as_pattern(self, o: AsPattern) -> PatternType: current_type = self.type_context[-1] if o.pattern is not None: pattern_type = self.accept(o.pattern, current_type) typ, rest_type, type_map = pattern_type else: typ, rest_type, type_map = current_type, UninhabitedType(), {} if not is_uninhabited(typ) and o.name is not None: typ, _ = self.chk.conditional_types_with_intersection( current_type, [get_type_range(typ)], o, default=current_type) if not is_uninhabited(typ): type_map[o.name] = typ return PatternType(typ, rest_type, type_map)
def meet_simple(s: Type, t: Type, default_right: bool = True) -> Type: if s == t: return s if isinstance(s, UnionType): return UnionType.make_simplified_union([meet_types(x, t) for x in s.items]) elif not is_overlapping_types(s, t, use_promotions=True): if experiments.STRICT_OPTIONAL: return UninhabitedType() else: return NoneTyp() else: if default_right: return t else: return s
def _analyze_iterable_item_type(self, expr: Expression) -> Type: """Return the item type given by 'expr' in an iterable context.""" # This logic is copied from mypy's TypeChecker.analyze_iterable_item_type. iterable = get_proper_type(self.types[expr]) echk = self.graph[self.module_name].type_checker().expr_checker iterator = echk.check_method_call_by_name('__iter__', iterable, [], [], expr)[0] from mypy.join import join_types if isinstance(iterable, TupleType): joined = UninhabitedType() # type: Type for item in iterable.items: joined = join_types(joined, item) return joined else: # Non-tuple iterable. return echk.check_method_call_by_name('__next__', iterator, [], [], expr)[0]
def visit_tuple_type(self, t: TupleType) -> Type: items = [] for item in t.items: proper_item = get_proper_type(item) if isinstance(proper_item, UnpackType): unpacked_items = self.expand_unpack(proper_item) if unpacked_items is None: # TODO: better error, something like tuple of unknown? return UninhabitedType() elif isinstance(unpacked_items, Instance): if len(t.items) == 1: return unpacked_items else: assert False, "Invalid unpack of variable length tuple" elif isinstance(unpacked_items, AnyType): return unpacked_items else: items.extend(unpacked_items) else: items.append(proper_item.accept(self)) return t.copy_modified(items=items)
def solve_constraints(vars: List[TypeVarId], constraints: List[Constraint], strict: bool =True) -> List[Optional[Type]]: """Solve type constraints. Return the best type(s) for type variables; each type can be None if the value of the variable could not be solved. If a variable has no constraints, if strict=True then arbitrarily pick NoneTyp as the value of the type variable. If strict=False, pick AnyType. """ # Collect a list of constraints for each type variable. cmap = defaultdict(list) # type: Dict[TypeVarId, List[Constraint]] for con in constraints: cmap[con.type_var].append(con) res = [] # type: List[Optional[Type]] # Solve each type variable separately. for tvar in vars: bottom = None # type: Optional[Type] top = None # type: Optional[Type] candidate = None # type: Optional[Type] # Process each constraint separately, and calculate the lower and upper # bounds based on constraints. Note that we assume that the constraint # targets do not have constraint references. for c in cmap.get(tvar, []): if c.op == SUPERTYPE_OF: if bottom is None: bottom = c.target else: bottom = join_types(bottom, c.target) else: if top is None: top = c.target else: top = meet_types(top, c.target) if isinstance(top, AnyType) or isinstance(bottom, AnyType): source_any = top if isinstance(top, AnyType) else bottom assert isinstance(source_any, AnyType) res.append(AnyType(TypeOfAny.from_another_any, source_any=source_any)) continue elif bottom is None: if top: candidate = top else: # No constraints for type variable -- 'UninhabitedType' is the most specific type. if strict: candidate = UninhabitedType() candidate.ambiguous = True else: candidate = AnyType(TypeOfAny.special_form) elif top is None: candidate = bottom elif is_subtype(bottom, top): candidate = bottom else: candidate = None res.append(candidate) return res
def __init__(self, variance: int = COVARIANT) -> None: # The 'object' class self.oi = self.make_type_info('builtins.object') # class object self.o = Instance(self.oi, []) # object # Type variables (these are effectively global) def make_type_var(name: str, id: int, values: List[Type], upper_bound: Type, variance: int) -> TypeVarType: return TypeVarType(name, name, id, values, upper_bound, variance) self.t = make_type_var('T', 1, [], self.o, variance) # T`1 (type variable) self.tf = make_type_var('T', -1, [], self.o, variance) # T`-1 (type variable) self.tf2 = make_type_var('T', -2, [], self.o, variance) # T`-2 (type variable) self.s = make_type_var('S', 2, [], self.o, variance) # S`2 (type variable) self.s1 = make_type_var('S', 1, [], self.o, variance) # S`1 (type variable) self.sf = make_type_var('S', -2, [], self.o, variance) # S`-2 (type variable) self.sf1 = make_type_var('S', -1, [], self.o, variance) # S`-1 (type variable) # Simple types self.anyt = AnyType(TypeOfAny.special_form) self.nonet = NoneType() self.uninhabited = UninhabitedType() # Abstract class TypeInfos # class F self.fi = self.make_type_info('F', is_abstract=True) # class F2 self.f2i = self.make_type_info('F2', is_abstract=True) # class F3(F) self.f3i = self.make_type_info('F3', is_abstract=True, mro=[self.fi]) # Class TypeInfos self.std_tuplei = self.make_type_info('builtins.tuple', mro=[self.oi], typevars=['T'], variances=[COVARIANT ]) # class tuple self.type_typei = self.make_type_info('builtins.type') # class type self.bool_type_info = self.make_type_info('builtins.bool') self.functioni = self.make_type_info( 'builtins.function') # function TODO self.ai = self.make_type_info('A', mro=[self.oi]) # class A self.bi = self.make_type_info('B', mro=[self.ai, self.oi]) # class B(A) self.ci = self.make_type_info('C', mro=[self.ai, self.oi]) # class C(A) self.di = self.make_type_info('D', mro=[self.oi]) # class D # class E(F) self.ei = self.make_type_info('E', mro=[self.fi, self.oi]) # class E2(F2, F) self.e2i = self.make_type_info('E2', mro=[self.f2i, self.fi, self.oi]) # class E3(F, F2) self.e3i = self.make_type_info('E3', mro=[self.fi, self.f2i, self.oi]) # Generic class TypeInfos # G[T] self.gi = self.make_type_info('G', mro=[self.oi], typevars=['T'], variances=[variance]) # G2[T] self.g2i = self.make_type_info('G2', mro=[self.oi], typevars=['T'], variances=[variance]) # H[S, T] self.hi = self.make_type_info('H', mro=[self.oi], typevars=['S', 'T'], variances=[variance, variance]) # GS[T, S] <: G[S] self.gsi = self.make_type_info('GS', mro=[self.gi, self.oi], typevars=['T', 'S'], variances=[variance, variance], bases=[Instance(self.gi, [self.s])]) # GS2[S] <: G[S] self.gs2i = self.make_type_info('GS2', mro=[self.gi, self.oi], typevars=['S'], variances=[variance], bases=[Instance(self.gi, [self.s1])]) # list[T] self.std_listi = self.make_type_info('builtins.list', mro=[self.oi], typevars=['T'], variances=[variance]) # Instance types self.std_tuple = Instance(self.std_tuplei, [self.anyt]) # tuple self.type_type = Instance(self.type_typei, []) # type self.function = Instance(self.functioni, []) # function TODO self.a = Instance(self.ai, []) # A self.b = Instance(self.bi, []) # B self.c = Instance(self.ci, []) # C self.d = Instance(self.di, []) # D self.e = Instance(self.ei, []) # E self.e2 = Instance(self.e2i, []) # E2 self.e3 = Instance(self.e3i, []) # E3 self.f = Instance(self.fi, []) # F self.f2 = Instance(self.f2i, []) # F2 self.f3 = Instance(self.f3i, []) # F3 # Generic instance types self.ga = Instance(self.gi, [self.a]) # G[A] self.gb = Instance(self.gi, [self.b]) # G[B] self.gd = Instance(self.gi, [self.d]) # G[D] self.go = Instance(self.gi, [self.o]) # G[object] self.gt = Instance(self.gi, [self.t]) # G[T`1] self.gtf = Instance(self.gi, [self.tf]) # G[T`-1] self.gtf2 = Instance(self.gi, [self.tf2]) # G[T`-2] self.gs = Instance(self.gi, [self.s]) # G[S] self.gdyn = Instance(self.gi, [self.anyt]) # G[Any] self.gn = Instance(self.gi, [NoneType()]) # G[None] self.g2a = Instance(self.g2i, [self.a]) # G2[A] self.gsaa = Instance(self.gsi, [self.a, self.a]) # GS[A, A] self.gsab = Instance(self.gsi, [self.a, self.b]) # GS[A, B] self.gsba = Instance(self.gsi, [self.b, self.a]) # GS[B, A] self.gs2a = Instance(self.gs2i, [self.a]) # GS2[A] self.gs2b = Instance(self.gs2i, [self.b]) # GS2[B] self.gs2d = Instance(self.gs2i, [self.d]) # GS2[D] self.hab = Instance(self.hi, [self.a, self.b]) # H[A, B] self.haa = Instance(self.hi, [self.a, self.a]) # H[A, A] self.hbb = Instance(self.hi, [self.b, self.b]) # H[B, B] self.hts = Instance(self.hi, [self.t, self.s]) # H[T, S] self.had = Instance(self.hi, [self.a, self.d]) # H[A, D] self.hao = Instance(self.hi, [self.a, self.o]) # H[A, object] self.lsta = Instance(self.std_listi, [self.a]) # List[A] self.lstb = Instance(self.std_listi, [self.b]) # List[B] self.lit1 = LiteralType(1, self.a) self.lit2 = LiteralType(2, self.a) self.lit3 = LiteralType("foo", self.d) self.lit4 = LiteralType(4, self.a) self.lit1_inst = Instance(self.ai, [], last_known_value=self.lit1) self.lit2_inst = Instance(self.ai, [], last_known_value=self.lit2) self.lit3_inst = Instance(self.di, [], last_known_value=self.lit3) self.lit4_inst = Instance(self.ai, [], last_known_value=self.lit4) 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) self._add_bool_dunder(self.bool_type_info) self._add_bool_dunder(self.ai)
def visit_unbound_type(self, t: UnboundType) -> Type: if t.optional: t.optional = False # We don't need to worry about double-wrapping Optionals or # wrapping Anys: Union simplification will take care of that. return make_optional_type(self.visit_unbound_type(t)) sym = self.lookup(t.name, t, suppress_errors=self.third_pass) # type: ignore if sym is not None: if sym.node is None: # UNBOUND_IMPORTED can happen if an unknown name was imported. if sym.kind != UNBOUND_IMPORTED: self.fail(errorcode.INTERNAL_ERROR_NODE_IS_NONE(sym.kind), t) return AnyType(TypeOfAny.special_form) fullname = sym.node.fullname() hook = self.plugin.get_type_analyze_hook(fullname) if hook: return hook(AnalyzeTypeContext(t, t, self)) if (fullname in nongen_builtins and t.args and not sym.normalized and not self.allow_unnormalized): self.fail(errorcode.NO_SUBSCRIPT_BUILTIN_ALIAS(fullname), t) if self.tvar_scope: tvar_def = self.tvar_scope.get_binding(sym) else: tvar_def = None if self.warn_bound_tvar and sym.kind == TVAR and tvar_def is not None: self.fail( errorcode. CANNOT_USE_BOUND_TYPE_VAR_TO_DEFINE_GENERIC_ALIAS(t.name), t) return AnyType(TypeOfAny.from_error) elif sym.kind == TVAR and tvar_def is not None: if len(t.args) > 0: self.fail(errorcode.TYPE_VAR_USED_WITH_ARG(t.name), t) return TypeVarType(tvar_def, t.line) elif fullname == 'builtins.None': return NoneTyp() elif fullname == 'typing.Any' or fullname == 'builtins.Any': return AnyType(TypeOfAny.explicit) elif fullname == 'typing.Tuple': if len(t.args) == 0 and not t.empty_tuple_index: # Bare 'Tuple' is same as 'tuple' if self.options.disallow_any_generics and not self.is_typeshed_stub: self.fail(errorcode.BARE_GENERIC(), t) typ = self.named_type('builtins.tuple', line=t.line, column=t.column) typ.from_generic_builtin = True return typ if len(t.args) == 2 and isinstance(t.args[1], EllipsisType): # Tuple[T, ...] (uniform, variable-length tuple) instance = self.named_type('builtins.tuple', [self.anal_type(t.args[0])]) instance.line = t.line return instance return self.tuple_type(self.anal_array(t.args)) elif fullname == 'typing.Union': items = self.anal_array(t.args) return UnionType.make_union(items) elif fullname == 'typing.Optional': if len(t.args) != 1: self.fail(errorcode.OPTIONAL_MUST_HAVE_ONE_ARGUMENT(), t) return AnyType(TypeOfAny.from_error) item = self.anal_type(t.args[0]) return make_optional_type(item) elif fullname == 'typing.Callable': return self.analyze_callable_type(t) elif fullname == 'typing.Type': if len(t.args) == 0: any_type = AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column) return TypeType(any_type, line=t.line, column=t.column) if len(t.args) != 1: self.fail(errorcode.TYPE_MUST_HAVE_EXACTLY_ONE_TYPE(), t) item = self.anal_type(t.args[0]) return TypeType.make_normalized(item, line=t.line) elif fullname == 'typing.ClassVar': if self.nesting_level > 0: self.fail( errorcode.INVALID_TYPE_CLASSVAR_NESTED_INSIDE_TYPE(), t) if len(t.args) == 0: return AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column) if len(t.args) != 1: self.fail( errorcode.CLASS_VAR_MUST_HAVE_AT_MOST_ONE_TYPE_ARG(), t) return AnyType(TypeOfAny.from_error) item = self.anal_type(t.args[0]) if isinstance(item, TypeVarType) or get_type_vars(item): self.fail(errorcode.CLASSVAR_CANNOT_BE_GENERIC(), t) return AnyType(TypeOfAny.from_error) return item elif fullname in ('mypy_extensions.NoReturn', 'typing.NoReturn'): return UninhabitedType(is_noreturn=True) elif sym.kind == TYPE_ALIAS: override = sym.type_override all_vars = sym.alias_tvars assert override is not None an_args = self.anal_array(t.args) if all_vars is not None: exp_len = len(all_vars) else: exp_len = 0 act_len = len(an_args) if exp_len > 0 and act_len == 0: # Interpret bare Alias same as normal generic, i.e., Alias[Any, Any, ...] assert all_vars is not None return set_any_tvars(override, all_vars, t.line, t.column) if exp_len == 0 and act_len == 0: return override if act_len != exp_len: self.fail( errorcode.BAD_NUMBER_ARGUMENT_FOR_TYPEALIAS( exp_len, act_len), t) return set_any_tvars(override, all_vars or [], t.line, t.column, implicit=False) assert all_vars is not None return replace_alias_tvars(override, all_vars, an_args, t.line, t.column) elif not isinstance(sym.node, TypeInfo): name = sym.fullname if name is None: name = sym.node.name() if isinstance(sym.node, Var) and isinstance( sym.node.type, AnyType): # Something with an Any type -- make it an alias for Any in a type # context. This is slightly problematic as it allows using the type 'Any' # as a base class -- however, this will fail soon at runtime so the problem # is pretty minor. return AnyType(TypeOfAny.from_unimported_type) # Allow unbound type variables when defining an alias if not (self.aliasing and sym.kind == TVAR and (not self.tvar_scope or self.tvar_scope.get_binding(sym) is None)): if (not self.third_pass and not self.in_dynamic_func and not (isinstance(sym.node, (FuncDef, Decorator)) or isinstance(sym.node, Var) and sym.node.is_ready) and not (sym.kind == TVAR and tvar_def is None)): if t.args and not self.global_scope: self.fail( errorcode.UNSUPPORTED_FORWARD_REFERENCE( t.name), t) return AnyType(TypeOfAny.from_error) return ForwardRef(t) self.fail(errorcode.INVALID_TYPE_X(name), t) if self.third_pass and sym.kind == TVAR: self.note_func( errorcode.FORWARD_REERENCES_TO_TYPE_VAR_PROHIBITED( ), t) return t info = sym.node # type: TypeInfo if len(t.args) > 0 and info.fullname() == 'builtins.tuple': fallback = Instance(info, [AnyType(TypeOfAny.special_form)], t.line) return TupleType(self.anal_array(t.args), fallback, t.line) else: # Analyze arguments and construct Instance type. The # number of type arguments and their values are # checked only later, since we do not always know the # valid count at this point. Thus we may construct an # Instance with an invalid number of type arguments. instance = Instance(info, self.anal_array(t.args), t.line, t.column) instance.from_generic_builtin = sym.normalized tup = info.tuple_type if tup is not None: # The class has a Tuple[...] base class so it will be # represented as a tuple type. if t.args: self.fail( errorcode.GENERIC_TUPLE_TYPES_NOT_SUPPORTED(), t) return AnyType(TypeOfAny.from_error) return tup.copy_modified(items=self.anal_array(tup.items), fallback=instance) td = info.typeddict_type if td is not None: # The class has a TypedDict[...] base class so it will be # represented as a typeddict type. if t.args: self.fail( errorcode.GENERIC_TYPEDDICT_TYPES_NOT_SUPPORTED(), t) return AnyType(TypeOfAny.from_error) # Create a named TypedDictType return td.copy_modified(item_types=self.anal_array( list(td.items.values())), fallback=instance) return instance else: if self.third_pass: self.fail(errorcode.INVALID_TYPE_X(t.name), t) return AnyType(TypeOfAny.from_error) return AnyType(TypeOfAny.special_form)
def bind_self(method: F, original_type: Optional[Type] = None, is_classmethod: bool = False) -> F: """Return a copy of `method`, with the type of its first parameter (usually self or cls) bound to original_type. If the type of `self` is a generic type (T, or Type[T] for classmethods), instantiate every occurrence of type with original_type in the rest of the signature and in the return type. original_type is the type of E in the expression E.copy(). It is None in compatibility checks. In this case we treat it as the erasure of the declared type of self. This way we can express "the type of self". For example: T = TypeVar('T', bound='A') class A: def copy(self: T) -> T: ... class B(A): pass b = B().copy() # type: B """ from mypy.infer import infer_type_arguments if isinstance(method, Overloaded): return cast( F, Overloaded([ bind_self(c, original_type, is_classmethod) for c in method.items ])) assert isinstance(method, CallableType) func = method if not func.arg_types: # Invalid method, return something. return cast(F, func) if func.arg_kinds[0] == ARG_STAR: # The signature is of the form 'def foo(*args, ...)'. # In this case we shouldn't drop the first arg, # since func will be absorbed by the *args. # TODO: infer bounds on the type of *args? return cast(F, func) self_param_type = get_proper_type(func.arg_types[0]) variables: Sequence[TypeVarLikeType] = [] if func.variables and supported_self_type(self_param_type): if original_type is None: # TODO: type check method override (see #7861). original_type = erase_to_bound(self_param_type) original_type = get_proper_type(original_type) all_ids = func.type_var_ids() typeargs = infer_type_arguments(all_ids, self_param_type, original_type, is_supertype=True) if (is_classmethod # TODO: why do we need the extra guards here? and any( isinstance(get_proper_type(t), UninhabitedType) for t in typeargs) and isinstance( original_type, (Instance, TypeVarType, TupleType))): # In case we call a classmethod through an instance x, fallback to type(x) typeargs = infer_type_arguments(all_ids, self_param_type, TypeType(original_type), is_supertype=True) ids = [ tid for tid in all_ids if any(tid == t.id for t in get_type_vars(self_param_type)) ] # Technically, some constrains might be unsolvable, make them <nothing>. to_apply = [ t if t is not None else UninhabitedType() for t in typeargs ] def expand(target: Type) -> Type: return expand_type(target, {id: to_apply[all_ids.index(id)] for id in ids}) arg_types = [expand(x) for x in func.arg_types[1:]] ret_type = expand(func.ret_type) variables = [v for v in func.variables if v.id not in ids] else: arg_types = func.arg_types[1:] ret_type = func.ret_type variables = func.variables original_type = get_proper_type(original_type) if isinstance(original_type, CallableType) and original_type.is_type_obj(): 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:], variables=variables, ret_type=ret_type, bound_args=[original_type]) return cast(F, res)