Example #1
0
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)
Example #2
0
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)
Example #3
0
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)
Example #4
0
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)
Example #5
0
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)
Example #6
0
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)
Example #7
0
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)
Example #8
0
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)
Example #9
0
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)
Example #10
0
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)
Example #11
0
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)
Example #12
0
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)
Example #13
0
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)
Example #14
0
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)