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 NoneType() 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, 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 narrowed.args)): return declared return meet_types(declared, narrowed) return narrowed
def typed_dict_get_callback(ctx: MethodContext) -> Type: """Infer a precise return type for TypedDict.get with literal first argument.""" if (isinstance(ctx.type, TypedDictType) and len(ctx.arg_types) >= 1 and len(ctx.arg_types[0]) == 1): key = try_getting_str_literal(ctx.args[0][0], ctx.arg_types[0][0]) if key is None: return ctx.default_return_type value_type = ctx.type.items.get(key) if value_type: if len(ctx.arg_types) == 1: return UnionType.make_simplified_union([value_type, NoneTyp()]) elif (len(ctx.arg_types) == 2 and len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1): default_arg = ctx.args[1][0] if (isinstance(default_arg, DictExpr) and len(default_arg.items) == 0 and isinstance(value_type, TypedDictType)): # Special case '{}' as the default for a typed dict type. return value_type.copy_modified(required_keys=set()) else: return UnionType.make_simplified_union([value_type, ctx.arg_types[1][0]]) else: ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) return AnyType(TypeOfAny.from_error) return ctx.default_return_type
def typed_dict_pop_callback(ctx: MethodContext) -> Type: """Type check and infer a precise return type for TypedDict.pop.""" if (isinstance(ctx.type, TypedDictType) and len(ctx.arg_types) >= 1 and len(ctx.arg_types[0]) == 1): keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0]) if keys is None: ctx.api.fail(message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, ctx.context) return AnyType(TypeOfAny.from_error) value_types = [] for key in keys: if key in ctx.type.required_keys: ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, ctx.context) value_type = ctx.type.items.get(key) if value_type: value_types.append(value_type) else: ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) return AnyType(TypeOfAny.from_error) if len(ctx.args[1]) == 0: return UnionType.make_simplified_union(value_types) elif (len(ctx.arg_types) == 2 and len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1): return UnionType.make_simplified_union([*value_types, ctx.arg_types[1][0]]) return ctx.default_return_type
def typed_dict_get_callback(ctx: MethodContext) -> Type: """Infer a precise return type for TypedDict.get with literal first argument.""" if (isinstance(ctx.type, TypedDictType) and len(ctx.arg_types) >= 1 and len(ctx.arg_types[0]) == 1): if isinstance(ctx.args[0][0], StrExpr): key = ctx.args[0][0].value value_type = ctx.type.items.get(key) if value_type: if len(ctx.arg_types) == 1: return UnionType.make_simplified_union( [value_type, NoneTyp()]) elif (len(ctx.arg_types) == 2 and len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1): default_arg = ctx.args[1][0] if (isinstance(default_arg, DictExpr) and len(default_arg.items) == 0 and isinstance(value_type, TypedDictType)): # Special case '{}' as the default for a typed dict type. return value_type.copy_modified(required_keys=set()) else: return UnionType.make_simplified_union( [value_type, ctx.arg_types[1][0]]) else: ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) return AnyType(TypeOfAny.from_error) return ctx.default_return_type
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 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 typed_dict_get_signature_callback(ctx: MethodSigContext) -> CallableType: """Try to infer a better signature type for TypedDict.get. This is used to get better type context for the second argument that depends on a TypedDict value type. """ signature = ctx.default_signature if (isinstance(ctx.type, TypedDictType) and len(ctx.args) == 2 and len(ctx.args[0]) == 1 and isinstance(ctx.args[0][0], StrExpr) and len(signature.arg_types) == 2 and len(signature.variables) == 1 and len(ctx.args[1]) == 1): key = ctx.args[0][0].value value_type = ctx.type.items.get(key) ret_type = signature.ret_type if value_type: default_arg = ctx.args[1][0] if (isinstance(value_type, TypedDictType) and isinstance(default_arg, DictExpr) and len(default_arg.items) == 0): # Caller has empty dict {} as default for typed dict. value_type = value_type.copy_modified(required_keys=set()) # Tweak the signature to include the value type as context. It's # only needed for type inference since there's a union with a type # variable that accepts everything. tv = TypeVarType(signature.variables[0]) return signature.copy_modified( arg_types=[signature.arg_types[0], UnionType.make_simplified_union([value_type, tv])], ret_type=ret_type) return signature
def join_simple(declaration: Type, s: Type, t: Type) -> Type: """Return a simple least upper bound given the declared type.""" if isinstance(s, AnyType): return s if isinstance(s, NoneTyp) and not isinstance(t, Void): return t if isinstance(s, ErasedType): return t if is_subtype(s, t): return t if is_subtype(t, s): return s if isinstance(declaration, UnionType): return UnionType.make_simplified_union([s, t]) value = t.accept(TypeJoinVisitor(s)) if value is None: # XXX this code path probably should be avoided. # It seems to happen when a line (x = y) is a type error, and # it's not clear that assuming that x is arbitrary afterward # is a good idea. return declaration if declaration is None or is_subtype(value, declaration): return value return declaration
def _autoconvertible_to_cdata(tp: Type, api: 'mypy.plugin.CheckerPluginInterface') -> Type: """Get a type that is compatible with all types that can be implicitly converted to the given CData type. Examples: * c_int -> Union[c_int, int] * c_char_p -> Union[c_char_p, bytes, int, NoneType] * MyStructure -> MyStructure """ allowed_types = [] # If tp is a union, we allow all types that are convertible to at least one of the union # items. This is not quite correct - strictly speaking, only types convertible to *all* of the # union items should be allowed. This may be worth changing in the future, but the more # correct algorithm could be too strict to be useful. for t in union_items(tp): # Every type can be converted from itself (obviously). allowed_types.append(t) if isinstance(t, Instance): unboxed = _find_simplecdata_base_arg(t, api) if unboxed is not None: # If _SimpleCData appears in tp's (direct or indirect) bases, its type argument # specifies the type's "unboxed" version, which can always be converted back to # the original "boxed" type. allowed_types.append(unboxed) if t.type.has_base('ctypes._PointerLike'): # Pointer-like _SimpleCData subclasses can also be converted from # an int or None. allowed_types.append(api.named_generic_type('builtins.int', [])) allowed_types.append(NoneTyp()) return UnionType.make_simplified_union(allowed_types)
def join_simple(declaration: Type, s: Type, t: Type) -> Type: """Return a simple least upper bound given the declared type.""" if isinstance(s, AnyType): return s if isinstance(s, NoneTyp) and not isinstance(t, Void): return t if isinstance(s, ErasedType): return t if is_subtype(s, t): return t if is_subtype(t, s): return s if isinstance(declaration, UnionType): return UnionType.make_simplified_union([s, t]) value = t.accept(TypeJoinVisitor(s)) if value is None: # XXX this code path probably should be avoided. # It seems to happen when a line (x = y) is a type error, and # it's not clear that assuming that x is arbitrary afterward # is a good idea. return declaration if declaration is None or is_subtype(value, declaration): return value return declaration
def typed_dict_setdefault_callback(ctx: MethodContext) -> Type: """Type check TypedDict.setdefault and infer a precise return type.""" if (isinstance(ctx.type, TypedDictType) and len(ctx.arg_types) == 2 and len(ctx.arg_types[0]) == 1 and len(ctx.arg_types[1]) == 1): keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0]) if keys is None: ctx.api.fail(message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, ctx.context) return AnyType(TypeOfAny.from_error) default_type = ctx.arg_types[1][0] value_types = [] for key in keys: value_type = ctx.type.items.get(key) if value_type is None: ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) return AnyType(TypeOfAny.from_error) # The signature_callback above can't always infer the right signature # (e.g. when the expression is a variable that happens to be a Literal str) # so we need to handle the check ourselves here and make sure the provided # default can be assigned to all key-value pairs we're updating. if not is_subtype(default_type, value_type): ctx.api.msg.typeddict_setdefault_arguments_inconsistent( default_type, value_type, ctx.context) return AnyType(TypeOfAny.from_error) value_types.append(value_type) return UnionType.make_simplified_union(value_types) return ctx.default_return_type
def typed_dict_pop_callback(ctx: MethodContext) -> Type: """Type check and infer a precise return type for TypedDict.pop.""" if (isinstance(ctx.type, TypedDictType) and len(ctx.arg_types) >= 1 and len(ctx.arg_types[0]) == 1): if isinstance(ctx.args[0][0], StrExpr): key = ctx.args[0][0].value if key in ctx.type.required_keys: ctx.api.msg.typeddict_key_cannot_be_deleted( ctx.type, key, ctx.context) value_type = ctx.type.items.get(key) if value_type: if len(ctx.args[1]) == 0: return value_type elif (len(ctx.arg_types) == 2 and len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1): return UnionType.make_simplified_union( [value_type, ctx.arg_types[1][0]]) else: ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) return AnyType(TypeOfAny.from_error) else: ctx.api.fail(messages.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, ctx.context) return AnyType(TypeOfAny.from_error) return ctx.default_return_type
def visit_type_var(self, left: TypeVarType) -> bool: right = self.right if isinstance(right, TypeVarType) and left.id == right.id: return True if left.values and is_subtype(UnionType.make_simplified_union(left.values), right): return True return is_subtype(left.upper_bound, self.right)
def _autoconvertible_to_cdata( tp: Type, api: 'mypy.plugin.CheckerPluginInterface') -> Type: """Get a type that is compatible with all types that can be implicitly converted to the given CData type. Examples: * c_int -> Union[c_int, int] * c_char_p -> Union[c_char_p, bytes, int, NoneType] * MyStructure -> MyStructure """ allowed_types = [] # If tp is a union, we allow all types that are convertible to at least one of the union # items. This is not quite correct - strictly speaking, only types convertible to *all* of the # union items should be allowed. This may be worth changing in the future, but the more # correct algorithm could be too strict to be useful. for t in union_items(tp): # Every type can be converted from itself (obviously). allowed_types.append(t) if isinstance(t, Instance): unboxed = _find_simplecdata_base_arg(t, api) if unboxed is not None: # If _SimpleCData appears in tp's (direct or indirect) bases, its type argument # specifies the type's "unboxed" version, which can always be converted back to # the original "boxed" type. allowed_types.append(unboxed) if t.type.has_base('ctypes._PointerLike'): # Pointer-like _SimpleCData subclasses can also be converted from # an int or None. allowed_types.append( api.named_generic_type('builtins.int', [])) allowed_types.append( api.named_generic_type('builtins.NoneType', [])) return UnionType.make_simplified_union(allowed_types)
def typed_dict_pop_signature_callback(ctx: MethodSigContext) -> CallableType: """Try to infer a better signature type for TypedDict.pop. This is used to get better type context for the second argument that depends on a TypedDict value type. """ signature = ctx.default_signature str_type = ctx.api.named_generic_type('builtins.str', []) if (isinstance(ctx.type, TypedDictType) and len(ctx.args) == 2 and len(ctx.args[0]) == 1 and isinstance(ctx.args[0][0], StrExpr) and len(signature.arg_types) == 2 and len(signature.variables) == 1 and len(ctx.args[1]) == 1): key = ctx.args[0][0].value value_type = ctx.type.items.get(key) if value_type: # Tweak the signature to include the value type as context. It's # only needed for type inference since there's a union with a type # variable that accepts everything. tv = TypeVarType(signature.variables[0]) typ = UnionType.make_simplified_union([value_type, tv]) return signature.copy_modified( arg_types=[str_type, typ], ret_type=typ) return signature.copy_modified(arg_types=[str_type, signature.arg_types[1]])
def typed_dict_get_callback(ctx: MethodContext) -> Type: """Infer a precise return type for TypedDict.get with literal first argument.""" if (isinstance(ctx.type, TypedDictType) and len(ctx.arg_types) >= 1 and len(ctx.arg_types[0]) == 1): keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0]) if keys is None: return ctx.default_return_type output_types = [] for key in keys: value_type = ctx.type.items.get(key) if value_type is None: ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) return AnyType(TypeOfAny.from_error) if len(ctx.arg_types) == 1: output_types.append(value_type) elif (len(ctx.arg_types) == 2 and len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1): default_arg = ctx.args[1][0] if (isinstance(default_arg, DictExpr) and len(default_arg.items) == 0 and isinstance(value_type, TypedDictType)): # Special case '{}' as the default for a typed dict type. output_types.append(value_type.copy_modified(required_keys=set())) else: output_types.append(value_type) output_types.append(ctx.arg_types[1][0]) if len(ctx.arg_types) == 1: output_types.append(NoneType()) return UnionType.make_simplified_union(output_types) return ctx.default_return_type
def join_simple(declaration: Optional[Type], s: Type, t: Type) -> Type: """Return a simple least upper bound given the declared type.""" if (s.can_be_true, s.can_be_false) != (t.can_be_true, t.can_be_false): # if types are restricted in different ways, use the more general versions s = true_or_false(s) t = true_or_false(t) if isinstance(s, AnyType): return s if isinstance(s, ErasedType): return t if is_proper_subtype(s, t): return t if is_proper_subtype(t, s): return s if isinstance(declaration, UnionType): return UnionType.make_simplified_union([s, t]) if isinstance(s, NoneType) and not isinstance(t, NoneType): s, t = t, s if isinstance(s, UninhabitedType) and not isinstance(t, UninhabitedType): s, t = t, s value = t.accept(TypeJoinVisitor(s)) if declaration is None or is_subtype(value, declaration): return value return declaration
def typed_dict_get_signature_callback( object_type: Type, args: List[List[Expression]], signature: CallableType, named_generic_type: Callable[[str, List[Type]], Type]) -> CallableType: """Try to infer a better signature type for TypedDict.get. This is used to get better type context for the second argument that depends on a TypedDict value type. """ if (isinstance(object_type, TypedDictType) and len(args) == 2 and len(args[0]) == 1 and isinstance(args[0][0], StrExpr) and len(signature.arg_types) == 2 and len(signature.variables) == 1): key = args[0][0].value value_type = object_type.items.get(key) if value_type: # Tweak the signature to include the value type as context. It's # only needed for type inference since there's a union with a type # variable that accepts everything. tv = TypeVarType(signature.variables[0]) return signature.copy_modified(arg_types=[ signature.arg_types[0], UnionType.make_simplified_union([value_type, tv]) ]) return signature
def typed_dict_get_signature_callback(ctx: MethodSigContext) -> CallableType: """Try to infer a better signature type for TypedDict.get. This is used to get better type context for the second argument that depends on a TypedDict value type. """ signature = ctx.default_signature if (isinstance(ctx.type, TypedDictType) and len(ctx.args) == 2 and len(ctx.args[0]) == 1 and isinstance(ctx.args[0][0], StrExpr) and len(signature.arg_types) == 2 and len(signature.variables) == 1 and len(ctx.args[1]) == 1): key = ctx.args[0][0].value value_type = ctx.type.items.get(key) ret_type = signature.ret_type if value_type: default_arg = ctx.args[1][0] if (isinstance(value_type, TypedDictType) and isinstance(default_arg, DictExpr) and len(default_arg.items) == 0): # Caller has empty dict {} as default for typed dict. value_type = value_type.copy_modified(required_keys=set()) # Tweak the signature to include the value type as context. It's # only needed for type inference since there's a union with a type # variable that accepts everything. tv = TypeVarType(signature.variables[0]) return signature.copy_modified(arg_types=[ signature.arg_types[0], UnionType.make_simplified_union([value_type, tv]) ], ret_type=ret_type) return signature
def meet_simple_away(s: Type, t: Type) -> Type: if isinstance(s, UnionType): return UnionType.make_simplified_union([x for x in s.items if not is_subtype(x, t)]) elif not isinstance(s, AnyType) and is_subtype(s, t): return Void() else: return s
def meet_simple_away(s: Type, t: Type) -> Type: if isinstance(s, UnionType): return UnionType.make_simplified_union([x for x in s.items if not is_subtype(x, t)]) elif not isinstance(s, AnyType) and is_subtype(s, t): return Void() else: return s
def analyze_union_member_access(name: str, typ: UnionType, mx: MemberContext) -> Type: mx.msg.disable_type_names += 1 results = [ _analyze_member_access(name, subtype, mx) for subtype in typ.relevant_items() ] mx.msg.disable_type_names -= 1 return UnionType.make_simplified_union(results)
def visit_union_type(self, t: UnionType) -> Type: if isinstance(self.s, UnionType): meets = [] # type: List[Type] for x in t.items: for y in self.s.items: meets.append(meet_types(x, y)) else: meets = [meet_types(x, self.s) for x in t.items] return UnionType.make_simplified_union(meets)
def visit_union_type(self, t: UnionType) -> Type: if isinstance(self.s, UnionType): meets = [] # type: List[Type] for x in t.items: for y in self.s.items: meets.append(meet_types(x, y)) else: meets = [meet_types(x, self.s) for x in t.items] return UnionType.make_simplified_union(meets)
def visit_none_type(self, t: NoneTyp) -> Type: if experiments.STRICT_OPTIONAL: if isinstance(self.s, (NoneTyp, UninhabitedType)): return t elif isinstance(self.s, UnboundType): return AnyType(TypeOfAny.special_form) else: return UnionType.make_simplified_union([self.s, t]) else: return self.s
def visit_none_type(self, t: NoneTyp) -> Type: if state.strict_optional: if isinstance(self.s, (NoneTyp, UninhabitedType)): return t elif isinstance(self.s, UnboundType): return AnyType(TypeOfAny.special_form) else: return UnionType.make_simplified_union([self.s, t]) else: return self.s
def visit_none_type(self, t: NoneTyp) -> Type: if experiments.STRICT_OPTIONAL: if isinstance(self.s, (NoneTyp, UninhabitedType)): return t elif isinstance(self.s, UnboundType): return AnyType(TypeOfAny.special_form) else: return UnionType.make_simplified_union([self.s, t]) else: return self.s
def analyze_union_member_access(name: str, typ: UnionType, mx: MemberContext) -> Type: mx.msg.disable_type_names += 1 results = [] for subtype in typ.relevant_items(): # Self types should be bound to every individual item of a union. item_mx = mx.copy_modified(self_type=subtype) results.append(_analyze_member_access(name, subtype, item_mx)) mx.msg.disable_type_names -= 1 return UnionType.make_simplified_union(results)
def visit_none_type(self, t: NoneTyp) -> Type: if state.strict_optional: if isinstance(self.s, (NoneTyp, UninhabitedType)): return t elif isinstance(self.s, UnboundType): return AnyType(TypeOfAny.special_form) else: return UnionType.make_simplified_union([self.s, t]) else: return self.s
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 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): return Void() else: if default_right: return t else: return s
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 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): return Void() else: if default_right: return t else: return s
def typed_dict_get_callback(object_type: Type, arg_types: List[List[Type]], args: List[List[Expression]], inferred_return_type: Type, context: PluginContext) -> Type: """Infer a precise return type for TypedDict.get with literal first argument.""" if (isinstance(object_type, TypedDictType) and len(arg_types) >= 1 and len(arg_types[0]) == 1): if isinstance(args[0][0], StrExpr): key = args[0][0].value value_type = object_type.items.get(key) if value_type: if len(arg_types) == 1: return UnionType.make_simplified_union( [value_type, NoneTyp()]) elif len(arg_types) == 2 and len(arg_types[1]) == 1: return UnionType.make_simplified_union( [value_type, arg_types[1][0]]) else: context.msg.typeddict_item_name_not_found( object_type, key, context.context) return AnyType() return inferred_return_type
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 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 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 visit_none_type(self, t: NoneTyp) -> Type: if experiments.STRICT_OPTIONAL: if isinstance(self.s, (NoneTyp, UninhabitedType)): return t elif isinstance(self.s, UnboundType): return AnyType() elif isinstance(self.s, Void) or isinstance(self.s, ErrorType): return ErrorType() else: return UnionType.make_simplified_union([self.s, t]) else: if not isinstance(self.s, Void): return self.s else: return self.default(self.s)
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 visit_none_type(self, t: NoneTyp) -> Type: if experiments.STRICT_OPTIONAL: if isinstance(self.s, (NoneTyp, UninhabitedType)): return t elif isinstance(self.s, UnboundType): return AnyType() elif isinstance(self.s, Void) or isinstance(self.s, ErrorType): return ErrorType() else: return UnionType.make_simplified_union([self.s, t]) else: if not isinstance(self.s, Void): return self.s else: return self.default(self.s)
def array_raw_callback(ctx: 'mypy.plugin.AttributeContext') -> Type: """Callback to provide an accurate type for ctypes.Array.raw.""" et = _get_array_element_type(ctx.type) if et is not None: types = [] # type: List[Type] for tp in union_items(et): if (isinstance(tp, AnyType) or isinstance(tp, Instance) and tp.type.fullname() == 'ctypes.c_char'): types.append(_get_bytes_type(ctx.api)) else: ctx.api.msg.fail( 'ctypes.Array attribute "raw" is only available' ' with element type c_char, not "{}"'.format(et), ctx.context) return UnionType.make_simplified_union(types) return ctx.default_attr_type
def array_raw_callback(ctx: 'mypy.plugin.AttributeContext') -> Type: """Callback to provide an accurate type for ctypes.Array.raw.""" et = _get_array_element_type(ctx.type) if et is not None: types = [] # type: List[Type] for tp in union_items(et): if (isinstance(tp, AnyType) or isinstance(tp, Instance) and tp.type.fullname() == 'ctypes.c_char'): types.append(_get_bytes_type(ctx.api)) else: ctx.api.msg.fail( 'ctypes.Array attribute "raw" is only available' ' with element type c_char, not "{}"' .format(et), ctx.context) return UnionType.make_simplified_union(types) return ctx.default_attr_type
def open_return_type(api: CheckerPluginInterface, args: List[List[Expression]]) -> Optional[Type]: def return_type(word: Literal["Text", "Buffered", "Raw"]) -> Type: return api.named_generic_type( "typing.Awaitable", [api.named_generic_type("trio._Async{}IOBase".format(word), [])], ) if len(args) < 2 or len(args[1]) == 0: # If mode is unspecified, the default is text return return_type("Text") if len(args[1]) == 1: # Mode was specified mode_arg = args[1][0] if isinstance(mode_arg, StrExpr): # Mode is a string constant if "b" not in mode_arg.value: # If there's no "b" in it, it's a text mode return return_type("Text") # Otherwise it's binary -- determine whether buffered or not if len(args) >= 3 and len(args[2]) == 1: # Buffering was specified buffering_arg = args[2][0] if isinstance(buffering_arg, IntExpr): # Buffering is a constant -- zero means # unbuffered, otherwise buffered if buffering_arg.value == 0: return return_type("Raw") return return_type("Buffered") # Not a constant, so we're not sure which it is. options = [ api.named_generic_type("trio._AsyncRawIOBase", []), api.named_generic_type("trio._AsyncBufferedIOBase", []), ] # type: List[Type] return api.named_generic_type( "typing.Awaitable", [UnionType.make_simplified_union(options)]) else: # Buffering is default if not specified return return_type("Buffered") # Mode wasn't a constant or we couldn't make sense of it return None
def _autounboxed_cdata(tp: Type) -> Type: """Get the auto-unboxed version of a CData type, if applicable. For *direct* _SimpleCData subclasses, the only type argument of _SimpleCData in the bases list is returned. For all other CData types, including indirect _SimpleCData subclasses, tp is returned as-is. """ if isinstance(tp, UnionType): return UnionType.make_simplified_union([_autounboxed_cdata(t) for t in tp.items]) elif isinstance(tp, Instance): for base in tp.type.bases: if base.type.fullname() == 'ctypes._SimpleCData': # If tp has _SimpleCData as a direct base class, # the auto-unboxed type is the single type argument of the _SimpleCData type. assert len(base.args) == 1 return base.args[0] # If tp is not a concrete type, or if there is no _SimpleCData in the bases, # the type is not auto-unboxed. return tp
def join_simple(declaration: Optional[Type], s: Type, t: Type) -> Type: """Return a simple least upper bound given the declared type.""" if (s.can_be_true, s.can_be_false) != (t.can_be_true, t.can_be_false): # if types are restricted in different ways, use the more general versions s = true_or_false(s) t = true_or_false(t) if isinstance(s, AnyType): return s if isinstance(s, ErasedType): return t if is_proper_subtype(s, t): return t if is_proper_subtype(t, s): return s if isinstance(declaration, UnionType): return UnionType.make_simplified_union([s, t]) if isinstance(s, NoneTyp) and not isinstance(t, NoneTyp): s, t = t, s if isinstance(s, UninhabitedType) and not isinstance(t, UninhabitedType): s, t = t, s value = t.accept(TypeJoinVisitor(s)) if value is None: # XXX this code path probably should be avoided. # It seems to happen when a line (x = y) is a type error, and # it's not clear that assuming that x is arbitrary afterward # is a good idea. return declaration if declaration is None or is_subtype(value, declaration): return value return declaration
def join_simple(declaration: Optional[Type], s: Type, t: Type) -> Type: """Return a simple least upper bound given the declared type.""" if (s.can_be_true, s.can_be_false) != (t.can_be_true, t.can_be_false): # if types are restricted in different ways, use the more general versions s = true_or_false(s) t = true_or_false(t) if isinstance(s, AnyType): return s if isinstance(s, ErasedType): return t if is_proper_subtype(s, t): return t if is_proper_subtype(t, s): return s if isinstance(declaration, UnionType): return UnionType.make_simplified_union([s, t]) if isinstance(s, NoneTyp) and not isinstance(t, NoneTyp): s, t = t, s if isinstance(s, UninhabitedType) and not isinstance(t, UninhabitedType): s, t = t, s value = t.accept(TypeJoinVisitor(s)) if value is None: # XXX this code path probably should be avoided. # It seems to happen when a line (x = y) is a type error, and # it's not clear that assuming that x is arbitrary afterward # is a good idea. return declaration if declaration is None or is_subtype(value, declaration): return value return declaration
def typed_dict_pop_callback(ctx: MethodContext) -> Type: """Type check and infer a precise return type for TypedDict.pop.""" if (isinstance(ctx.type, TypedDictType) and len(ctx.arg_types) >= 1 and len(ctx.arg_types[0]) == 1): key = try_getting_str_literal(ctx.args[0][0], ctx.arg_types[0][0]) if key is None: ctx.api.fail(message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, ctx.context) return AnyType(TypeOfAny.from_error) if key in ctx.type.required_keys: ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, ctx.context) value_type = ctx.type.items.get(key) if value_type: if len(ctx.args[1]) == 0: return value_type elif (len(ctx.arg_types) == 2 and len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1): return UnionType.make_simplified_union([value_type, ctx.arg_types[1][0]]) else: ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) return AnyType(TypeOfAny.from_error) return ctx.default_return_type
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 UnionType.make_simplified_union([self.visit_unbound_type(t), NoneTyp()]) sym = self.lookup(t.name, t) 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('Internal error (node is None, kind={})'.format(sym.kind), t) return AnyType() fullname = sym.node.fullname() if sym.kind == BOUND_TVAR: if len(t.args) > 0: self.fail('Type variable "{}" used with arguments'.format( t.name), t) assert sym.tvar_def is not None return TypeVarType(sym.tvar_def, t.line) elif fullname == 'builtins.None': if experiments.STRICT_OPTIONAL: return NoneTyp(is_ret_type=t.is_ret_type) else: return Void() elif fullname == 'typing.Any': return AnyType() elif fullname == 'typing.Tuple': if len(t.args) == 2 and isinstance(t.args[1], EllipsisType): # Tuple[T, ...] (uniform, variable-length tuple) node = self.lookup_fqn_func('builtins.tuple') tuple_info = cast(TypeInfo, node.node) return Instance(tuple_info, [t.args[0].accept(self)], t.line) return self.tuple_type(self.anal_array(t.args)) elif fullname == 'typing.Union': items = self.anal_array(t.args) items = [item for item in items if not isinstance(item, Void)] return UnionType.make_union(items) elif fullname == 'typing.Optional': if len(t.args) != 1: self.fail('Optional[...] must have exactly one type argument', t) return AnyType() items = self.anal_array(t.args) if experiments.STRICT_OPTIONAL: return UnionType.make_simplified_union([items[0], NoneTyp()]) else: # Without strict Optional checking Optional[t] is just an alias for t. return items[0] elif fullname == 'typing.Callable': return self.analyze_callable_type(t) elif fullname == 'typing.Type': if len(t.args) == 0: return TypeType(AnyType(), line=t.line) if len(t.args) != 1: self.fail('Type[...] must have exactly one type argument', t) items = self.anal_array(t.args) item = items[0] return TypeType(item, line=t.line) elif sym.kind == TYPE_ALIAS: # TODO: Generic type aliases. return sym.type_override 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() self.fail('Invalid type "{}"'.format(name), t) return t info = sym.node # type: TypeInfo if len(t.args) > 0 and info.fullname() == 'builtins.tuple': return TupleType(self.anal_array(t.args), Instance(info, [AnyType()], t.line), 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) tup = info.tuple_type if tup is None: return instance else: # The class has a Tuple[...] base class so it will be # represented as a tuple type. if t.args: self.fail('Generic tuple types not supported', t) return AnyType() return tup.copy_modified(items=self.anal_array(tup.items), fallback=instance) else: return AnyType()
def analyze_member_access(name: str, typ: Type, node: Context, is_lvalue: bool, is_super: bool, is_operator: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, *, original_type: Type, chk: 'mypy.checker.TypeChecker', override_info: Optional[TypeInfo] = None) -> Type: """Return the type of attribute `name` of typ. This is a general operation that supports various different variations: 1. lvalue or non-lvalue access (i.e. setter or getter access) 2. supertype access (when using super(); is_super == True and override_info should refer to the supertype) original_type is the most precise inferred or declared type of the base object that we have available. typ is generally a supertype of original_type. When looking for an attribute of typ, we may perform recursive calls targeting the fallback type, for example. original_type is always the type used in the initial call. """ # TODO: this and following functions share some logic with subtypes.find_member, # consider refactoring. if isinstance(typ, Instance): if name == '__init__' and not is_super: # Accessing __init__ in statically typed code would compromise # type safety unless used via super(). msg.fail(messages.CANNOT_ACCESS_INIT, node) return AnyType(TypeOfAny.from_error) # The base object has an instance type. info = typ.type if override_info: info = override_info if (experiments.find_occurrences and info.name() == experiments.find_occurrences[0] and name == experiments.find_occurrences[1]): msg.note("Occurrence of '{}.{}'".format(*experiments.find_occurrences), node) # Look up the member. First look up the method dictionary. method = info.get_method(name) if method: if method.is_property: assert isinstance(method, OverloadedFuncDef) first_item = cast(Decorator, method.items[0]) return analyze_var(name, first_item.var, typ, info, node, is_lvalue, msg, original_type, not_ready_callback, chk=chk) if is_lvalue: msg.cant_assign_to_method(node) signature = function_type(method, builtin_type('builtins.function')) signature = freshen_function_type_vars(signature) if name == '__new__': # __new__ is special and behaves like a static method -- don't strip # the first argument. pass else: signature = bind_self(signature, original_type) typ = map_instance_to_supertype(typ, method.info) member_type = expand_type_by_instance(signature, typ) freeze_type_vars(member_type) return member_type else: # Not a method. return analyze_member_var_access(name, typ, info, node, is_lvalue, is_super, builtin_type, not_ready_callback, msg, original_type=original_type, chk=chk) elif isinstance(typ, AnyType): # The base object has dynamic type. return AnyType(TypeOfAny.from_another_any, source_any=typ) elif isinstance(typ, NoneTyp): if chk.should_suppress_optional_error([typ]): return AnyType(TypeOfAny.from_error) # The only attribute NoneType has are those it inherits from object return analyze_member_access(name, builtin_type('builtins.object'), node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, original_type=original_type, chk=chk) elif isinstance(typ, UnionType): # The base object has dynamic type. msg.disable_type_names += 1 results = [analyze_member_access(name, subtype, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, original_type=original_type, chk=chk) for subtype in typ.relevant_items()] msg.disable_type_names -= 1 return UnionType.make_simplified_union(results) elif isinstance(typ, TupleType): # Actually look up from the fallback instance type. return analyze_member_access(name, typ.fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, original_type=original_type, chk=chk) elif isinstance(typ, TypedDictType): # Actually look up from the fallback instance type. return analyze_member_access(name, typ.fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, original_type=original_type, chk=chk) elif isinstance(typ, FunctionLike) and typ.is_type_obj(): # Class attribute. # TODO super? ret_type = typ.items()[0].ret_type if isinstance(ret_type, TupleType): ret_type = ret_type.fallback if isinstance(ret_type, Instance): if not is_operator: # When Python sees an operator (eg `3 == 4`), it automatically translates that # into something like `int.__eq__(3, 4)` instead of `(3).__eq__(4)` as an # optimization. # # While it normally it doesn't matter which of the two versions are used, it # does cause inconsistencies when working with classes. For example, translating # `int == int` to `int.__eq__(int)` would not work since `int.__eq__` is meant to # compare two int _instances_. What we really want is `type(int).__eq__`, which # is meant to compare two types or classes. # # This check makes sure that when we encounter an operator, we skip looking up # the corresponding method in the current instance to avoid this edge case. # See https://github.com/python/mypy/pull/1787 for more info. result = analyze_class_attribute_access(ret_type, name, node, is_lvalue, builtin_type, not_ready_callback, msg, original_type=original_type) if result: return result # Look up from the 'type' type. return analyze_member_access(name, typ.fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, original_type=original_type, chk=chk) else: assert False, 'Unexpected type {}'.format(repr(ret_type)) elif isinstance(typ, FunctionLike): # Look up from the 'function' type. return analyze_member_access(name, typ.fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, original_type=original_type, chk=chk) elif isinstance(typ, TypeVarType): return analyze_member_access(name, typ.upper_bound, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, original_type=original_type, chk=chk) elif isinstance(typ, DeletedType): msg.deleted_as_rvalue(typ, node) return AnyType(TypeOfAny.from_error) elif isinstance(typ, TypeType): # Similar to FunctionLike + is_type_obj() above. item = None fallback = builtin_type('builtins.type') ignore_messages = msg.copy() ignore_messages.disable_errors() if isinstance(typ.item, Instance): item = typ.item elif isinstance(typ.item, AnyType): return analyze_member_access(name, fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, ignore_messages, original_type=original_type, chk=chk) elif isinstance(typ.item, TypeVarType): if isinstance(typ.item.upper_bound, Instance): item = typ.item.upper_bound elif isinstance(typ.item, TupleType): item = typ.item.fallback elif isinstance(typ.item, FunctionLike) and typ.item.is_type_obj(): item = typ.item.fallback elif isinstance(typ.item, TypeType): # Access member on metaclass object via Type[Type[C]] if isinstance(typ.item.item, Instance): item = typ.item.item.type.metaclass_type if item and not is_operator: # See comment above for why operators are skipped result = analyze_class_attribute_access(item, name, node, is_lvalue, builtin_type, not_ready_callback, msg, original_type=original_type) if result: if not (isinstance(result, AnyType) and item.type.fallback_to_any): return result else: # We don't want errors on metaclass lookup for classes with Any fallback msg = ignore_messages if item is not None: fallback = item.type.metaclass_type or fallback return analyze_member_access(name, fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, original_type=original_type, chk=chk) if chk.should_suppress_optional_error([typ]): return AnyType(TypeOfAny.from_error) return msg.has_no_attr(original_type, typ, name, node)
def analyze_member_access( name: str, typ: Type, node: Context, is_lvalue: bool, is_super: bool, is_operator: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, override_info: TypeInfo = None, report_type: Type = None, chk: "mypy.checker.TypeChecker" = None, ) -> Type: """Analyse attribute access. This is a general operation that supports various different variations: 1. lvalue or non-lvalue access (i.e. setter or getter access) 2. supertype access (when using super(); is_super == True and override_info should refer to the supertype) """ report_type = report_type or typ if isinstance(typ, Instance): if name == "__init__" and not is_super: # Accessing __init__ in statically typed code would compromise # type safety unless used via super(). msg.fail(messages.CANNOT_ACCESS_INIT, node) return AnyType() # The base object has an instance type. info = typ.type if override_info: info = override_info # Look up the member. First look up the method dictionary. method = info.get_method(name) if method: if method.is_property: assert isinstance(method, OverloadedFuncDef) return analyze_var(name, method.items[0].var, typ, info, node, is_lvalue, msg, not_ready_callback) if is_lvalue: msg.cant_assign_to_method(node) typ = map_instance_to_supertype(typ, method.info) if name == "__new__": # __new__ is special and behaves like a static method -- don't strip # the first argument. signature = function_type(method, builtin_type("builtins.function")) else: signature = method_type_with_fallback(method, builtin_type("builtins.function")) return expand_type_by_instance(signature, typ) else: # Not a method. return analyze_member_var_access( name, typ, info, node, is_lvalue, is_super, builtin_type, not_ready_callback, msg, report_type=report_type, chk=chk, ) elif isinstance(typ, AnyType): # The base object has dynamic type. return AnyType() elif isinstance(typ, NoneTyp): if chk and chk.should_suppress_optional_error([typ]): return AnyType() # The only attribute NoneType has are those it inherits from object return analyze_member_access( name, builtin_type("builtins.object"), node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, report_type=report_type, chk=chk, ) elif isinstance(typ, UnionType): # The base object has dynamic type. msg.disable_type_names += 1 results = [ analyze_member_access( name, subtype, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, chk=chk ) for subtype in typ.items ] msg.disable_type_names -= 1 return UnionType.make_simplified_union(results) elif isinstance(typ, TupleType): # Actually look up from the fallback instance type. return analyze_member_access( name, typ.fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, chk=chk ) elif isinstance(typ, FunctionLike) and typ.is_type_obj(): # Class attribute. # TODO super? ret_type = typ.items()[0].ret_type if isinstance(ret_type, TupleType): ret_type = ret_type.fallback if isinstance(ret_type, Instance): if not is_operator: # When Python sees an operator (eg `3 == 4`), it automatically translates that # into something like `int.__eq__(3, 4)` instead of `(3).__eq__(4)` as an # optimation. # # While it normally it doesn't matter which of the two versions are used, it # does cause inconsistencies when working with classes. For example, translating # `int == int` to `int.__eq__(int)` would not work since `int.__eq__` is meant to # compare two int _instances_. What we really want is `type(int).__eq__`, which # is meant to compare two types or classes. # # This check makes sure that when we encounter an operator, we skip looking up # the corresponding method in the current instance to avoid this edge case. # See https://github.com/python/mypy/pull/1787 for more info. result = analyze_class_attribute_access( ret_type, name, node, is_lvalue, builtin_type, not_ready_callback, msg ) if result: return result # Look up from the 'type' type. return analyze_member_access( name, typ.fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, report_type=report_type, chk=chk, ) else: assert False, "Unexpected type {}".format(repr(ret_type)) elif isinstance(typ, FunctionLike): # Look up from the 'function' type. return analyze_member_access( name, typ.fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, report_type=report_type, chk=chk, ) elif isinstance(typ, TypeVarType): return analyze_member_access( name, typ.upper_bound, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, report_type=report_type, chk=chk, ) elif isinstance(typ, DeletedType): msg.deleted_as_rvalue(typ, node) return AnyType() elif isinstance(typ, TypeType): # Similar to FunctionLike + is_type_obj() above. item = None if isinstance(typ.item, Instance): item = typ.item elif isinstance(typ.item, TypeVarType): if isinstance(typ.item.upper_bound, Instance): item = typ.item.upper_bound if item and not is_operator: # See comment above for why operators are skipped result = analyze_class_attribute_access(item, name, node, is_lvalue, builtin_type, not_ready_callback, msg) if result: return result fallback = builtin_type("builtins.type") return analyze_member_access( name, fallback, node, is_lvalue, is_super, is_operator, builtin_type, not_ready_callback, msg, report_type=report_type, chk=chk, ) if chk and chk.should_suppress_optional_error([typ]): return AnyType() return msg.has_no_attr(report_type, name, node)
def visit_union_type(self, t: UnionType) -> Type: erased_items = [erase_type(item) for item in t.items] return UnionType.make_simplified_union(erased_items)
def visit_union_type(self, t: UnionType) -> Type: # After substituting for type variables in t.items, # some of the resulting types might be subtypes of others. return UnionType.make_simplified_union(self.expand_types(t.items), t.line)
def visit_union_type(self, t: UnionType) -> Type: if is_subtype(self.s, t): return t else: return UnionType.make_simplified_union([self.s, t])
def analyze_descriptor_access(instance_type: Type, descriptor_type: Type, builtin_type: Callable[[str], Instance], msg: MessageBuilder, context: Context, *, chk: 'mypy.checker.TypeChecker') -> Type: """Type check descriptor access. Arguments: instance_type: The type of the instance on which the descriptor attribute is being accessed (the type of ``a`` in ``a.f`` when ``f`` is a descriptor). descriptor_type: The type of the descriptor attribute being accessed (the type of ``f`` in ``a.f`` when ``f`` is a descriptor). context: The node defining the context of this inference. Return: The return type of the appropriate ``__get__`` overload for the descriptor. """ if isinstance(descriptor_type, UnionType): # Map the access over union types return UnionType.make_simplified_union([ analyze_descriptor_access(instance_type, typ, builtin_type, msg, context, chk=chk) for typ in descriptor_type.items ]) elif not isinstance(descriptor_type, Instance): return descriptor_type if not descriptor_type.type.has_readable_member('__get__'): return descriptor_type dunder_get = descriptor_type.type.get_method('__get__') if dunder_get is None: msg.fail(message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(descriptor_type), context) return AnyType(TypeOfAny.from_error) function = function_type(dunder_get, builtin_type('builtins.function')) bound_method = bind_self(function, descriptor_type) typ = map_instance_to_supertype(descriptor_type, dunder_get.info) dunder_get_type = expand_type_by_instance(bound_method, typ) if isinstance(instance_type, FunctionLike) and instance_type.is_type_obj(): owner_type = instance_type.items()[0].ret_type instance_type = NoneTyp() elif isinstance(instance_type, TypeType): owner_type = instance_type.item instance_type = NoneTyp() else: owner_type = instance_type _, inferred_dunder_get_type = chk.expr_checker.check_call( dunder_get_type, [TempNode(instance_type), TempNode(TypeType.make_normalized(owner_type))], [ARG_POS, ARG_POS], context) if isinstance(inferred_dunder_get_type, AnyType): # check_call failed, and will have reported an error return inferred_dunder_get_type if not isinstance(inferred_dunder_get_type, CallableType): msg.fail(message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(descriptor_type), context) return AnyType(TypeOfAny.from_error) return inferred_dunder_get_type.ret_type
def analyze_member_access(name: str, typ: Type, node: Context, is_lvalue: bool, is_super: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, override_info: TypeInfo = None, report_type: Type = None) -> Type: """Analyse attribute access. This is a general operation that supports various different variations: 1. lvalue or non-lvalue access (i.e. setter or getter access) 2. supertype access (when using super(); is_super == True and override_info should refer to the supertype) """ report_type = report_type or typ if isinstance(typ, Instance): if name == '__init__' and not is_super: # Accessing __init__ in statically typed code would compromise # type safety unless used via super(). msg.fail(messages.CANNOT_ACCESS_INIT, node) return AnyType() # The base object has an instance type. info = typ.type if override_info: info = override_info # Look up the member. First look up the method dictionary. method = info.get_method(name) if method: if method.is_property: assert isinstance(method, OverloadedFuncDef) method = cast(OverloadedFuncDef, method) return analyze_var(name, method.items[0].var, typ, info, node, is_lvalue, msg, not_ready_callback) if is_lvalue: msg.cant_assign_to_method(node) typ = map_instance_to_supertype(typ, method.info) if name == '__new__': # __new__ is special and behaves like a static method -- don't strip # the first argument. signature = function_type(method, builtin_type('builtins.function')) else: signature = method_type_with_fallback(method, builtin_type('builtins.function')) return expand_type_by_instance(signature, typ) else: # Not a method. return analyze_member_var_access(name, typ, info, node, is_lvalue, is_super, builtin_type, not_ready_callback, msg, report_type=report_type) elif isinstance(typ, AnyType): # The base object has dynamic type. return AnyType() elif isinstance(typ, UnionType): # The base object has dynamic type. msg.disable_type_names += 1 results = [analyze_member_access(name, subtype, node, is_lvalue, is_super, builtin_type, not_ready_callback, msg) for subtype in typ.items] msg.disable_type_names -= 1 return UnionType.make_simplified_union(results) elif isinstance(typ, TupleType): # Actually look up from the fallback instance type. return analyze_member_access(name, typ.fallback, node, is_lvalue, is_super, builtin_type, not_ready_callback, msg) elif isinstance(typ, FunctionLike) and typ.is_type_obj(): # Class attribute. # TODO super? ret_type = typ.items()[0].ret_type if isinstance(ret_type, TupleType): ret_type = ret_type.fallback if isinstance(ret_type, Instance): result = analyze_class_attribute_access(ret_type, name, node, is_lvalue, builtin_type, not_ready_callback, msg) if result: return result # Look up from the 'type' type. return analyze_member_access(name, typ.fallback, node, is_lvalue, is_super, builtin_type, not_ready_callback, msg, report_type=report_type) else: assert False, 'Unexpected type {}'.format(repr(ret_type)) elif isinstance(typ, FunctionLike): # Look up from the 'function' type. return analyze_member_access(name, typ.fallback, node, is_lvalue, is_super, builtin_type, not_ready_callback, msg, report_type=report_type) elif isinstance(typ, TypeVarType): return analyze_member_access(name, typ.upper_bound, node, is_lvalue, is_super, builtin_type, not_ready_callback, msg, report_type=report_type) elif isinstance(typ, DeletedType): msg.deleted_as_rvalue(typ, node) return AnyType() return msg.has_no_attr(report_type, name, node)
def simplify_union(t: Type) -> Type: if isinstance(t, UnionType): return UnionType.make_simplified_union(t.items) return t
def analyse_member_access(name: str, typ: Type, node: Context, is_lvalue: bool, is_super: bool, builtin_type: Function[[str], Instance], msg: MessageBuilder, override_info: TypeInfo = None, report_type: Type = None) -> Type: """Analyse attribute access. This is a general operation that supports various different variations: 1. lvalue or non-lvalue access (i.e. setter or getter access) 2. supertype access (when using super(); is_super == True and override_info should refer to the supertype) """ report_type = report_type or typ if isinstance(typ, Instance): if name == '__init__' and not is_super: # Accessing __init__ in statically typed code would compromise # type safety unless used via super(). msg.fail(messages.CANNOT_ACCESS_INIT, node) return AnyType() # The base object has an instance type. info = typ.type if override_info: info = override_info # Look up the member. First look up the method dictionary. method = info.get_method(name) if method: if is_lvalue: msg.cant_assign_to_method(node) typ = map_instance_to_supertype(typ, method.info) return expand_type_by_instance( method_type(method, builtin_type('builtins.function')), typ) else: # Not a method. return analyse_member_var_access(name, typ, info, node, is_lvalue, is_super, msg, report_type=report_type) elif isinstance(typ, AnyType): # The base object has dynamic type. return AnyType() elif isinstance(typ, UnionType): # The base object has dynamic type. msg.disable_type_names += 1 results = [analyse_member_access(name, subtype, node, is_lvalue, is_super, builtin_type, msg) for subtype in typ.items] msg.disable_type_names -= 1 return UnionType.make_simplified_union(results) elif isinstance(typ, TupleType): # Actually look up from the fallback instance type. return analyse_member_access(name, typ.fallback, node, is_lvalue, is_super, builtin_type, msg) elif (isinstance(typ, FunctionLike) and cast(FunctionLike, typ).is_type_obj()): # Class attribute. # TODO super? sig = cast(FunctionLike, typ) itype = cast(Instance, sig.items()[0].ret_type) result = analyse_class_attribute_access(itype, name, node, is_lvalue, builtin_type, msg) if result: return result # Look up from the 'type' type. return analyse_member_access(name, sig.fallback, node, is_lvalue, is_super, builtin_type, msg, report_type=report_type) elif isinstance(typ, FunctionLike): # Look up from the 'function' type. return analyse_member_access(name, typ.fallback, node, is_lvalue, is_super, builtin_type, msg, report_type=report_type) return msg.has_no_attr(report_type, name, node)
def analyze_union_member_access(name: str, typ: UnionType, mx: MemberContext) -> Type: mx.msg.disable_type_names += 1 results = [_analyze_member_access(name, subtype, mx) for subtype in typ.relevant_items()] mx.msg.disable_type_names -= 1 return UnionType.make_simplified_union(results)