def visit_tuple_type(self, left: TupleType) -> bool: right = self.right if isinstance(right, Instance): if is_named_instance(right, 'builtins.object'): return True if is_named_instance(right, 'builtins.tuple'): target_item_type = right.args[0] return all(is_subtype(item, target_item_type) for item in left.items) elif (is_named_instance(right, 'typing.Iterable') or is_named_instance(right, 'typing.Sequence') or is_named_instance(right, 'typing.Reversible')): iter_type = right.args[0] return all(is_subtype(li, iter_type) for li in left.items) return False elif isinstance(right, TupleType): if len(left.items) != len(right.items): return False for i in range(len(left.items)): if not is_subtype(left.items[i], right.items[i], self.check_type_parameter): return False if not is_subtype(left.fallback, right.fallback, self.check_type_parameter): return False return True else: return False
def visit_instance(self, left: Instance) -> bool: if left.type.fallback_to_any: if isinstance(self.right, NoneTyp): # NOTE: `None` is a *non-subclassable* singleton, therefore no class # can by a subtype of it, even with an `Any` fallback. # This special case is needed to treat descriptors in classes with # dynamic base classes correctly, see #5456. return False return True right = self.right if isinstance(right, TupleType) and right.fallback.type.is_enum: return is_subtype(left, right.fallback) if isinstance(right, Instance): if TypeState.is_cached_subtype_check(left, right): return True # NOTE: left.type.mro may be None in quick mode if there # was an error somewhere. if left.type.mro is not None: for base in left.type.mro: # TODO: Also pass recursively ignore_declared_variance if base._promote and is_subtype( base._promote, self.right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): TypeState.record_subtype_cache_entry(left, right) return True rname = right.type.fullname() # Always try a nominal check if possible, # there might be errors that a user wants to silence *once*. if ((left.type.has_base(rname) or rname == 'builtins.object') and not self.ignore_declared_variance): # Map left type to corresponding right instances. t = map_instance_to_supertype(left, right.type) nominal = all(self.check_type_parameter(lefta, righta, tvar.variance) for lefta, righta, tvar in zip(t.args, right.args, right.type.defn.type_vars)) if nominal: TypeState.record_subtype_cache_entry(left, right) return nominal if right.type.is_protocol and is_protocol_implementation(left, right): return True return False if isinstance(right, TypeType): item = right.item if isinstance(item, TupleType): item = item.fallback if is_named_instance(left, 'builtins.type'): return is_subtype(TypeType(AnyType(TypeOfAny.special_form)), right) if left.type.is_metaclass(): if isinstance(item, AnyType): return True if isinstance(item, Instance): return is_named_instance(item, 'builtins.object') if isinstance(right, CallableType): # Special case: Instance can be a subtype of Callable. call = find_member('__call__', left, left) if call: return is_subtype(call, right) return False else: return False
def visit_tuple_type(self, left: TupleType) -> bool: right = self.right if isinstance(right, Instance): if is_named_instance(right, 'builtins.object'): return True if is_named_instance(right, 'builtins.tuple'): target_item_type = right.args[0] return all( is_subtype(item, target_item_type) for item in left.items) elif (is_named_instance(right, 'typing.Iterable') or is_named_instance(right, 'typing.Sequence') or is_named_instance(right, 'typing.Reversible')): iter_type = right.args[0] return all(is_subtype(li, iter_type) for li in left.items) return False elif isinstance(right, TupleType): if len(left.items) != len(right.items): return False for i in range(len(left.items)): if not is_subtype(left.items[i], right.items[i], self.check_type_parameter): return False if not is_subtype(left.fallback, right.fallback, self.check_type_parameter): return False return True else: return False
def visit_instance(self, left: Instance) -> bool: if left.type.fallback_to_any: return True right = self.right if isinstance(right, TupleType) and right.fallback.type.is_enum: return is_subtype(left, right.fallback) if isinstance(right, Instance): if right.type.is_cached_subtype_check(left, right): return True # NOTE: left.type.mro may be None in quick mode if there # was an error somewhere. if left.type.mro is not None: for base in left.type.mro: # TODO: Also pass recursively ignore_declared_variance if base._promote and is_subtype( base._promote, self.right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): right.type.record_subtype_cache_entry(left, right) return True rname = right.type.fullname() # Always try a nominal check if possible, # there might be errors that a user wants to silence *once*. if ((left.type.has_base(rname) or rname == 'builtins.object') and not self.ignore_declared_variance): # Map left type to corresponding right instances. t = map_instance_to_supertype(left, right.type) nominal = all(self.check_type_parameter(lefta, righta, tvar.variance) for lefta, righta, tvar in zip(t.args, right.args, right.type.defn.type_vars)) if nominal: right.type.record_subtype_cache_entry(left, right) return nominal if right.type.is_protocol and is_protocol_implementation(left, right): return True return False if isinstance(right, TypeType): item = right.item if isinstance(item, TupleType): item = item.fallback if is_named_instance(left, 'builtins.type'): return is_subtype(TypeType(AnyType(TypeOfAny.special_form)), right) if left.type.is_metaclass(): if isinstance(item, AnyType): return True if isinstance(item, Instance): # Special-case enum since we don't have better way of expressing it if (is_named_instance(left, 'enum.EnumMeta') and is_named_instance(item, 'enum.Enum')): return True return is_named_instance(item, 'builtins.object') if isinstance(right, CallableType): # Special case: Instance can be a subtype of Callable. call = find_member('__call__', left, left) if call: return is_subtype(call, right) return False else: return False
def visit_callable_type(self, left: CallableType) -> bool: right = self.right if isinstance(right, CallableType): return is_callable_subtype(left, right) elif isinstance(right, Overloaded): return all(is_subtype(left, item) for item in right.items()) elif is_named_instance(right, 'builtins.object'): return True elif (is_named_instance(right, 'builtins.type') and left.is_type_obj()): return True else: return False
def visit_instance(self, template: Instance) -> List[Constraint]: actual = self.actual res = [] # type: List[Constraint] if isinstance(actual, CallableType) and actual.fallback is not None: actual = actual.fallback if isinstance(actual, TypedDictType): actual = actual.as_anonymous().fallback if isinstance(actual, Instance): instance = actual if (self.direction == SUBTYPE_OF and template.type.has_base(instance.type.fullname())): mapped = map_instance_to_supertype(template, instance.type) for i in range(len(instance.args)): # The constraints for generic type parameters are # invariant. Include constraints from both directions # to achieve the effect. res.extend( infer_constraints(mapped.args[i], instance.args[i], self.direction)) res.extend( infer_constraints(mapped.args[i], instance.args[i], neg_op(self.direction))) return res elif (self.direction == SUPERTYPE_OF and instance.type.has_base(template.type.fullname())): mapped = map_instance_to_supertype(instance, template.type) for j in range(len(template.args)): # The constraints for generic type parameters are # invariant. res.extend( infer_constraints(template.args[j], mapped.args[j], self.direction)) res.extend( infer_constraints(template.args[j], mapped.args[j], neg_op(self.direction))) return res if isinstance(actual, AnyType): # IDEA: Include both ways, i.e. add negation as well? return self.infer_against_any(template.args, actual) if (isinstance(actual, TupleType) and (is_named_instance(template, 'typing.Iterable') or is_named_instance(template, 'typing.Container') or is_named_instance(template, 'typing.Sequence') or is_named_instance(template, 'typing.Reversible')) and self.direction == SUPERTYPE_OF): for item in actual.items: cb = infer_constraints(template.args[0], item, SUPERTYPE_OF) res.extend(cb) return res else: return []
def is_subtype( left: Type, right: Type, type_parameter_checker: TypeParameterChecker = check_type_parameter, *, ignore_pos_arg_names: bool = False) -> bool: """Is 'left' subtype of 'right'? Also consider Any to be a subtype of any type, and vice versa. This recursively applies to components of composite types (List[int] is subtype of List[Any], for example). type_parameter_checker is used to check the type parameters (for example, A with B in is_subtype(C[A], C[B]). The default checks for subtype relation between the type arguments (e.g., A and B), taking the variance of the type var into account. """ if (isinstance(right, AnyType) or isinstance(right, UnboundType) or isinstance(right, ErasedType)): return True elif isinstance(right, UnionType) and not isinstance(left, UnionType): # Normally, when 'left' is not itself a union, the only way # 'left' can be a subtype of the union 'right' is if it is a # subtype of one of the items making up the union. is_subtype_of_item = any( is_subtype(left, item, type_parameter_checker, ignore_pos_arg_names=ignore_pos_arg_names) for item in right.items) # However, if 'left' is a type variable T, T might also have # an upper bound which is itself a union. This case will be # handled below by the SubtypeVisitor. We have to check both # possibilities, to handle both cases like T <: Union[T, U] # and cases like T <: B where B is the upper bound of T and is # a union. (See #2314.) if not isinstance(left, TypeVarType): return is_subtype_of_item elif is_subtype_of_item: return True # otherwise, fall through # Treat builtins.type the same as Type[Any] elif is_named_instance(left, 'builtins.type'): return is_subtype(TypeType(AnyType()), right) elif is_named_instance(right, 'builtins.type'): return is_subtype(left, TypeType(AnyType())) return left.accept( SubtypeVisitor(right, type_parameter_checker, ignore_pos_arg_names=ignore_pos_arg_names))
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType) or is_named_instance( right, 'builtins.type'): for item in left.items(): if is_subtype(item, right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return True return False elif isinstance(right, Overloaded): # TODO: this may be too restrictive if len(left.items()) != len(right.items()): return False for i in range(len(left.items())): if not is_subtype(left.items()[i], right.items()[i], self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return False return True elif isinstance(right, UnboundType): return True elif isinstance(right, TypeType): # All the items must have the same type object status, so # it's sufficient to query only (any) one of them. # This is unsound, we don't check the __init__ signature. return left.is_type_obj() and is_subtype(left.items()[0].ret_type, right.item) else: return False
def visit_none_type(self, left: NoneTyp) -> bool: if experiments.STRICT_OPTIONAL: return (isinstance(self.right, NoneTyp) or is_named_instance(self.right, 'builtins.object') or isinstance(self.right, Instance) and self.right.type.is_protocol and not self.right.type.protocol_members) else: return True
def visit_instance(self, left: Instance) -> bool: if left.type.fallback_to_any: return True right = self.right if isinstance(right, TupleType) and right.fallback.type.is_enum: return is_subtype(left, right.fallback) if isinstance(right, Instance): # NOTO: left.type.mro may be None in quick mode if there # was an error somewhere. if left.type.mro is not None: for base in left.type.mro: if base._promote and is_subtype( base._promote, self.right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return True rname = right.type.fullname() if not left.type.has_base(rname) and rname != 'builtins.object': return False # Map left type to corresponding right instances. t = map_instance_to_supertype(left, right.type) return all( self.check_type_parameter(lefta, righta, tvar.variance) for lefta, righta, tvar in zip(t.args, right.args, right.type.defn.type_vars)) if isinstance(right, TypeType): item = right.item if isinstance(item, TupleType): item = item.fallback if is_named_instance(left, 'builtins.type'): return is_subtype(TypeType(AnyType()), right) if left.type.is_metaclass(): if isinstance(item, AnyType): return True if isinstance(item, Instance): # Special-case enum since we don't have better way of expressing it if (is_named_instance(left, 'enum.EnumMeta') and is_named_instance(item, 'enum.Enum')): return True return is_named_instance(item, 'builtins.object') return False
def visit_instance(self, template: Instance) -> List[Constraint]: actual = self.actual res = [] # type: List[Constraint] if isinstance(actual, Instance): instance = actual if (self.direction == SUBTYPE_OF and template.type.has_base(instance.type.fullname())): mapped = map_instance_to_supertype(template, instance.type) for i in range(len(instance.args)): # The constraints for generic type parameters are # invariant. Include constraints from both directions # to achieve the effect. res.extend(infer_constraints( mapped.args[i], instance.args[i], self.direction)) res.extend(infer_constraints( mapped.args[i], instance.args[i], neg_op(self.direction))) return res elif (self.direction == SUPERTYPE_OF and instance.type.has_base(template.type.fullname())): mapped = map_instance_to_supertype(instance, template.type) for j in range(len(template.args)): # The constraints for generic type parameters are # invariant. res.extend(infer_constraints( template.args[j], mapped.args[j], self.direction)) res.extend(infer_constraints( template.args[j], mapped.args[j], neg_op(self.direction))) return res if isinstance(actual, AnyType): # IDEA: Include both ways, i.e. add negation as well? return self.infer_against_any(template.args) if (isinstance(actual, TupleType) and (is_named_instance(template, 'typing.Iterable') or is_named_instance(template, 'typing.Container') or is_named_instance(template, 'typing.Sequence') or is_named_instance(template, 'typing.Reversible')) and self.direction == SUPERTYPE_OF): for item in actual.items: cb = infer_constraints(template.args[0], item, SUPERTYPE_OF) res.extend(cb) return res else: return []
def visit_tuple_type(self, left: TupleType) -> bool: right = self.right if isinstance(right, Instance): if (is_named_instance(right, 'builtins.tuple') or is_named_instance(right, 'typing.Iterable') or is_named_instance(right, 'typing.Container') or is_named_instance(right, 'typing.Sequence') or is_named_instance(right, 'typing.Reversible')): if not right.args: return False iter_type = right.args[0] if is_named_instance(right, 'builtins.tuple') and isinstance(iter_type, AnyType): # TODO: We shouldn't need this special case. This is currently needed # for isinstance(x, tuple), though it's unclear why. return True return all(is_proper_subtype(li, iter_type) for li in left.items) return is_proper_subtype(left.fallback, right) elif isinstance(right, TupleType): if len(left.items) != len(right.items): return False for l, r in zip(left.items, right.items): if not is_proper_subtype(l, r): return False return is_proper_subtype(left.fallback, right.fallback) return False
def visit_tuple_type(self, left: TupleType) -> bool: right = self.right if isinstance(right, Instance): if is_named_instance(right, 'typing.Sized'): return True elif (is_named_instance(right, 'builtins.tuple') or is_named_instance(right, 'typing.Iterable') or is_named_instance(right, 'typing.Container') or is_named_instance(right, 'typing.Sequence') or is_named_instance(right, 'typing.Reversible')): if right.args: iter_type = right.args[0] else: iter_type = AnyType() return all(is_subtype(li, iter_type) for li in left.items) elif is_subtype(left.fallback, right, self.check_type_parameter): return True return False elif isinstance(right, TupleType): if len(left.items) != len(right.items): return False for l, r in zip(left.items, right.items): if not is_subtype(l, r, self.check_type_parameter): return False if not is_subtype(left.fallback, right.fallback, self.check_type_parameter): return False return True else: return False
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if is_named_instance(right, 'builtins.object'): return True elif isinstance(right, CallableType) or is_named_instance( right, 'builtins.type'): for item in left.items(): if is_subtype(item, right): return True return False elif isinstance(right, Overloaded): # TODO: this may be too restrictive if len(left.items()) != len(right.items()): return False for i in range(len(left.items())): if not is_subtype(left.items()[i], right.items()[i]): return False return True elif isinstance(right, UnboundType): return True else: return False
def is_subtype( left: Type, right: Type, type_parameter_checker: TypeParameterChecker = check_type_parameter, *, ignore_pos_arg_names: bool = False) -> bool: """Is 'left' subtype of 'right'? Also consider Any to be a subtype of any type, and vice versa. This recursively applies to components of composite types (List[int] is subtype of List[Any], for example). type_parameter_checker is used to check the type parameters (for example, A with B in is_subtype(C[A], C[B]). The default checks for subtype relation between the type arguments (e.g., A and B), taking the variance of the type var into account. """ if (isinstance(right, AnyType) or isinstance(right, UnboundType) or isinstance(right, ErasedType)): return True elif isinstance(right, UnionType) and not isinstance(left, UnionType): return any( is_subtype(left, item, type_parameter_checker, ignore_pos_arg_names=ignore_pos_arg_names) for item in right.items) # Treat builtins.type the same as Type[Any] elif is_named_instance(left, 'builtins.type'): return is_subtype(TypeType(AnyType()), right) elif is_named_instance(right, 'builtins.type'): return is_subtype(left, TypeType(AnyType())) else: return left.accept( SubtypeVisitor(right, type_parameter_checker, ignore_pos_arg_names=ignore_pos_arg_names))
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType) or is_named_instance(right, "builtins.type"): for item in left.items(): if is_subtype(item, right, self.check_type_parameter): return True return False elif isinstance(right, Overloaded): # TODO: this may be too restrictive if len(left.items()) != len(right.items()): return False for i in range(len(left.items())): if not is_subtype(left.items()[i], right.items()[i], self.check_type_parameter): return False return True elif isinstance(right, UnboundType): return True else: return False
def visit_type_var(self, left: TypeVarType) -> bool: right = self.right if isinstance(right, TypeVarType): return left.id == right.id else: return is_named_instance(self.right, 'builtins.object')
def visit_instance(self, template: Instance) -> List[Constraint]: original_actual = actual = self.actual res = [] # type: List[Constraint] if isinstance(actual, CallableType) and actual.fallback is not None: actual = actual.fallback if isinstance(actual, TypedDictType): actual = actual.as_anonymous().fallback if isinstance(actual, Instance): instance = actual # We always try nominal inference if possible, # it is much faster than the structural one. if (self.direction == SUBTYPE_OF and template.type.has_base(instance.type.fullname())): mapped = map_instance_to_supertype(template, instance.type) for i in range(len(instance.args)): # The constraints for generic type parameters are # invariant. Include constraints from both directions # to achieve the effect. res.extend( infer_constraints(mapped.args[i], instance.args[i], self.direction)) res.extend( infer_constraints(mapped.args[i], instance.args[i], neg_op(self.direction))) return res elif (self.direction == SUPERTYPE_OF and instance.type.has_base(template.type.fullname())): mapped = map_instance_to_supertype(instance, template.type) for j in range(len(template.args)): # The constraints for generic type parameters are # invariant. res.extend( infer_constraints(template.args[j], mapped.args[j], self.direction)) res.extend( infer_constraints(template.args[j], mapped.args[j], neg_op(self.direction))) return res if (template.type.is_protocol and self.direction == SUPERTYPE_OF and # We avoid infinite recursion for structural subtypes by checking # whether this type already appeared in the inference chain. # This is a conservative way break the inference cycles. # It never produces any "false" constraints but gives up soon # on purely structural inference cycles, see #3829. not any( is_same_type(template, t) for t in template.type.inferring) and mypy.subtypes.is_subtype(instance, erase_typevars(template))): template.type.inferring.append(template) self.infer_constraints_from_protocol_members( res, instance, template, original_actual, template) template.type.inferring.pop() return res elif (instance.type.is_protocol and self.direction == SUBTYPE_OF and # We avoid infinite recursion for structural subtypes also here. not any( is_same_type(instance, i) for i in instance.type.inferring) and mypy.subtypes.is_subtype(erase_typevars(template), instance)): instance.type.inferring.append(instance) self.infer_constraints_from_protocol_members( res, instance, template, template, instance) instance.type.inferring.pop() return res if isinstance(actual, AnyType): # IDEA: Include both ways, i.e. add negation as well? return self.infer_against_any(template.args, actual) if (isinstance(actual, TupleType) and (is_named_instance(template, 'typing.Iterable') or is_named_instance(template, 'typing.Container') or is_named_instance(template, 'typing.Sequence') or is_named_instance(template, 'typing.Reversible')) and self.direction == SUPERTYPE_OF): for item in actual.items: cb = infer_constraints(template.args[0], item, SUPERTYPE_OF) res.extend(cb) return res elif isinstance(actual, TupleType) and self.direction == SUPERTYPE_OF: return infer_constraints(template, actual.fallback, self.direction) else: return []
def visit_instance(self, template: Instance) -> List[Constraint]: original_actual = actual = self.actual res = [] # type: List[Constraint] if isinstance(actual, CallableType) and actual.fallback is not None: actual = actual.fallback if isinstance(actual, TypedDictType): actual = actual.as_anonymous().fallback if isinstance(actual, Instance): instance = actual # We always try nominal inference if possible, # it is much faster than the structural one. if (self.direction == SUBTYPE_OF and template.type.has_base(instance.type.fullname())): mapped = map_instance_to_supertype(template, instance.type) for i in range(len(instance.args)): # The constraints for generic type parameters are # invariant. Include constraints from both directions # to achieve the effect. res.extend(infer_constraints( mapped.args[i], instance.args[i], self.direction)) res.extend(infer_constraints( mapped.args[i], instance.args[i], neg_op(self.direction))) return res elif (self.direction == SUPERTYPE_OF and instance.type.has_base(template.type.fullname())): mapped = map_instance_to_supertype(instance, template.type) for j in range(len(template.args)): # The constraints for generic type parameters are # invariant. res.extend(infer_constraints( template.args[j], mapped.args[j], self.direction)) res.extend(infer_constraints( template.args[j], mapped.args[j], neg_op(self.direction))) return res if (template.type.is_protocol and self.direction == SUPERTYPE_OF and # We avoid infinite recursion for structural subtypes by checking # whether this type already appeared in the inference chain. # This is a conservative way break the inference cycles. # It never produces any "false" constraints but gives up soon # on purely structural inference cycles, see #3829. not any(is_same_type(template, t) for t in template.type.inferring) and mypy.subtypes.is_subtype(instance, erase_typevars(template))): template.type.inferring.append(template) self.infer_constraints_from_protocol_members(res, instance, template, original_actual, template) template.type.inferring.pop() return res elif (instance.type.is_protocol and self.direction == SUBTYPE_OF and # We avoid infinite recursion for structural subtypes also here. not any(is_same_type(instance, i) for i in instance.type.inferring) and mypy.subtypes.is_subtype(erase_typevars(template), instance)): instance.type.inferring.append(instance) self.infer_constraints_from_protocol_members(res, instance, template, template, instance) instance.type.inferring.pop() return res if isinstance(actual, AnyType): # IDEA: Include both ways, i.e. add negation as well? return self.infer_against_any(template.args, actual) if (isinstance(actual, TupleType) and (is_named_instance(template, 'typing.Iterable') or is_named_instance(template, 'typing.Container') or is_named_instance(template, 'typing.Sequence') or is_named_instance(template, 'typing.Reversible')) and self.direction == SUPERTYPE_OF): for item in actual.items: cb = infer_constraints(template.args[0], item, SUPERTYPE_OF) res.extend(cb) return res elif isinstance(actual, TupleType) and self.direction == SUPERTYPE_OF: return infer_constraints(template, actual.fallback, self.direction) else: return []
def visit_none_type(self, left: NoneTyp) -> bool: if experiments.STRICT_OPTIONAL: return (isinstance(self.right, NoneTyp) or is_named_instance(self.right, 'builtins.object')) else: return not isinstance(self.right, Void)
def visit_instance(self, template: Instance) -> List[Constraint]: original_actual = actual = self.actual res: List[Constraint] = [] if isinstance( actual, (CallableType, Overloaded)) and template.type.is_protocol: if template.type.protocol_members == ['__call__']: # Special case: a generic callback protocol if not any( mypy.sametypes.is_same_type(template, t) for t in template.type.inferring): template.type.inferring.append(template) call = mypy.subtypes.find_member('__call__', template, actual, is_operator=True) assert call is not None if mypy.subtypes.is_subtype(actual, erase_typevars(call)): subres = infer_constraints(call, actual, self.direction) res.extend(subres) template.type.inferring.pop() return res if isinstance(actual, CallableType) and actual.fallback is not None: actual = actual.fallback if isinstance(actual, Overloaded) and actual.fallback is not None: actual = actual.fallback if isinstance(actual, TypedDictType): actual = actual.as_anonymous().fallback if isinstance(actual, LiteralType): actual = actual.fallback if isinstance(actual, Instance): instance = actual erased = erase_typevars(template) assert isinstance(erased, Instance) # type: ignore # We always try nominal inference if possible, # it is much faster than the structural one. if (self.direction == SUBTYPE_OF and template.type.has_base(instance.type.fullname)): mapped = map_instance_to_supertype(template, instance.type) tvars = mapped.type.defn.type_vars # N.B: We use zip instead of indexing because the lengths might have # mismatches during daemon reprocessing. for tvar, mapped_arg, instance_arg in zip( tvars, mapped.args, instance.args): # TODO: ParamSpecType if isinstance(tvar, TypeVarType): # The constraints for generic type parameters depend on variance. # Include constraints from both directions if invariant. if tvar.variance != CONTRAVARIANT: res.extend( infer_constraints(mapped_arg, instance_arg, self.direction)) if tvar.variance != COVARIANT: res.extend( infer_constraints(mapped_arg, instance_arg, neg_op(self.direction))) return res elif (self.direction == SUPERTYPE_OF and instance.type.has_base(template.type.fullname)): mapped = map_instance_to_supertype(instance, template.type) tvars = template.type.defn.type_vars # N.B: We use zip instead of indexing because the lengths might have # mismatches during daemon reprocessing. for tvar, mapped_arg, template_arg in zip( tvars, mapped.args, template.args): # TODO: ParamSpecType if isinstance(tvar, TypeVarType): # The constraints for generic type parameters depend on variance. # Include constraints from both directions if invariant. if tvar.variance != CONTRAVARIANT: res.extend( infer_constraints(template_arg, mapped_arg, self.direction)) if tvar.variance != COVARIANT: res.extend( infer_constraints(template_arg, mapped_arg, neg_op(self.direction))) return res if (template.type.is_protocol and self.direction == SUPERTYPE_OF and # We avoid infinite recursion for structural subtypes by checking # whether this type already appeared in the inference chain. # This is a conservative way to break the inference cycles. # It never produces any "false" constraints but gives up soon # on purely structural inference cycles, see #3829. # Note that we use is_protocol_implementation instead of is_subtype # because some type may be considered a subtype of a protocol # due to _promote, but still not implement the protocol. not any( mypy.sametypes.is_same_type(template, t) for t in template.type.inferring) and mypy.subtypes.is_protocol_implementation( instance, erased)): template.type.inferring.append(template) res.extend( self.infer_constraints_from_protocol_members( instance, template, original_actual, template)) template.type.inferring.pop() return res elif (instance.type.is_protocol and self.direction == SUBTYPE_OF and # We avoid infinite recursion for structural subtypes also here. not any( mypy.sametypes.is_same_type(instance, i) for i in instance.type.inferring) and mypy.subtypes.is_protocol_implementation( erased, instance)): instance.type.inferring.append(instance) res.extend( self.infer_constraints_from_protocol_members( instance, template, template, instance)) instance.type.inferring.pop() return res if isinstance(actual, AnyType): return self.infer_against_any(template.args, actual) if (isinstance(actual, TupleType) and is_named_instance(template, TUPLE_LIKE_INSTANCE_NAMES) and self.direction == SUPERTYPE_OF): for item in actual.items: cb = infer_constraints(template.args[0], item, SUPERTYPE_OF) res.extend(cb) return res elif isinstance(actual, TupleType) and self.direction == SUPERTYPE_OF: return infer_constraints(template, mypy.typeops.tuple_fallback(actual), self.direction) elif isinstance(actual, TypeVarType): if not actual.values: return infer_constraints(template, actual.upper_bound, self.direction) return [] else: return []
def visit_instance(self, template: Instance) -> List[Constraint]: original_actual = actual = self.actual res = [] # type: List[Constraint] if isinstance( actual, (CallableType, Overloaded)) and template.type.is_protocol: if template.type.protocol_members == ['__call__']: # Special case: a generic callback protocol if not any( mypy.sametypes.is_same_type(template, t) for t in template.type.inferring): template.type.inferring.append(template) call = mypy.subtypes.find_member('__call__', template, actual) assert call is not None if mypy.subtypes.is_subtype(actual, erase_typevars(call)): subres = infer_constraints(call, actual, self.direction) res.extend(subres) template.type.inferring.pop() return res if isinstance(actual, CallableType) and actual.fallback is not None: actual = actual.fallback if isinstance(actual, Overloaded) and actual.fallback is not None: actual = actual.fallback if isinstance(actual, TypedDictType): actual = actual.as_anonymous().fallback if isinstance(actual, Instance): instance = actual erased = erase_typevars(template) assert isinstance(erased, Instance) # We always try nominal inference if possible, # it is much faster than the structural one. if (self.direction == SUBTYPE_OF and template.type.has_base(instance.type.fullname())): mapped = map_instance_to_supertype(template, instance.type) tvars = mapped.type.defn.type_vars for i in range(len(instance.args)): # The constraints for generic type parameters depend on variance. # Include constraints from both directions if invariant. if tvars[i].variance != CONTRAVARIANT: res.extend( infer_constraints(mapped.args[i], instance.args[i], self.direction)) if tvars[i].variance != COVARIANT: res.extend( infer_constraints(mapped.args[i], instance.args[i], neg_op(self.direction))) return res elif (self.direction == SUPERTYPE_OF and instance.type.has_base(template.type.fullname())): mapped = map_instance_to_supertype(instance, template.type) tvars = template.type.defn.type_vars for j in range(len(template.args)): # The constraints for generic type parameters depend on variance. # Include constraints from both directions if invariant. if tvars[j].variance != CONTRAVARIANT: res.extend( infer_constraints(template.args[j], mapped.args[j], self.direction)) if tvars[j].variance != COVARIANT: res.extend( infer_constraints(template.args[j], mapped.args[j], neg_op(self.direction))) return res if (template.type.is_protocol and self.direction == SUPERTYPE_OF and # We avoid infinite recursion for structural subtypes by checking # whether this type already appeared in the inference chain. # This is a conservative way break the inference cycles. # It never produces any "false" constraints but gives up soon # on purely structural inference cycles, see #3829. # Note that we use is_protocol_implementation instead of is_subtype # because some type may be considered a subtype of a protocol # due to _promote, but still not implement the protocol. not any( mypy.sametypes.is_same_type(template, t) for t in template.type.inferring) and mypy.subtypes.is_protocol_implementation( instance, erased)): template.type.inferring.append(template) self.infer_constraints_from_protocol_members( res, instance, template, original_actual, template) template.type.inferring.pop() return res elif (instance.type.is_protocol and self.direction == SUBTYPE_OF and # We avoid infinite recursion for structural subtypes also here. not any( mypy.sametypes.is_same_type(instance, i) for i in instance.type.inferring) and mypy.subtypes.is_protocol_implementation( erased, instance)): instance.type.inferring.append(instance) self.infer_constraints_from_protocol_members( res, instance, template, template, instance) instance.type.inferring.pop() return res if isinstance(actual, AnyType): # IDEA: Include both ways, i.e. add negation as well? return self.infer_against_any(template.args, actual) if (isinstance(actual, TupleType) and (is_named_instance(template, 'typing.Iterable') or is_named_instance(template, 'typing.Container') or is_named_instance(template, 'typing.Sequence') or is_named_instance(template, 'typing.Reversible')) and self.direction == SUPERTYPE_OF): for item in actual.items: cb = infer_constraints(template.args[0], item, SUPERTYPE_OF) res.extend(cb) return res elif isinstance(actual, TupleType) and self.direction == SUPERTYPE_OF: return infer_constraints(template, mypy.typeops.tuple_fallback(actual), self.direction) else: return []
def visit_instance(self, template: Instance) -> List[Constraint]: original_actual = actual = self.actual res = [] # type: List[Constraint] if isinstance(actual, (CallableType, Overloaded)) and template.type.is_protocol: if template.type.protocol_members == ['__call__']: # Special case: a generic callback protocol if not any(is_same_type(template, t) for t in template.type.inferring): template.type.inferring.append(template) call = mypy.subtypes.find_member('__call__', template, actual) assert call is not None if mypy.subtypes.is_subtype(actual, erase_typevars(call)): subres = infer_constraints(call, actual, self.direction) res.extend(subres) template.type.inferring.pop() return res if isinstance(actual, CallableType) and actual.fallback is not None: actual = actual.fallback if isinstance(actual, Overloaded) and actual.fallback is not None: actual = actual.fallback if isinstance(actual, TypedDictType): actual = actual.as_anonymous().fallback if isinstance(actual, Instance): instance = actual erased = erase_typevars(template) assert isinstance(erased, Instance) # We always try nominal inference if possible, # it is much faster than the structural one. if (self.direction == SUBTYPE_OF and template.type.has_base(instance.type.fullname())): mapped = map_instance_to_supertype(template, instance.type) tvars = mapped.type.defn.type_vars for i in range(len(instance.args)): # The constraints for generic type parameters depend on variance. # Include constraints from both directions if invariant. if tvars[i].variance != CONTRAVARIANT: res.extend(infer_constraints( mapped.args[i], instance.args[i], self.direction)) if tvars[i].variance != COVARIANT: res.extend(infer_constraints( mapped.args[i], instance.args[i], neg_op(self.direction))) return res elif (self.direction == SUPERTYPE_OF and instance.type.has_base(template.type.fullname())): mapped = map_instance_to_supertype(instance, template.type) tvars = template.type.defn.type_vars for j in range(len(template.args)): # The constraints for generic type parameters depend on variance. # Include constraints from both directions if invariant. if tvars[j].variance != CONTRAVARIANT: res.extend(infer_constraints( template.args[j], mapped.args[j], self.direction)) if tvars[j].variance != COVARIANT: res.extend(infer_constraints( template.args[j], mapped.args[j], neg_op(self.direction))) return res if (template.type.is_protocol and self.direction == SUPERTYPE_OF and # We avoid infinite recursion for structural subtypes by checking # whether this type already appeared in the inference chain. # This is a conservative way break the inference cycles. # It never produces any "false" constraints but gives up soon # on purely structural inference cycles, see #3829. # Note that we use is_protocol_implementation instead of is_subtype # because some type may be considered a subtype of a protocol # due to _promote, but still not implement the protocol. not any(is_same_type(template, t) for t in template.type.inferring) and mypy.subtypes.is_protocol_implementation(instance, erased)): template.type.inferring.append(template) self.infer_constraints_from_protocol_members(res, instance, template, original_actual, template) template.type.inferring.pop() return res elif (instance.type.is_protocol and self.direction == SUBTYPE_OF and # We avoid infinite recursion for structural subtypes also here. not any(is_same_type(instance, i) for i in instance.type.inferring) and mypy.subtypes.is_protocol_implementation(erased, instance)): instance.type.inferring.append(instance) self.infer_constraints_from_protocol_members(res, instance, template, template, instance) instance.type.inferring.pop() return res if isinstance(actual, AnyType): # IDEA: Include both ways, i.e. add negation as well? return self.infer_against_any(template.args, actual) if (isinstance(actual, TupleType) and (is_named_instance(template, 'typing.Iterable') or is_named_instance(template, 'typing.Container') or is_named_instance(template, 'typing.Sequence') or is_named_instance(template, 'typing.Reversible')) and self.direction == SUPERTYPE_OF): for item in actual.items: cb = infer_constraints(template.args[0], item, SUPERTYPE_OF) res.extend(cb) return res elif isinstance(actual, TupleType) and self.direction == SUPERTYPE_OF: return infer_constraints(template, actual.fallback, self.direction) else: return []