def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, is_super: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, original_type: Type, chk: 'mypy.checker.TypeChecker' = None) -> Type: """Analyse attribute access that does not target a method. This is logically part of analyze_member_access and the arguments are similar. original_type is the type of E in the expression E.var """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(v, Var): return analyze_var(name, v, itype, info, node, is_lvalue, msg, original_type, not_ready_callback) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif not v and name not in [ '__getattr__', '__setattr__', '__getattribute__' ]: if not is_lvalue: for method_name in ('__getattribute__', '__getattr__'): method = info.get_method(method_name) # __getattribute__ is defined on builtins.object and returns Any, so without # the guard this search will always find object.__getattribute__ and conclude # that the attribute exists if method and method.info.fullname() != 'builtins.object': function = function_type(method, builtin_type('builtins.function')) bound_method = bind_self(function, original_type) typ = map_instance_to_supertype(itype, method.info) getattr_type = expand_type_by_instance(bound_method, typ) if isinstance(getattr_type, CallableType): return getattr_type.ret_type if itype.type.fallback_to_any: return AnyType() # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return AnyType() else: if chk and chk.should_suppress_optional_error([itype]): return AnyType() return msg.has_no_attr(original_type, name, node)
def analyse_member_var_access(name: str, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, is_super: bool, msg: MessageBuilder, report_type: Type = None) -> Type: """Analyse attribute access that does not target a method. This is logically part of analyse_member_access and the arguments are similar. """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(v, Var): # Found a member variable. var = v itype = map_instance_to_supertype(itype, var.info) if var.type: t = expand_type_by_instance(var.type, itype) if var.is_initialized_in_class and isinstance(t, FunctionLike): if is_lvalue: if var.is_property: msg.read_only_property(name, info, node) else: msg.cant_assign_to_method(node) if not var.is_staticmethod: # Class-level function objects and classmethods become bound # methods: the former to the instance, the latter to the # class. functype = cast(FunctionLike, t) check_method_type(functype, itype, node, msg) signature = method_type(functype) if var.is_property: # A property cannot have an overloaded type => the cast # is fine. return cast(Callable, signature).ret_type else: return signature return t else: if not var.is_ready: msg.cannot_determine_type(var.name(), node) # Implicit 'Any' type. return AnyType() elif isinstance(v, FuncDef): assert False, "Did not expect a function" # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return AnyType() else: return msg.has_no_attr(report_type or itype, name, node)
def analyse_member_var_access(name: str, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, is_super: bool, msg: MessageBuilder, report_type: Type = None) -> Type: """Analyse attribute access that does not target a method. This is logically part of analyse_member_access and the arguments are similar. """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(v, Var): # Found a member variable. var = v itype = map_instance_to_supertype(itype, var.info) if var.type: t = expand_type_by_instance(var.type, itype) if (var.is_initialized_in_class and isinstance(t, FunctionLike) and not var.is_staticmethod): # Class-level function object becomes a bound method. functype = cast(FunctionLike, t) check_method_type(functype, itype, node, msg) signature = method_type(functype) if var.is_property: if is_lvalue: msg.read_only_property(name, info, node) # A property cannot have an overloaded type => the cast # is fine. return cast(Callable, signature).ret_type else: return signature return t else: if not var.is_ready: msg.cannot_determine_type(var.name(), node) # Implicit 'Any' type. return AnyType() elif isinstance(v, FuncDef): assert False, "Did not expect a function" # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return AnyType() else: return msg.has_no_attr(report_type or itype, name, node)
def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, is_super: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, original_type: Type, chk: 'mypy.checker.TypeChecker' = None) -> Type: """Analyse attribute access that does not target a method. This is logically part of analyze_member_access and the arguments are similar. original_type is the type of E in the expression E.var """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(v, Var): return analyze_var(name, v, itype, info, node, is_lvalue, msg, original_type, not_ready_callback) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif not v and name not in ['__getattr__', '__setattr__', '__getattribute__']: if not is_lvalue: for method_name in ('__getattribute__', '__getattr__'): method = info.get_method(method_name) # __getattribute__ is defined on builtins.object and returns Any, so without # the guard this search will always find object.__getattribute__ and conclude # that the attribute exists if method and method.info.fullname() != 'builtins.object': function = function_type(method, builtin_type('builtins.function')) bound_method = bind_self(function, original_type) typ = map_instance_to_supertype(itype, method.info) getattr_type = expand_type_by_instance(bound_method, typ) if isinstance(getattr_type, CallableType): return getattr_type.ret_type if itype.type.fallback_to_any: return AnyType() # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return AnyType() else: if chk and chk.should_suppress_optional_error([itype]): return AnyType() return msg.has_no_attr(original_type, name, node)
def analyze_member_var_access( name: str, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, is_super: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, report_type: Type = None, chk: "mypy.checker.TypeChecker" = None, ) -> Type: """Analyse attribute access that does not target a method. This is logically part of analyze_member_access and the arguments are similar. """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(v, Var): return analyze_var(name, v, itype, info, node, is_lvalue, msg, not_ready_callback) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif not v and name not in ["__getattr__", "__setattr__"]: if not is_lvalue: method = info.get_method("__getattr__") if method: typ = map_instance_to_supertype(itype, method.info) getattr_type = expand_type_by_instance( method_type_with_fallback(method, builtin_type("builtins.function")), typ ) if isinstance(getattr_type, CallableType): return getattr_type.ret_type if itype.type.fallback_to_any: return AnyType() # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return AnyType() else: if chk and chk.should_suppress_optional_error([itype]): return AnyType() return msg.has_no_attr(report_type or itype, name, node)
def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, is_super: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, report_type: Type = None, chk: 'mypy.checker.TypeChecker' = None) -> Type: """Analyse attribute access that does not target a method. This is logically part of analyze_member_access and the arguments are similar. """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(v, Var): return analyze_var(name, v, itype, info, node, is_lvalue, msg, not_ready_callback) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif not v and name not in ['__getattr__', '__setattr__']: if not is_lvalue: method = info.get_method('__getattr__') if method: typ = map_instance_to_supertype(itype, method.info) getattr_type = expand_type_by_instance( method_type_with_fallback( method, builtin_type('builtins.function')), typ) if isinstance(getattr_type, CallableType): return getattr_type.ret_type if itype.type.fallback_to_any: return AnyType() # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return AnyType() else: if chk and chk.should_suppress_optional_error([itype]): return AnyType() return msg.has_no_attr(report_type or itype, 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, *, original_type: Type, override_info: TypeInfo = None, chk: 'mypy.checker.TypeChecker' = 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. """ 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, original_type, not_ready_callback) 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() 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, 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.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() 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, original_type=original_type) if result: return result fallback = builtin_type('builtins.type') 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 and chk.should_suppress_optional_error([typ]): return AnyType() return msg.has_no_attr(original_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 # 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], 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) if is_lvalue: msg.cant_assign_to_method(node) typ = map_instance_to_supertype(typ, method.info) return expand_type_by_instance( method_type_with_fallback(method, builtin_type('builtins.function')), typ) else: # Not a method. return analyze_member_var_access(name, typ, info, node, is_lvalue, is_super, builtin_type, 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, 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, msg) elif isinstance(typ, FunctionLike) and typ.is_type_obj(): # Class attribute. # TODO super? itype = cast(Instance, typ.items()[0].ret_type) result = analyze_class_attribute_access(itype, name, node, is_lvalue, builtin_type, 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, msg, report_type=report_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, msg, report_type=report_type) elif isinstance(typ, TypeVarType): return analyze_member_access(name, typ.upper_bound, node, is_lvalue, is_super, builtin_type, msg, report_type=report_type) return msg.has_no_attr(report_type, name, node)
def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, is_super: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, original_type: Type, chk: 'mypy.checker.TypeChecker') -> Type: """Analyse attribute access that does not target a method. This is logically part of analyze_member_access and the arguments are similar. original_type is the type of E in the expression E.var """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(vv, TypeInfo): # If the associated variable is a TypeInfo synthesize a Var node for # the purposes of type checking. This enables us to type check things # like accessing class attributes on an inner class. v = Var(name, type=type_object_type(vv, builtin_type)) v.info = info if isinstance(vv, TypeAlias) and isinstance(vv.target, Instance): # Similar to the above TypeInfo case, we allow using # qualified type aliases in runtime context if it refers to an # instance type. For example: # class C: # A = List[int] # x = C.A() <- this is OK typ = instance_alias_type(vv, builtin_type) v = Var(name, type=typ) v.info = info if isinstance(v, Var): implicit = info[name].implicit # An assignment to final attribute is always an error, # independently of types. if is_lvalue and not chk.get_final_context(): check_final_member(name, info, msg, node) return analyze_var(name, v, itype, info, node, is_lvalue, msg, original_type, builtin_type, not_ready_callback, chk=chk, implicit=implicit) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif not v and name not in ['__getattr__', '__setattr__', '__getattribute__']: if not is_lvalue: for method_name in ('__getattribute__', '__getattr__'): method = info.get_method(method_name) # __getattribute__ is defined on builtins.object and returns Any, so without # the guard this search will always find object.__getattribute__ and conclude # that the attribute exists if method and method.info.fullname() != 'builtins.object': function = function_type(method, builtin_type('builtins.function')) bound_method = bind_self(function, original_type) typ = map_instance_to_supertype(itype, method.info) getattr_type = expand_type_by_instance(bound_method, typ) if isinstance(getattr_type, CallableType): return getattr_type.ret_type else: setattr_meth = info.get_method('__setattr__') if setattr_meth and setattr_meth.info.fullname() != 'builtins.object': setattr_func = function_type(setattr_meth, builtin_type('builtins.function')) bound_type = bind_self(setattr_func, original_type) typ = map_instance_to_supertype(itype, setattr_meth.info) setattr_type = expand_type_by_instance(bound_type, typ) if isinstance(setattr_type, CallableType) and len(setattr_type.arg_types) > 0: return setattr_type.arg_types[-1] if itype.type.fallback_to_any: return AnyType(TypeOfAny.special_form) # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return AnyType(TypeOfAny.from_error) else: if chk and chk.should_suppress_optional_error([itype]): return AnyType(TypeOfAny.from_error) return msg.has_no_attr(original_type, itype, 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)
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 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 analyse_member_access(name: str, typ: Type, node: Context, is_lvalue: bool, is_super: bool, basic_types: BasicTypes, 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.fail(messages.CANNOT_ASSIGN_TO_METHOD, node) typ = map_instance_to_supertype(typ, method.info) return expand_type_by_instance(method_type(method), 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, TupleType): # Actually look up from the 'tuple' type. return analyse_member_access(name, basic_types.tuple, node, is_lvalue, is_super, basic_types, 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, msg) if result: return result # Look up from the 'type' type. return analyse_member_access(name, basic_types.type_type, node, is_lvalue, is_super, basic_types, msg, report_type=report_type) elif isinstance(typ, FunctionLike): # Look up from the 'function' type. return analyse_member_access(name, basic_types.function, node, is_lvalue, is_super, basic_types, msg, report_type=report_type) return msg.has_no_attr(report_type, name, node)