def analyse_class_attribute_access(itype: Instance, name: str, context: Context, is_lvalue: bool, msg: MessageBuilder) -> Type: node = itype.type.get(name) if not node: return None is_decorated = isinstance(node.node, Decorator) is_method = is_decorated or isinstance(node.node, FuncDef) if is_lvalue: if is_method: msg.cant_assign_to_method(context) if isinstance(node.node, TypeInfo): msg.fail(messages.CANNOT_ASSIGN_TO_TYPE, context) t = node.type if t: is_classmethod = is_decorated and cast(Decorator, node.node).func.is_class return add_class_tvars(t, itype.type, is_classmethod) if isinstance(node.node, TypeInfo): # TODO add second argument return type_object_type(cast(TypeInfo, node.node), None) return function_type(cast(FuncBase, node.node))
def analyze_class_attribute_access(itype: Instance, name: str, context: Context, is_lvalue: bool, builtin_type: Callable[[str], Instance], msg: MessageBuilder) -> Type: node = itype.type.get(name) if not node: return None is_decorated = isinstance(node.node, Decorator) is_method = is_decorated or isinstance(node.node, FuncDef) if is_lvalue: if is_method: msg.cant_assign_to_method(context) if isinstance(node.node, TypeInfo): msg.fail(messages.CANNOT_ASSIGN_TO_TYPE, context) if itype.type.is_enum and not (is_lvalue or is_decorated or is_method): return itype t = node.type if t: is_classmethod = is_decorated and cast(Decorator, node.node).func.is_class return add_class_tvars(t, itype.type, is_classmethod, builtin_type) if isinstance(node.node, TypeInfo): return type_object_type(cast(TypeInfo, node.node), builtin_type) return function_type(cast(FuncBase, node.node), builtin_type('builtins.function'))
def transform_function_body(self, fdef: FuncDef) -> None: """Transform the body of a function.""" self.dynamic_funcs.append(fdef.is_implicit) # FIX overloads self.return_types.append(cast(Callable, function_type(fdef)).ret_type) super().visit_func_def(fdef) self.return_types.pop() self.dynamic_funcs.pop()
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 visit_overloaded_func_def(self, defn): t = [] for f in defn.items: f.is_overload = True f.accept(self) t.append(function_type(f)) defn.type = Overloaded(t) defn.type.line = defn.line if self.type: self.type.methods[defn.name()] = defn defn.info = self.type
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 get_wrapper_sig(self, act_as_func_def, is_dynamic): """Return the signature of the wrapper method. The wrapper method signature has an additional type variable argument (with type 'any'), and all type variables have been erased. """ sig = function_type(act_as_func_def) if is_dynamic: return dynamic_sig(sig) elif is_generic(act_as_func_def): return erase_generic_types(sig) # FIX REFACTOR? else: return sig
def get_target_sig(self, act_as_func_def, target_func_def, is_dynamic, is_wrapper_class): """Return the target method signature for a method wrapper.""" sig = function_type(target_func_def) if is_wrapper_class: if sig.is_generic() and is_dynamic: sig = translate_function_type_vars_to_dynamic(sig) return translate_type_vars_to_wrapped_object_vars(sig) elif is_dynamic: if sig.is_generic(): return translate_function_type_vars_to_dynamic(sig) else: return sig else: return sig
def get_wrapper_sig(self, act_as_func_def: FuncDef, is_dynamic: bool) -> Callable: """Return the signature of the wrapper method. The wrapper method signature has an additional type variable argument (with type 'Any'), and all type variables have been erased. """ sig = cast(Callable, function_type(act_as_func_def)) if is_dynamic: return dynamic_sig(sig) elif is_generic(act_as_func_def): return cast(Callable, erase_generic_types(sig)) # FIX REFACTOR? else: return sig
def analyze_class_attribute_access( itype: Instance, name: str, context: Context, is_lvalue: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, ) -> Type: node = itype.type.get(name) if not node: if itype.type.fallback_to_any: return AnyType() return None is_decorated = isinstance(node.node, Decorator) is_method = is_decorated or isinstance(node.node, FuncDef) if is_lvalue: if is_method: msg.cant_assign_to_method(context) if isinstance(node.node, TypeInfo): msg.fail(messages.CANNOT_ASSIGN_TO_TYPE, context) if itype.type.is_enum and not (is_lvalue or is_decorated or is_method): return itype t = node.type if t: if isinstance(t, PartialType): return handle_partial_attribute_type(t, is_lvalue, msg, node.node) is_classmethod = is_decorated and cast(Decorator, node.node).func.is_class return add_class_tvars(t, itype.type, is_classmethod, builtin_type) elif isinstance(node.node, Var): not_ready_callback(name, context) return AnyType() if isinstance(node.node, TypeInfo): return type_object_type(node.node, builtin_type) if isinstance(node.node, MypyFile): # Reference to a module object. return builtin_type("builtins.module") if is_decorated: # TODO: Return type of decorated function. This is quick hack to work around #998. return AnyType() else: return function_type(cast(FuncBase, node.node), builtin_type("builtins.function"))
def make_overload_check(self, f, fixed_args, rest_args): a = [] i = 0 if rest_args: a.append(self.make_argument_count_check(f, len(fixed_args), rest_args)) for t in function_type(f).arg_types: if not isinstance(t, Any) and (t.repr or isinstance(t, Callable)): a.append(self.make_argument_check( self.argument_ref(i, fixed_args, rest_args), t)) i += 1 if len(a) > 0: return ' and '.join(a) else: return 'True'
def analyze_class_attribute_access(itype: Instance, name: str, context: Context, is_lvalue: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder) -> Type: node = itype.type.get(name) if not node: if itype.type.fallback_to_any: return AnyType() return None is_decorated = isinstance(node.node, Decorator) is_method = is_decorated or isinstance(node.node, FuncDef) if is_lvalue: if is_method: msg.cant_assign_to_method(context) if isinstance(node.node, TypeInfo): msg.fail(messages.CANNOT_ASSIGN_TO_TYPE, context) if itype.type.is_enum and not (is_lvalue or is_decorated or is_method): return itype t = node.type if t: if isinstance(t, PartialType): return handle_partial_attribute_type(t, is_lvalue, msg, node.node) is_classmethod = is_decorated and cast(Decorator, node.node).func.is_class return add_class_tvars(t, itype.type, is_classmethod, builtin_type) elif isinstance(node.node, Var): not_ready_callback(name, context) return AnyType() if isinstance(node.node, TypeInfo): return type_object_type(node.node, builtin_type) if isinstance(node.node, MypyFile): # Reference to a module object. return builtin_type('builtins.module') if is_decorated: # TODO: Return type of decorated function. This is quick hack to work around #998. return AnyType() else: return function_type(cast(FuncBase, node.node), builtin_type('builtins.function'))
def transform_method_implementation(self, fdef, name): """Transform the implementation of a method (i.e. unwrapped).""" args = fdef.args arg_kinds = fdef.arg_kinds typ = function_type(fdef) init = fdef.init_expressions() if fdef.name() == '__init__' and is_generic(fdef): args, arg_kinds, init, typ = self.add_constructor_tvar_args( fdef, typ, args, arg_kinds, init) fdef2 = FuncDef(name, args, arg_kinds, init, fdef.body, typ) fdef2.info = fdef.info self.tf.prepend_generic_function_tvar_args(fdef2) return fdef2
def transform_method_implementation(self, fdef: FuncDef, name: str) -> FuncDef: """Transform the implementation of a method (i.e. unwrapped).""" args = fdef.args arg_kinds = fdef.arg_kinds typ = function_type(fdef) # type: Type init = fdef.init_expressions() if fdef.name() == '__init__' and is_generic(fdef): args, arg_kinds, init, typ = self.add_constructor_tvar_args( fdef, typ, args, arg_kinds, init) fdef2 = FuncDef(name, args, arg_kinds, init, fdef.body, typ) fdef2.info = fdef.info self.tf.prepend_generic_function_tvar_args(fdef2) return fdef2
def analyse_class_attribute_access(itype: Instance, name: str, context: Context, is_lvalue: bool, msg: MessageBuilder) -> Type: node = itype.type.get(name) if node: if is_lvalue and isinstance(node.node, FuncDef): msg.fail(messages.CANNOT_ASSIGN_TO_METHOD, context) if is_lvalue and isinstance(node.node, TypeInfo): msg.fail(messages.CANNOT_ASSIGN_TO_TYPE, context) t = node.type if t: return add_class_tvars(t, itype.type) elif isinstance(node.node, TypeInfo): # TODO add second argument return type_object_type(cast(TypeInfo, node.node), None) else: return function_type(cast(FuncBase, node.node)) else: return None
def get_target_sig(self, act_as_func_def: FuncDef, target_func_def: FuncDef, is_dynamic: bool, is_wrapper_class: bool) -> Callable: """Return the target method signature for a method wrapper.""" sig = cast(Callable, function_type(target_func_def)) if is_wrapper_class: if sig.is_generic() and is_dynamic: sig = cast(Callable, translate_function_type_vars_to_dynamic(sig)) return cast(Callable, translate_type_vars_to_wrapped_object_vars(sig)) elif is_dynamic: if sig.is_generic(): return cast(Callable, translate_function_type_vars_to_dynamic(sig)) else: return sig else: return sig
def get_call_sig(self, act_as_func_def: FuncDef, current_class: TypeInfo, is_dynamic: bool, is_wrapper_class: bool, is_override: bool) -> Callable: """Return the source signature in a wrapped call. It has type variables replaced with 'Any', but as an exception, type variables are intact in the return type in generic wrapper classes. The exception allows omitting an extra return value coercion, as the target return type and the source return type will be the same. """ sig = cast(Callable, function_type(act_as_func_def)) if is_dynamic: return dynamic_sig(sig) elif is_generic(act_as_func_def): call_sig = sig # If this is an override wrapper, keep type variables # intact. Otherwise replace them with dynamic to get # desired coercions that check argument types. if not is_override or is_wrapper_class: call_sig = (cast(Callable, replace_type_vars(call_sig, False))) else: call_sig = cast( Callable, map_type_from_supertype(call_sig, current_class, act_as_func_def.info)) if is_wrapper_class: # Replace return type with the original return within # wrapper classes to get rid of an unnecessary # coercion. There will still be a coercion due to the # extra coercion generated for generic wrapper # classes. However, function generic type variables # still need to be replaced, as the wrapper does not # affect them. ret = sig.ret_type if is_dynamic: ret = translate_function_type_vars_to_dynamic(ret) call_sig = replace_ret_type( call_sig, translate_type_vars_to_wrapper_vars(ret)) return call_sig else: return sig
def get_call_sig(self, act_as_func_def: FuncDef, current_class: TypeInfo, is_dynamic: bool, is_wrapper_class: bool, is_override: bool) -> Callable: """Return the source signature in a wrapped call. It has type variables replaced with 'Any', but as an exception, type variables are intact in the return type in generic wrapper classes. The exception allows omitting an extra return value coercion, as the target return type and the source return type will be the same. """ sig = cast(Callable, function_type(act_as_func_def)) if is_dynamic: return dynamic_sig(sig) elif is_generic(act_as_func_def): call_sig = sig # If this is an override wrapper, keep type variables # intact. Otherwise replace them with dynamic to get # desired coercions that check argument types. if not is_override or is_wrapper_class: call_sig = (cast(Callable, replace_type_vars(call_sig, False))) else: call_sig = cast(Callable, map_type_from_supertype( call_sig, current_class, act_as_func_def.info)) if is_wrapper_class: # Replace return type with the original return within # wrapper classes to get rid of an unnecessary # coercion. There will still be a coercion due to the # extra coercion generated for generic wrapper # classes. However, function generic type variables # still need to be replaced, as the wrapper does not # affect them. ret = sig.ret_type if is_dynamic: ret = translate_function_type_vars_to_dynamic(ret) call_sig = replace_ret_type( call_sig, translate_type_vars_to_wrapper_vars(ret)) return call_sig else: return sig
def prepend_generic_function_tvar_args(self, fdef: FuncDef) -> None: """Add implicit function type variable arguments if fdef is generic.""" sig = cast(Callable, function_type(fdef)) tvars = sig.variables if not fdef.type: fdef.type = sig tv = [] # type: List[Var] ntvars = len(tvars) if fdef.is_method(): # For methods, add type variable arguments after the self arg. for n in range(ntvars): tv.append(Var(tvar_arg_name(-1 - n))) fdef.type = add_arg_type_after_self(cast(Callable, fdef.type), AnyType()) fdef.args = [fdef.args[0]] + tv + fdef.args[1:] else: # For ordinary functions, prepend type variable arguments. for n in range(ntvars): tv.append(Var(tvar_arg_name(-1 - n))) fdef.type = prepend_arg_type(cast(Callable, fdef.type), AnyType()) fdef.args = tv + fdef.args fdef.init = List[AssignmentStmt]([None]) * ntvars + fdef.init
def prepend_generic_function_tvar_args(self, fdef): """Add implicit function type variable arguments if fdef is generic.""" sig = function_type(fdef) tvars = sig.variables.items if not fdef.type: fdef.type = sig tv = [] ntvars = len(tvars) if fdef.is_method(): # For methods, add type variable arguments after the self arg. for n in range(ntvars): tv.append(Var(tvar_arg_name(-1 - n))) fdef.type = add_arg_type_after_self(fdef.type, Any()) fdef.args = [fdef.args[0]] + tv + fdef.args[1:] else: # For ordinary functions, prepend type variable arguments. for n in range(ntvars): tv.append(Var(tvar_arg_name(-1 - n))) fdef.type = prepend_arg_type(fdef.type, Any()) fdef.args = tv + fdef.args fdef.init = [None] * ntvars + fdef.init
def check_func_item(self, defn, type_override=None): # We may be checking a function definition or an anonymous function. In # the first case, set up another reference with the precise type. fdef = None if isinstance(defn, FuncDef): fdef = defn self.dynamic_funcs.append(defn.type is None and not type_override) if fdef: self.errors.push_function(fdef.name()) typ = function_type(defn) if type_override: typ = type_override if isinstance(typ, Callable): self.check_func_def(defn, typ) else: raise RuntimeError('Not supported') if fdef: self.errors.pop_function() self.dynamic_funcs.pop()
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)
# Create a dynamically-typed method wrapper. res.append(self.dynamic_method_wrapper(fdef)) else: # Not an override. # Create a dynamically-typed method wrapper. res.append(self.dynamic_method_wrapper(fdef)) return res FuncDef transform_method_implementation(self, FuncDef fdef, str name): """Transform the implementation of a method (i.e. unwrapped).""" args = fdef.args arg_kinds = fdef.arg_kinds Type typ = function_type(fdef) init = fdef.init_expressions() if fdef.name() == '__init__' and is_generic(fdef): args, arg_kinds, init, typ = self.add_constructor_tvar_args( fdef, typ, args, arg_kinds, init) fdef2 = FuncDef(name, args, arg_kinds, init, fdef.body, typ) fdef2.info = fdef.info self.tf.prepend_generic_function_tvar_args(fdef2) return fdef2 tuple<Var[], int[], Node[], Type> \ add_constructor_tvar_args(
else: self.string('elif ') self.string(c) self.string(':' + '\n' + indent) self.string(' return {}'.format(make_overload_call( fi, i + 1, argc, fixed_args, optional_args, rest_args)) + '\n') n += 1 self.string(indent + 'else:' + '\n') self.string(indent + ' raise TypeError("Invalid arguments")') tuple<str, int>[] make_overload_check(self, FuncDef f, str[] fixed_args, str[] optional_args, str rest_args): """Return (condition, arg count) tuples.""" res = <tuple<str, int>> [] sig = (Callable)function_type(f) for argc in range(sig.min_args, len(sig.arg_types) + 1): a = <str> [] if rest_args: a.append(make_argument_count_check(f, len(fixed_args), rest_args)) i = 0 for t in sig.arg_types[:argc]: if not isinstance(t, Any) and (t.repr or isinstance(t, Callable)): a.append(self.make_argument_check( argument_ref(i, fixed_args, optional_args, rest_args), t)) elif i >= len(fixed_args): j = i - len(fixed_args) a.append('{} is not {}'.format(optional_args[j],
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 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 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) 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 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)
bool is_simple_override(FuncDef fdef, TypeInfo 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 = (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)