def is_proper_subtype(t: Type, s: Type) -> bool: """Check if t is a proper subtype of s? For proper subtypes, there's no need to rely on compatibility due to Any types. Any instance type t is also a proper subtype of t. """ # FIX tuple types if isinstance(t, Instance): if isinstance(s, Instance): if not t.type.has_base(s.type.fullname()): return False def check_argument(left: Type, right: Type, variance: int) -> bool: if variance == COVARIANT: return is_proper_subtype(left, right) elif variance == CONTRAVARIANT: return is_proper_subtype(right, left) else: return sametypes.is_same_type(left, right) # Map left type to corresponding right instances. t = map_instance_to_supertype(t, s.type) return all(check_argument(ta, ra, tvar.variance) for ta, ra, tvar in zip(t.args, s.args, s.type.defn.type_vars)) return False else: return sametypes.is_same_type(t, s)
def visit_intersection(self, t): # TODO Obsolete; target overload types instead? # Only support very rudimentary meets between intersection types. if is_same_type(self.s, t): return self.s else: return self.default(self.s)
def check_argument(left: Type, right: Type, variance: int) -> bool: if variance == COVARIANT: return is_proper_subtype(left, right) elif variance == CONTRAVARIANT: return is_proper_subtype(right, left) else: return sametypes.is_same_type(left, right)
def check_type_var_values(self, type: TypeInfo, actuals: List[Type], valids: List[Type], context: Context) -> None: for actual in actuals: if (not isinstance(actual, AnyType) and not any(is_same_type(actual, value) for value in valids)): self.fail('Invalid type argument value for "{}"'.format( type.name()), context)
def add_symbol(self, name: str, node: SymbolTableNode, context: Context) -> None: # NOTE: This is closely related to SemanticAnalyzerPass2.add_symbol. Since both methods # will be called on top-level definitions, they need to co-operate. If you change # this, you may have to change the other method as well. if self.sem.is_func_scope(): assert self.sem.locals[-1] is not None if name in self.sem.locals[-1]: # Flag redefinition unless this is a reimport of a module. if not (node.kind == MODULE_REF and self.sem.locals[-1][name].node == node.node): self.sem.name_already_defined(name, context) self.sem.locals[-1][name] = node else: assert self.sem.type is None # Pass 1 doesn't look inside classes existing = self.sem.globals.get(name) if (existing and (not isinstance(node.node, MypyFile) or existing.node != node.node) and existing.kind != UNBOUND_IMPORTED and not isinstance(existing.node, ImportedName)): # Modules can be imported multiple times to support import # of multiple submodules of a package (e.g. a.x and a.y). ok = False # Only report an error if the symbol collision provides a different type. if existing.type and node.type and is_same_type(existing.type, node.type): ok = True if not ok: self.sem.name_already_defined(name, context) elif not existing: self.sem.globals[name] = node
def is_proper_subtype(t: Type, s: Type) -> bool: """Check if t is a proper subtype of s? For proper subtypes, there's no need to rely on compatibility due to Any types. Any instance type t is also a proper subtype of t. """ # FIX tuple types if isinstance(t, Instance): if isinstance(s, Instance): if not t.type.has_base(s.type.fullname()): return False t = map_instance_to_supertype(t, s.type) return all(sametypes.is_same_type(x, y) for x, y in zip(t.args, s.args)) return False else: return sametypes.is_same_type(t, s)
def apply_generic_arguments(callable: CallableType, types: List[Type], msg: MessageBuilder, context: Context) -> Type: """Apply generic type arguments to a callable type. For example, applying [int] to 'def [T] (T) -> T' results in 'def (int) -> int'. Note that each type can be None; in this case, it will not be applied. """ tvars = callable.variables if len(tvars) != len(types): msg.incompatible_type_application(len(tvars), len(types), context) return AnyType() # Check that inferred type variable values are compatible with allowed # values and bounds. Also, promote subtype values to allowed values. types = types[:] for i, type in enumerate(types): values = callable.variables[i].values if values and type: if isinstance(type, AnyType): continue if isinstance(type, TypeVarType) and type.values: # Allow substituting T1 for T if every allowed value of T1 # is also a legal value of T. if all(any(is_same_type(v, v1) for v in values) for v1 in type.values): continue for value in values: if mypy.subtypes.is_subtype(type, value): types[i] = value break else: msg.incompatible_typevar_value(callable, i + 1, type, context) upper_bound = callable.variables[i].upper_bound if type and not mypy.subtypes.satisfies_upper_bound(type, upper_bound): msg.incompatible_typevar_value(callable, i + 1, type, context) # Create a map from type variable id to target type. id_to_type = {} # type: Dict[TypeVarId, Type] for i, tv in enumerate(tvars): if types[i]: id_to_type[tv.id] = types[i] # Apply arguments to argument types. arg_types = [expand_type(at, id_to_type) for at in callable.arg_types] # The callable may retain some type vars if only some were applied. remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] return callable.copy_modified( arg_types=arg_types, ret_type=expand_type(callable.ret_type, id_to_type), variables=remaining_tvars, )
def update_from_options(self, frames: List[Frame]) -> bool: """Update the frame to reflect that each key will be updated as in one of the frames. Return whether any item changes. If a key is declared as AnyType, only update it if all the options are the same. """ frames = [f for f in frames if not f.unreachable] changed = False keys = set(key for f in frames for key in f) for key in keys: current_value = self._get(key) resulting_values = [f.get(key, current_value) for f in frames] if any(x is None for x in resulting_values): # We didn't know anything about key before # (current_value must be None), and we still don't # know anything about key in at least one possible frame. continue type = resulting_values[0] assert type is not None declaration_type = self.declarations.get(key) if isinstance(declaration_type, AnyType): # At this point resulting values can't contain None, see continue above if not all( is_same_type(type, cast(Type, t)) for t in resulting_values[1:]): type = AnyType(TypeOfAny.from_another_any, source_any=declaration_type) else: for other in resulting_values[1:]: assert other is not None type = join_simple(self.declarations[key], type, other) if current_value is None or not is_same_type(type, current_value): self._put(key, type) changed = True self.frames[-1].unreachable = not frames return changed
def assert_simple_is_same(self, s: Type, t: Type, expected: bool, strict: bool) -> None: actual = is_same_type(s, t) assert_equal(actual, expected, 'is_same_type({}, {}) is {{}} ({{}} expected)'.format(s, t)) if strict: actual2 = (s == t) assert_equal(actual2, expected, '({} == {}) is {{}} ({{}} expected)'.format(s, t)) assert_equal(hash(s) == hash(t), expected, '(hash({}) == hash({}) is {{}} ({{}} expected)'.format(s, t))
def visit_typeddict_type(self, left: TypedDictType) -> bool: right = self.right if isinstance(right, TypedDictType): for name, typ in left.items.items(): if name in right.items and not is_same_type(typ, right.items[name]): return False for name, typ in right.items.items(): if name not in left.items: return False return True return is_proper_subtype(left.fallback, right)
def check_type_var_values(self, type: TypeInfo, actuals: List[Type], valids: List[Type], arg_number: int, context: Context) -> None: for actual in actuals: if (not isinstance(actual, AnyType) and not any(is_same_type(actual, value) for value in valids)): if len(actuals) > 1 or not isinstance(actual, Instance): self.fail('Invalid type argument value for "{}"'.format( type.name()), context) else: self.fail('Type argument {} of "{}" has incompatible value "{}"'.format( arg_number, type.name(), actual.type.name()), context)
def test_literal_type(self) -> None: a = self.fx.a lit1 = self.fx.lit1 lit2 = self.fx.lit2 lit3 = self.fx.lit3 self.assert_meet(lit1, lit1, lit1) self.assert_meet(lit1, a, lit1) self.assert_meet_uninhabited(lit1, lit3) self.assert_meet_uninhabited(lit1, lit2) self.assert_meet(UnionType([lit1, lit2]), lit1, lit1) self.assert_meet(UnionType([lit1, lit2]), UnionType([lit2, lit3]), lit2) self.assert_meet(UnionType([lit1, lit2]), UnionType([lit1, lit2]), UnionType([lit1, lit2])) self.assert_meet(lit1, self.fx.anyt, lit1) self.assert_meet(lit1, self.fx.o, lit1) assert is_same_type(lit1, narrow_declared_type(lit1, a)) assert is_same_type(lit2, narrow_declared_type(lit2, a))
def generate_type_combinations(types: List[Type]) -> List[Type]: """Generate possible combinations of a list of types. mypy essentially supports two different ways to do this: joining the types and unioning the types. We try both. """ joined_type = join_type_list(types) union_type = make_simplified_union(types) if is_same_type(joined_type, union_type): return [joined_type] else: return [joined_type, union_type]
def is_proper_subtype(t: Type, s: Type) -> bool: """Check if t is a proper subtype of s? For proper subtypes, there's no need to rely on compatibility due to Any types. Any instance type t is also a proper subtype of t. """ # FIX tuple types if isinstance(t, Instance): if isinstance(s, Instance): if not t.type.has_base(s.type.fullname()): return False t = map_instance_to_supertype(t, s.type) if not is_immutable(s): return all(sametypes.is_same_type(ta, ra) for (ta, ra) in zip(t.args, s.args)) else: return all(is_proper_subtype(ta, ra) for (ta, ra) in zip(t.args, s.args)) return False else: return sametypes.is_same_type(t, s)
def zerodim_to_scalar(typ: Type) -> Type: if is_subtype( typ, API.named_generic_type( 'numpy.ndarray', args=[ AnyType(TypeOfAny.unannotated), API.named_type('numpy.ZeroD') ])) and (not is_same_type(typ.args[1], AnyType(TypeOfAny.unannotated))): return typ.args[0] return typ
def test_literal_type(self) -> None: a = self.fx.a d = self.fx.d lit1 = LiteralType(1, a) lit2 = LiteralType(2, a) lit3 = LiteralType("foo", d) self.assert_meet(lit1, lit1, lit1) self.assert_meet(lit1, a, lit1) self.assert_meet_uninhabited(lit1, lit3) self.assert_meet_uninhabited(lit1, lit2) self.assert_meet(UnionType([lit1, lit2]), lit1, lit1) self.assert_meet(UnionType([lit1, lit2]), UnionType([lit2, lit3]), lit2) self.assert_meet(UnionType([lit1, lit2]), UnionType([lit1, lit2]), UnionType([lit1, lit2])) self.assert_meet(lit1, self.fx.anyt, lit1) self.assert_meet(lit1, self.fx.o, lit1) assert_true(is_same_type(lit1, narrow_declared_type(lit1, a))) assert_true(is_same_type(lit2, narrow_declared_type(lit2, a)))
def generate_type_combinations(types: List[Type]) -> List[Type]: """Generate possible combinations of a list of types. mypy essentially supports two different ways to do this: joining the types and unioning the types. We try both. """ joined_type = join_type_list(types) union_type = UnionType.make_simplified_union(types) if is_same_type(joined_type, union_type): return [joined_type] else: return [joined_type, union_type]
def is_more_precise(t: Type, s: Type) -> bool: """Check if t is a more precise type than s. A t is a proper subtype of s, t is also more precise than s. Also, if s is Any, t is more precise than s for any t. Finally, if t is the same type as s, t is more precise than s. """ # TODO Should List[int] be more precise than List[Any]? if isinstance(s, AnyType): return True if isinstance(s, Instance): return is_proper_subtype(t, s) return sametypes.is_same_type(t, s)
def is_ndarray_of_ints(typ: Type, no_bools: bool = True): if not isinstance(typ, Instance): return False of_ints = is_subtype( typ, API.named_generic_type( 'numpy.ndarray', args=[int_type(), API.named_type('numpy._Dimension')])) and ( not is_same_type(typ.args[1], AnyType(TypeOfAny.unannotated))) if not no_bools: return of_ints return of_ints and not is_ndarray_of_bools(typ)
def update_from_options(self, frames: List[Frame]) -> bool: """Update the frame to reflect that each key will be updated as in one of the frames. Return whether any item changes. If a key is declared as AnyType, only update it if all the options are the same. """ frames = [f for f in frames if not f.unreachable] changed = False keys = set(key for f in frames for key in f) for key in keys: current_value = self._get(key) resulting_values = [f.get(key, current_value) for f in frames] if any(x is None for x in resulting_values): # We didn't know anything about key before # (current_value must be None), and we still don't # know anything about key in at least one possible frame. continue type = resulting_values[0] assert type is not None declaration_type = self.declarations.get(key) if isinstance(declaration_type, AnyType): # At this point resulting values can't contain None, see continue above if not all(is_same_type(type, cast(Type, t)) for t in resulting_values[1:]): type = AnyType(TypeOfAny.from_another_any, source_any=declaration_type) else: for other in resulting_values[1:]: assert other is not None type = join_simple(self.declarations[key], type, other) if current_value is None or not is_same_type(type, current_value): self._put(key, type) changed = True self.frames[-1].unreachable = not frames return changed
def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: str, valids: List[Type], arg_number: int, context: Context) -> None: for actual in actuals: if (not isinstance(actual, AnyType) and not any(is_same_type(actual, value) for value in valids)): if len(actuals) > 1 or not isinstance(actual, Instance): self.fail('Invalid type argument value for "{}"'.format( type.name()), context) else: class_name = '"{}"'.format(type.name()) actual_type_name = '"{}"'.format(actual.type.name()) self.fail(messages.INCOMPATIBLE_TYPEVAR_VALUE.format( arg_name, class_name, actual_type_name), context)
def check_instance_definition( passed_types: TupleType, instance_signature: CallableType, fullname: str, ctx: MethodContext, ) -> _RuntimeValidationContext: """ Checks runtime type. We call "runtime types" things that we use at runtime to dispatch our calls. For example: 1. We check that type passed in ``some.instance(...)`` matches one defined in a type annotation 2. We check that types don't have any concrete types 3. We check that types don't have any unbound type variables 4. We check that ``is_protocol`` is passed correctly """ runtime_type = inference.infer_runtime_type_from_context( fallback=passed_types.items[0], fullname=fullname, ctx=ctx, ) if len(passed_types.items) == 2: is_protocol, protocol_arg_check = _check_protocol_arg(passed_types, ctx) else: is_protocol = False protocol_arg_check = True instance_type = instance_signature.arg_types[0] instance_check = is_same_type( erase_type(instance_type), erase_type(runtime_type), ) if not instance_check: ctx.api.fail( _INSTANCE_RUNTIME_MISMATCH_MSG.format(instance_type, runtime_type), ctx.context, ) return _RuntimeValidationContext(runtime_type, is_protocol, all([ _check_runtime_protocol(runtime_type, ctx, is_protocol=is_protocol), _check_concrete_generics(runtime_type, instance_type, ctx), _check_tuple_size(instance_type, ctx), protocol_arg_check, instance_check, ]))
def is_more_precise(t: Type, s: Type) -> bool: """Check if t is a more precise type than s. A t is a proper subtype of s, t is also more precise than s. Also, if s is Any, t is more precise than s for any t. Finally, if t is the same type as s, t is more precise than s. """ # TODO Should List[int] be more precise than List[Any]? if isinstance(s, AnyType): return True if isinstance(s, Instance): if isinstance(t, CallableType): # Fall back to subclass check and ignore other properties of the callable. return is_proper_subtype(t.fallback, s) return is_proper_subtype(t, s) return sametypes.is_same_type(t, s)
def is_valid_inferred_type(self, typ): """Is an inferred type invalid? Examples include the None type or a type with a None component. """ if is_same_type(typ, NoneTyp()): return False elif isinstance(typ, Instance): for arg in (typ).args: if not self.is_valid_inferred_type(arg): return False elif isinstance(typ, TupleType): for item in (typ).items: if not self.is_valid_inferred_type(item): return False return True
def is_trivial_coercion(target_type, source_type, is_java): """Is an implicit coercion from source_type to target_type a no-op? Note that we omit coercions of form any <= C, unless C is a primitive that may have a special representation. """ # FIX: Replace type vars in source type with any? if isinstance(source_type, Void) or is_same_type(target_type, source_type): return True # Coercions from a primitive type to any other type are non-trivial, since # we may have to change the representation. if not is_java and is_special_primitive(source_type): return False return (is_proper_subtype(source_type, target_type) or isinstance(source_type, NoneTyp) or isinstance(target_type, Any))
def is_trivial_coercion(target_type: Type, source_type: Type, is_java: bool) -> bool: """Is an implicit coercion from source_type to target_type a no-op? Note that we omit coercions of form any <= C, unless C is a primitive that may have a special representation. """ # FIX: Replace type vars in source type with any? if isinstance(source_type, Void) or is_same_type(target_type, source_type): return True # Coercions from a primitive type to any other type are non-trivial, since # we may have to change the representation. if not is_java and is_special_primitive(source_type): return False return (is_proper_subtype(source_type, target_type) or isinstance(source_type, NoneTyp) or isinstance(target_type, AnyType))
def is_simple_override(fdef, info): """Is function an override with the same type precision as the original? Compare to the original method in the superclass of info. """ # If this is not an override, this can't be a simple override either. # Generic inheritance is not currently supported, since we need to map # type variables between types; in the future this restriction can be # lifted. if info.base is None or info.base.type_vars != []: return False orig = info.base.get_method(fdef.name()) # Ignore the first argument (self) when determining type sameness. # TODO overloads newtype = function_type(fdef) newtype = replace_self_type(newtype, Any()) origtype = function_type(orig) origtype = replace_self_type(origtype, Any()) return is_same_type(newtype, origtype)
def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: str, valids: List[Type], arg_number: int, context: Context) -> None: for actual in get_proper_types(actuals): if (not isinstance(actual, AnyType) and not any(is_same_type(actual, value) for value in valids)): if len(actuals) > 1 or not isinstance(actual, Instance): self.fail( message_registry.INVALID_TYPEVAR_ARG_VALUE.format( type.name), context, code=codes.TYPE_VAR) else: class_name = '"{}"'.format(type.name) actual_type_name = '"{}"'.format(actual.type.name) self.fail( message_registry.INCOMPATIBLE_TYPEVAR_VALUE.format( arg_name, class_name, actual_type_name), context, code=codes.TYPE_VAR)
def is_simple_override(fdef: FuncDef, info: TypeInfo) -> bool: """Is function an override with the same type precision as the original? Compare to the original method in the superclass of info. """ # If this is not an override, this can't be a simple override either. # Generic inheritance is not currently supported, since we need to map # type variables between types; in the future this restriction can be # lifted. if len(info.mro) <= 1: return False base = info.mro[1] if base.type_vars != []: return False orig = base.get_method(fdef.name()) # Ignore the first argument (self) when determining type sameness. # TODO overloads newtype = cast(Callable, function_type(fdef)) newtype = replace_leading_arg_type(newtype, AnyType()) origtype = cast(Callable, function_type(orig)) origtype = replace_leading_arg_type(origtype, AnyType()) return is_same_type(newtype, origtype)
def is_simple_override(fdef: FuncDef, info: TypeInfo) -> bool: """Is function an override with the same type precision as the original? Compare to the original method in the superclass of info. """ # If this is not an override, this can't be a simple override either. # Generic inheritance is not currently supported, since we need to map # type variables between types; in the future this restriction can be # lifted. if len(info.mro) <= 1: return False base = info.mro[1] if base.type_vars != []: return False orig = base.get_method(fdef.name()) # Ignore the first argument (self) when determining type sameness. # TODO overloads newtype = cast(Callable, function_type(fdef)) newtype = replace_self_type(newtype, AnyType()) origtype = cast(Callable, function_type(orig)) origtype = replace_self_type(origtype, AnyType()) return is_same_type(newtype, origtype)
def is_ellipsis(type: Type): return is_same_type(type, API.named_type('builtins.ellipsis'))
def is_list_of_int(type: Type): return is_same_type( type, API.named_generic_type('builtins.list', args=[int_type()]))
def is_slice(type: Type): return is_same_type(type, API.named_type('builtins.slice'))
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) 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. # 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 []
def visit_intersection(self, t): # Only support very rudimentary meets between intersection types. if is_same_type(self.s, t): return self.s else: return self.default(self.s)
Compare to the original method in the superclass of info. """ # If this is not an override, this can't be a simple override either. # Generic inheritance is not currently supported, since we need to map # type variables between types; in the future this restriction can be # lifted. if info.base is None or info.base.type_vars != []: return False orig = info.base.get_method(fdef.name()) # Ignore the first argument (self) when determining type sameness. # TODO overloads newtype = (Callable)function_type(fdef) newtype = replace_self_type(newtype, Any()) origtype = (Callable)function_type(orig) origtype = replace_self_type(origtype, Any()) return is_same_type(newtype, origtype) str tvar_slot_name(int n, any is_alt=False): """Return the name of the member that holds the runtime value of the given type variable slot. """ if is_alt != BOUND_VAR: if n == 0: return '__tv' else: return '__tv{}'.format(n + 1) else: # Greatest lower bound if n == 0: return '__btv'
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 is_float(typ: Type): return is_same_type(typ, float_type())
def apply_generic_arguments(callable: CallableType, orig_types: Sequence[Optional[Type]], msg: MessageBuilder, context: Context, skip_unsatisfied: bool = False) -> CallableType: """Apply generic type arguments to a callable type. For example, applying [int] to 'def [T] (T) -> T' results in 'def (int) -> int'. Note that each type can be None; in this case, it will not be applied. If `skip_unsatisfied` is True, then just skip the types that don't satisfy type variable bound or constraints, instead of giving an error. """ tvars = callable.variables assert len(tvars) == len(orig_types) # Check that inferred type variable values are compatible with allowed # values and bounds. Also, promote subtype values to allowed values. types = list(orig_types) for i, type in enumerate(types): assert not isinstance(type, PartialType), "Internal error: must never apply partial type" values = callable.variables[i].values if type is None: continue if values: if isinstance(type, AnyType): continue if isinstance(type, TypeVarType) and type.values: # Allow substituting T1 for T if every allowed value of T1 # is also a legal value of T. if all(any(is_same_type(v, v1) for v in values) for v1 in type.values): continue matching = [] for value in values: if mypy.subtypes.is_subtype(type, value): matching.append(value) if matching: best = matching[0] # If there are more than one matching value, we select the narrowest for match in matching[1:]: if mypy.subtypes.is_subtype(match, best): best = match types[i] = best else: if skip_unsatisfied: types[i] = None else: msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context) else: upper_bound = callable.variables[i].upper_bound if not mypy.subtypes.is_subtype(type, upper_bound): if skip_unsatisfied: types[i] = None else: msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context) # Create a map from type variable id to target type. id_to_type = {} # type: Dict[TypeVarId, Type] for i, tv in enumerate(tvars): typ = types[i] if typ: id_to_type[tv.id] = typ # Apply arguments to argument types. arg_types = [expand_type(at, id_to_type) for at in callable.arg_types] # The callable may retain some type vars if only some were applied. remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] return callable.copy_modified( arg_types=arg_types, ret_type=expand_type(callable.ret_type, id_to_type), variables=remaining_tvars, )
def is_object(typ: Type): return is_same_type(typ, API.named_type('builtins.object'))
def is_protocol_implementation(left: Instance, right: Instance, proper_subtype: bool = False) -> bool: """Check whether 'left' implements the protocol 'right'. If 'proper_subtype' is True, then check for a proper subtype. Treat recursive protocols by using the 'assuming' structural subtype matrix (in sparse representation, i.e. as a list of pairs (subtype, supertype)), see also comment in nodes.TypeInfo. When we enter a check for classes (A, P), defined as following:: class P(Protocol): def f(self) -> P: ... class A: def f(self) -> A: ... this results in A being a subtype of P without infinite recursion. On every false result, we pop the assumption, thus avoiding an infinite recursion as well. """ assert right.type.is_protocol assuming = right.type.assuming_proper if proper_subtype else right.type.assuming for (l, r) in reversed(assuming): if sametypes.is_same_type(l, left) and sametypes.is_same_type(r, right): return True with pop_on_exit(assuming, left, right): for member in right.type.protocol_members: # nominal subtyping currently ignores '__init__' and '__new__' signatures if member in ('__init__', '__new__'): continue # The third argument below indicates to what self type is bound. # We always bind self to the subtype. (Similarly to nominal types). supertype = find_member(member, right, left) assert supertype is not None subtype = find_member(member, left, left) # Useful for debugging: # print(member, 'of', left, 'has type', subtype) # print(member, 'of', right, 'has type', supertype) if not subtype: return False if not proper_subtype: # Nominal check currently ignores arg names is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=True) else: is_compat = is_proper_subtype(subtype, supertype) if not is_compat: return False if isinstance(subtype, NoneTyp) and isinstance(supertype, CallableType): # We want __hash__ = None idiom to work even without --strict-optional return False subflags = get_member_flags(member, left.type) superflags = get_member_flags(member, right.type) if IS_SETTABLE in superflags: # Check opposite direction for settable attributes. if not is_subtype(supertype, subtype): return False if (IS_CLASSVAR in subflags) != (IS_CLASSVAR in superflags): return False if IS_SETTABLE in superflags and IS_SETTABLE not in subflags: return False # This rule is copied from nominal check in checker.py if IS_CLASS_OR_STATIC in superflags and IS_CLASS_OR_STATIC not in subflags: return False right.type.record_subtype_cache_entry(left, right, proper_subtype) return True
def is_same_constraint(c1: Constraint, c2: Constraint) -> bool: return (c1.type_var == c2.type_var and c1.op == c2.op and is_same_type(c1.target, c2.target))
def apply_generic_arguments(callable: CallableType, orig_types: Sequence[Optional[Type]], msg: MessageBuilder, context: Context) -> CallableType: """Apply generic type arguments to a callable type. For example, applying [int] to 'def [T] (T) -> T' results in 'def (int) -> int'. Note that each type can be None; in this case, it will not be applied. """ tvars = callable.variables assert len(tvars) == len(orig_types) # Check that inferred type variable values are compatible with allowed # values and bounds. Also, promote subtype values to allowed values. types = list(orig_types) for i, type in enumerate(types): assert not isinstance( type, PartialType), "Internal error: must never apply partial type" values = callable.variables[i].values if values and type: if isinstance(type, AnyType): continue if isinstance(type, TypeVarType) and type.values: # Allow substituting T1 for T if every allowed value of T1 # is also a legal value of T. if all( any(is_same_type(v, v1) for v in values) for v1 in type.values): continue for value in values: if mypy.subtypes.is_subtype(type, value): types[i] = value break else: msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context) upper_bound = callable.variables[i].upper_bound if type and not mypy.subtypes.is_subtype(type, upper_bound): msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context) # Create a map from type variable id to target type. id_to_type = {} # type: Dict[TypeVarId, Type] for i, tv in enumerate(tvars): typ = types[i] if typ: id_to_type[tv.id] = typ # Apply arguments to argument types. arg_types = [expand_type(at, id_to_type) for at in callable.arg_types] # The callable may retain some type vars if only some were applied. remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] return callable.copy_modified( arg_types=arg_types, ret_type=expand_type(callable.ret_type, id_to_type), variables=remaining_tvars, )
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 []
def is_none(typ: Type): return is_same_type(typ, NoneTyp())
def _oas_handler_analyzer( specifications: Mapping[Path, oas_model.OASSpecification], f_ctx: FunctionContext, ) -> Type: # TODO(kornicameister) make `OASSpectification.operations` a mapping # to allow easy access oas_handler = f_ctx.arg_types[0][0] assert isinstance(oas_handler, CallableType) assert oas_handler.definition f_name = oas_handler.name oas_operation = _get_oas_operation( oas_handler.definition.fullname, specifications, ) if oas_operation is None: return errors.not_oas_handler( msg=f'{f_name} is not OAS operation', ctx=f_ctx, line_number=oas_handler.definition.line, ) signature: Dict[str, Type] = { k: v for k, v in dict(zip( oas_handler.arg_names, oas_handler.arg_types, ), ).items() if k is not None }.copy() sorted(signature) for oas_param, f_param in map( lambda ofp: (ofp, handler_model.get_f_param(ofp.name)), oas.operation_filter_parameters( oas_operation, 'path', 'query', ), ): handler_arg_type: Optional[Type] = signature.pop(f_param, None) handler_arg_default_value = _get_default_value(f_param, oas_handler) oas_default_values = oas.parameter_default_values(oas_param) if handler_arg_type is None: # log the fact that argument is not there _oas_handler_msg( f_ctx.api.msg.fail, f_ctx, ( errors.ERROR_INVALID_OAS_ARG, (f'{f_name} does not declare OAS {oas.parameter_in(oas_param)} ' f'{oas_param.name}::{f_param} argument'), ), ) continue oas_param_type = transform_parameter_to_type( oas_param, get_proper_type(handler_arg_type), handler_arg_default_value, f_ctx, ) if not any(( is_same_type(handler_arg_type, oas_param_type), is_equivalent_type(handler_arg_type, oas_param_type), )): errors.invalid_argument( msg=(f'[{f_name}({f_param} -> {oas_param.name})] ' f'expected {format_type(oas_param_type)}, ' f'but got {format_type(handler_arg_type)}'), ctx=f_ctx, line_number=handler_arg_type.line, ) continue # validate default value if handler_arg_default_value is not _ARG_NO_DEFAULT_VALUE_MARKER: if len(oas_default_values): default_matches = handler_arg_default_value in oas_default_values if not default_matches: errors.invalid_default_value( msg= (f'[{f_name}({f_param} -> {oas_param.name})] ' f'Incorrect default value. ' f'Expected {",".join(map(str, oas_default_values))} ' f'but got {handler_arg_default_value}'), ctx=f_ctx, line_number=handler_arg_type.line, ) continue elif not isinstance(handler_arg_default_value, type(None)): errors.default_value_not_in_oas( msg= (f'[{f_name}({f_param} -> {oas_param.name})] ' f'OAS does not define a default value. ' f'If you want "{handler_arg_default_value}" to be ' f'consistent default value, it should be declared in OAS too. ' ), ctx=f_ctx, line_number=handler_arg_type.line, ) elif oas_default_values: errors.invalid_default_value( msg= (f'[{f_name}({f_param} -> {oas_param.name})] OAS ' f'defines "{",".join(map(str, oas_default_values))}" as a ' f'default value. It should be reflected in argument default value.' ), ctx=f_ctx, line_number=handler_arg_type.line, ) continue if signature: # unconsumed handler arguments ... return f_ctx.default_return_type
def has_no_typevars(typ: Type) -> bool: return is_same_type(typ, erase_typevars(typ))
def is_protocol_implementation(left: Instance, right: Instance, proper_subtype: bool = False) -> bool: """Check whether 'left' implements the protocol 'right'. If 'proper_subtype' is True, then check for a proper subtype. Treat recursive protocols by using the 'assuming' structural subtype matrix (in sparse representation, i.e. as a list of pairs (subtype, supertype)), see also comment in nodes.TypeInfo. When we enter a check for classes (A, P), defined as following:: class P(Protocol): def f(self) -> P: ... class A: def f(self) -> A: ... this results in A being a subtype of P without infinite recursion. On every false result, we pop the assumption, thus avoiding an infinite recursion as well. """ assert right.type.is_protocol # We need to record this check to generate protocol fine-grained dependencies. TypeState.record_protocol_subtype_check(left.type, right.type) assuming = right.type.assuming_proper if proper_subtype else right.type.assuming for (l, r) in reversed(assuming): if sametypes.is_same_type(l, left) and sametypes.is_same_type( r, right): return True with pop_on_exit(assuming, left, right): for member in right.type.protocol_members: # nominal subtyping currently ignores '__init__' and '__new__' signatures if member in ('__init__', '__new__'): continue # The third argument below indicates to what self type is bound. # We always bind self to the subtype. (Similarly to nominal types). supertype = find_member(member, right, left) assert supertype is not None subtype = find_member(member, left, left) # Useful for debugging: # print(member, 'of', left, 'has type', subtype) # print(member, 'of', right, 'has type', supertype) if not subtype: return False if not proper_subtype: # Nominal check currently ignores arg names is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=True) else: is_compat = is_proper_subtype(subtype, supertype) if not is_compat: return False if isinstance(subtype, NoneTyp) and isinstance( supertype, CallableType): # We want __hash__ = None idiom to work even without --strict-optional return False subflags = get_member_flags(member, left.type) superflags = get_member_flags(member, right.type) if IS_SETTABLE in superflags: # Check opposite direction for settable attributes. if not is_subtype(supertype, subtype): return False if (IS_CLASSVAR in subflags) != (IS_CLASSVAR in superflags): return False if IS_SETTABLE in superflags and IS_SETTABLE not in subflags: return False # This rule is copied from nominal check in checker.py if IS_CLASS_OR_STATIC in superflags and IS_CLASS_OR_STATIC not in subflags: return False if proper_subtype: TypeState.record_proper_subtype_cache_entry(left, right) else: TypeState.record_subtype_cache_entry(left, right) return True