Ejemplo n.º 1
0
def add_class_tvars(t: ProperType, itype: Instance, isuper: Optional[Instance],
                    is_classmethod: bool, builtin_type: Callable[[str],
                                                                 Instance],
                    original_type: Type) -> Type:
    """Instantiate type variables during analyze_class_attribute_access,
    e.g T and Q in the following:

    class A(Generic[T]):
        @classmethod
        def foo(cls: Type[Q]) -> Tuple[T, Q]: ...

    class B(A[str]): pass

    B.foo()

    original_type is the value of the type B in the expression B.foo()
    """
    # TODO: verify consistency between Q and T
    info = itype.type  # type: TypeInfo
    if is_classmethod:
        assert isuper is not None
        t = expand_type_by_instance(t, isuper)
    # We add class type variables if the class method is accessed on class object
    # without applied type arguments, this matches the behavior of __init__().
    # For example (continuing the example in docstring):
    #     A       # The type of callable is def [T] () -> A[T], _not_ def () -> A[Any]
    #     A[int]  # The type of callable is def () -> A[int]
    # and
    #     A.foo       # The type is generic def [T] () -> Tuple[T, A[T]]
    #     A[int].foo  # The type is non-generic def () -> Tuple[int, A[int]]
    #
    # This behaviour is useful for defining alternative constructors for generic classes.
    # To achieve such behaviour, we add the class type variables that are still free
    # (i.e. appear in the return type of the class object on which the method was accessed).
    free_ids = {t.id for t in itype.args if isinstance(t, TypeVarType)}

    if isinstance(t, CallableType):
        # NOTE: in practice either all or none of the variables are free, since
        # visit_type_application() will detect any type argument count mismatch and apply
        # a correct number of Anys.
        tvars = [
            TypeVarDef(n, n, i + 1, [], builtin_type('builtins.object'),
                       tv.variance)
            for (i,
                 n), tv in zip(enumerate(info.type_vars), info.defn.type_vars)
            # use 'is' to avoid id clashes with unrelated variables
            if any(tv.id is id for id in free_ids)
        ]
        if is_classmethod:
            t = bind_self(t, original_type, is_classmethod=True)
        return t.copy_modified(variables=tvars + t.variables)
    elif isinstance(t, Overloaded):
        return Overloaded([
            cast(
                CallableType,
                add_class_tvars(item, itype, isuper, is_classmethod,
                                builtin_type, original_type))
            for item in t.items()
        ])
    return t
Ejemplo n.º 2
0
def add_class_tvars(t: ProperType,
                    itype: Instance,
                    isuper: Optional[Instance],
                    is_classmethod: bool,
                    builtin_type: Callable[[str], Instance],
                    original_type: Type,
                    original_vars: Optional[List[TypeVarDef]] = None) -> Type:
    """Instantiate type variables during analyze_class_attribute_access,
    e.g T and Q in the following:

    class A(Generic[T]):
        @classmethod
        def foo(cls: Type[Q]) -> Tuple[T, Q]: ...

    class B(A[str]): pass

    B.foo()

    original_type is the value of the type B in the expression B.foo() or the corresponding
    component in case if a union (this is used to bind the self-types); original_vars are type
    variables of the class callable on which the method was accessed.
    """
    # TODO: verify consistency between Q and T
    if is_classmethod:
        assert isuper is not None
        t = get_proper_type(expand_type_by_instance(t, isuper))
    # We add class type variables if the class method is accessed on class object
    # without applied type arguments, this matches the behavior of __init__().
    # For example (continuing the example in docstring):
    #     A       # The type of callable is def [T] () -> A[T], _not_ def () -> A[Any]
    #     A[int]  # The type of callable is def () -> A[int]
    # and
    #     A.foo       # The type is generic def [T] () -> Tuple[T, A[T]]
    #     A[int].foo  # The type is non-generic def () -> Tuple[int, A[int]]
    #
    # This behaviour is useful for defining alternative constructors for generic classes.
    # To achieve such behaviour, we add the class type variables that are still free
    # (i.e. appear in the return type of the class object on which the method was accessed).
    if isinstance(t, CallableType):
        tvars = original_vars if original_vars is not None else []
        if is_classmethod:
            t = bind_self(t, original_type, is_classmethod=True)
        return t.copy_modified(variables=tvars + t.variables)
    elif isinstance(t, Overloaded):
        return Overloaded([
            cast(
                CallableType,
                add_class_tvars(item,
                                itype,
                                isuper,
                                is_classmethod,
                                builtin_type,
                                original_type,
                                original_vars=original_vars))
            for item in t.items()
        ])
    return t
Ejemplo n.º 3
0
    def _process_class_node(self, node: Node) -> MypyType:
        if isinstance(node, Decorator):
            if not node.func.is_static:
                assert isinstance(node.func.type, FunctionLike)
                return bind_self(
                    node.func.type,
                    is_classmethod=node.func.is_class,
                )

            assert node.func.type
            return node.func.type
        elif isinstance(node, FuncBase):
            assert isinstance(node.type, FunctionLike)
            return bind_self(node.type, is_classmethod=False)

        assert isinstance(node, Var)
        assert node.type
        return node.type
Ejemplo n.º 4
0
def add_class_tvars(t: ProperType, isuper: Optional[Instance],
                    is_classmethod: bool,
                    original_type: Type,
                    original_vars: Optional[Sequence[TypeVarLikeDef]] = None) -> Type:
    """Instantiate type variables during analyze_class_attribute_access,
    e.g T and Q in the following:

    class A(Generic[T]):
        @classmethod
        def foo(cls: Type[Q]) -> Tuple[T, Q]: ...

    class B(A[str]): pass
    B.foo()

    Args:
        t: Declared type of the method (or property)
        isuper: Current instance mapped to the superclass where method was defined, this
            is usually done by map_instance_to_supertype()
        is_classmethod: True if this method is decorated with @classmethod
        original_type: The value of the type B in the expression B.foo() or the corresponding
            component in case of a union (this is used to bind the self-types)
        original_vars: Type variables of the class callable on which the method was accessed
    Returns:
        Expanded method type with added type variables (when needed).
    """
    # TODO: verify consistency between Q and T

    # We add class type variables if the class method is accessed on class object
    # without applied type arguments, this matches the behavior of __init__().
    # For example (continuing the example in docstring):
    #     A       # The type of callable is def [T] () -> A[T], _not_ def () -> A[Any]
    #     A[int]  # The type of callable is def () -> A[int]
    # and
    #     A.foo       # The type is generic def [T] () -> Tuple[T, A[T]]
    #     A[int].foo  # The type is non-generic def () -> Tuple[int, A[int]]
    #
    # This behaviour is useful for defining alternative constructors for generic classes.
    # To achieve such behaviour, we add the class type variables that are still free
    # (i.e. appear in the return type of the class object on which the method was accessed).
    if isinstance(t, CallableType):
        tvars = original_vars if original_vars is not None else []
        if is_classmethod:
            t = freshen_function_type_vars(t)
            t = bind_self(t, original_type, is_classmethod=True)
            assert isuper is not None
            t = cast(CallableType, expand_type_by_instance(t, isuper))
            freeze_type_vars(t)
        return t.copy_modified(variables=list(tvars) + list(t.variables))
    elif isinstance(t, Overloaded):
        return Overloaded([cast(CallableType, add_class_tvars(item, isuper,
                                                              is_classmethod, original_type,
                                                              original_vars=original_vars))
                           for item in t.items()])
    if isuper is not None:
        t = cast(ProperType, expand_type_by_instance(t, isuper))
    return t
Ejemplo n.º 5
0
def cached_function_method_signature(ctx: MethodSigContext) -> CallableType:
    """Fixes the `_CachedFunction.__call__` signature to be correct.

    It already has *almost* the correct signature, except:

        1. the `self` argument needs to be marked as "bound";
        2. any `cache_context` argument should be removed;
        3. an optional keyword argument `on_invalidated` should be added.
    """

    # First we mark this as a bound function signature.
    signature = bind_self(ctx.default_signature)

    # Secondly, we remove any "cache_context" args.
    #
    # Note: We should be only doing this if `cache_context=True` is set, but if
    # it isn't then the code will raise an exception when its called anyway, so
    # its not the end of the world.
    context_arg_index = None
    for idx, name in enumerate(signature.arg_names):
        if name == "cache_context":
            context_arg_index = idx
            break

    arg_types = list(signature.arg_types)
    arg_names = list(signature.arg_names)
    arg_kinds = list(signature.arg_kinds)

    if context_arg_index:
        arg_types.pop(context_arg_index)
        arg_names.pop(context_arg_index)
        arg_kinds.pop(context_arg_index)

    # Third, we add an optional "on_invalidate" argument.
    #
    # This is a callable which accepts no input and returns nothing.
    calltyp = CallableType(
        arg_types=[],
        arg_kinds=[],
        arg_names=[],
        ret_type=NoneType(),
        fallback=ctx.api.named_generic_type("builtins.function", []),
    )

    arg_types.append(calltyp)
    arg_names.append("on_invalidate")
    arg_kinds.append(ARG_NAMED_OPT)  # Arg is an optional kwarg.

    signature = signature.copy_modified(
        arg_types=arg_types,
        arg_names=arg_names,
        arg_kinds=arg_kinds,
    )

    return signature
Ejemplo n.º 6
0
def analyze_instance_member_access(name: str, typ: Instance, mx: MemberContext,
                                   override_info: Optional[TypeInfo]) -> Type:
    if name == '__init__' and not mx.is_super:
        # Accessing __init__ in statically typed code would compromise
        # type safety unless used via super().
        mx.msg.fail(message_registry.CANNOT_ACCESS_INIT, mx.context)
        return AnyType(TypeOfAny.from_error)

    # The base object has an instance type.

    info = typ.type
    if override_info:
        info = override_info

    if (state.find_occurrences and info.name == state.find_occurrences[0]
            and name == state.find_occurrences[1]):
        mx.msg.note("Occurrence of '{}.{}'".format(*state.find_occurrences),
                    mx.context)

    # 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, mx)
        if mx.is_lvalue:
            mx.msg.cant_assign_to_method(mx.context)
        signature = function_type(method, mx.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:
            if isinstance(signature, FunctionLike) and name != '__call__':
                # TODO: use proper treatment of special methods on unions instead
                #       of this hack here and below (i.e. mx.self_type).
                dispatched_type = meet.meet_types(mx.original_type, typ)
                signature = check_self_arg(signature, dispatched_type,
                                           method.is_class, mx.context, name,
                                           mx.msg)
            signature = bind_self(signature,
                                  mx.self_type,
                                  is_classmethod=method.is_class)
        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, mx)
Ejemplo n.º 7
0
def kinded_get_descriptor(ctx: MethodContext) -> MypyType:
    """
    Used to analyze ``@kinded`` method calls.

    We do this due to ``__get__`` descriptor magic.
    """
    assert isinstance(ctx.type, Instance)
    assert isinstance(ctx.type.args[0], CallableType)

    wrapped_method = ctx.type.args[0]
    self_type = wrapped_method.arg_types[0]
    signature = bind_self(
        wrapped_method,
        is_classmethod=isinstance(self_type, TypeType),
    )
    return ctx.type.copy_modified(args=[signature])
Ejemplo n.º 8
0
def analyze_decorator_or_funcbase_access(
    defn: Union[Decorator, FuncBase],
    itype: Instance,
    info: TypeInfo,
    self_type: Optional[Type],
    name: str,
    mx: MemberContext,
) -> Type:
    """Analyzes the type behind method access.

    The function itself can possibly be decorated.
    See: https://github.com/python/mypy/issues/10409
    """
    if isinstance(defn, Decorator):
        return analyze_var(name, defn.var, itype, info, mx)
    return bind_self(
        function_type(defn, mx.chk.named_type('builtins.function')),
        original_type=self_type,
    )
Ejemplo n.º 9
0
def cached_function_method_signature(ctx: MethodSigContext) -> CallableType:
    """Fixes the `_CachedFunction.__call__` signature to be correct.

    It already has *almost* the correct signature, except:

        1. the `self` argument needs to be marked as "bound"; and
        2. any `cache_context` argument should be removed.
    """

    # First we mark this as a bound function signature.
    signature = bind_self(ctx.default_signature)

    # Secondly, we remove any "cache_context" args.
    #
    # Note: We should be only doing this if `cache_context=True` is set, but if
    # it isn't then the code will raise an exception when its called anyway, so
    # its not the end of the world.
    context_arg_index = None
    for idx, name in enumerate(signature.arg_names):
        if name == "cache_context":
            context_arg_index = idx
            break

    if context_arg_index:
        arg_types = list(signature.arg_types)
        arg_types.pop(context_arg_index)

        arg_names = list(signature.arg_names)
        arg_names.pop(context_arg_index)

        arg_kinds = list(signature.arg_kinds)
        arg_kinds.pop(context_arg_index)

        signature = signature.copy_modified(
            arg_types=arg_types,
            arg_names=arg_names,
            arg_kinds=arg_kinds,
        )

    return signature
Ejemplo n.º 10
0
def analyze_class_attribute_access(
        itype: Instance,
        name: str,
        mx: MemberContext,
        override_info: Optional[TypeInfo] = None,
        original_vars: Optional[List[TypeVarDef]] = None) -> Optional[Type]:
    """Analyze access to an attribute on a class object.

    itype is the return type of the class object callable, original_type is the type
    of E in the expression E.var, original_vars are type variables of the class callable
    (for generic classes).
    """
    info = itype.type
    if override_info:
        info = override_info

    node = info.get(name)
    if not node:
        if info.fallback_to_any:
            return AnyType(TypeOfAny.special_form)
        return None

    is_decorated = isinstance(node.node, Decorator)
    is_method = is_decorated or isinstance(node.node, FuncBase)
    if mx.is_lvalue:
        if is_method:
            mx.msg.cant_assign_to_method(mx.context)
        if isinstance(node.node, TypeInfo):
            mx.msg.fail(message_registry.CANNOT_ASSIGN_TO_TYPE, mx.context)

    # If a final attribute was declared on `self` in `__init__`, then it
    # can't be accessed on the class object.
    if node.implicit and isinstance(node.node, Var) and node.node.is_final:
        mx.msg.fail(
            message_registry.CANNOT_ACCESS_FINAL_INSTANCE_ATTR.format(
                node.node.name), mx.context)

    # An assignment to final attribute on class object is also always an error,
    # independently of types.
    if mx.is_lvalue and not mx.chk.get_final_context():
        check_final_member(name, info, mx.msg, mx.context)

    if info.is_enum and not (mx.is_lvalue or is_decorated or is_method):
        enum_literal = LiteralType(name, fallback=itype)
        # When we analyze enums, the corresponding Instance is always considered to be erased
        # due to how the signature of Enum.__new__ is `(cls: Type[_T], value: object) -> _T`
        # in typeshed. However, this is really more of an implementation detail of how Enums
        # are typed, and we really don't want to treat every single Enum value as if it were
        # from type variable substitution. So we reset the 'erased' field here.
        return itype.copy_modified(erased=False, last_known_value=enum_literal)

    t = node.type
    if t:
        if isinstance(t, PartialType):
            symnode = node.node
            assert isinstance(symnode, Var)
            return mx.chk.handle_partial_var_type(t, mx.is_lvalue, symnode,
                                                  mx.context)

        # Find the class where method/variable was defined.
        if isinstance(node.node, Decorator):
            super_info = node.node.var.info  # type: Optional[TypeInfo]
        elif isinstance(node.node, (Var, SYMBOL_FUNCBASE_TYPES)):
            super_info = node.node.info
        else:
            super_info = None

        # Map the type to how it would look as a defining class. For example:
        #     class C(Generic[T]): ...
        #     class D(C[Tuple[T, S]]): ...
        #     D[int, str].method()
        # Here itype is D[int, str], isuper is C[Tuple[int, str]].
        if not super_info:
            isuper = None
        else:
            isuper = map_instance_to_supertype(itype, super_info)

        if isinstance(node.node, Var):
            assert isuper is not None
            # Check if original variable type has type variables. For example:
            #     class C(Generic[T]):
            #         x: T
            #     C.x  # Error, ambiguous access
            #     C[int].x  # Also an error, since C[int] is same as C at runtime
            if isinstance(t, TypeVarType) or has_type_vars(t):
                # Exception: access on Type[...], including first argument of class methods is OK.
                if not isinstance(get_proper_type(mx.original_type),
                                  TypeType) or node.implicit:
                    if node.node.is_classvar:
                        message = message_registry.GENERIC_CLASS_VAR_ACCESS
                    else:
                        message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS
                    mx.msg.fail(message, mx.context)

            # Erase non-mapped variables, but keep mapped ones, even if there is an error.
            # In the above example this means that we infer following types:
            #     C.x -> Any
            #     C[int].x -> int
            t = erase_typevars(expand_type_by_instance(t, isuper))

        is_classmethod = (
            (is_decorated and cast(Decorator, node.node).func.is_class)
            or (isinstance(node.node, FuncBase) and node.node.is_class))
        t = get_proper_type(t)
        if isinstance(t, FunctionLike) and is_classmethod:
            t = check_self_arg(t, mx.self_type, False, mx.context, name,
                               mx.msg)
        result = add_class_tvars(t,
                                 isuper,
                                 is_classmethod,
                                 mx.self_type,
                                 original_vars=original_vars)
        if not mx.is_lvalue:
            result = analyze_descriptor_access(mx.original_type,
                                               result,
                                               mx.builtin_type,
                                               mx.msg,
                                               mx.context,
                                               chk=mx.chk)
        return result
    elif isinstance(node.node, Var):
        mx.not_ready_callback(name, mx.context)
        return AnyType(TypeOfAny.special_form)

    if isinstance(node.node, TypeVarExpr):
        mx.msg.fail(
            message_registry.CANNOT_USE_TYPEVAR_AS_EXPRESSION.format(
                info.name, name), mx.context)
        return AnyType(TypeOfAny.from_error)

    if isinstance(node.node, TypeInfo):
        return type_object_type(node.node, mx.builtin_type)

    if isinstance(node.node, MypyFile):
        # Reference to a module object.
        return mx.builtin_type('types.ModuleType')

    if (isinstance(node.node, TypeAlias)
            and isinstance(get_proper_type(node.node.target), Instance)):
        return instance_alias_type(node.node, mx.builtin_type)

    if is_decorated:
        assert isinstance(node.node, Decorator)
        if node.node.type:
            return node.node.type
        else:
            mx.not_ready_callback(name, mx.context)
            return AnyType(TypeOfAny.from_error)
    else:
        assert isinstance(node.node, FuncBase)
        typ = function_type(node.node, mx.builtin_type('builtins.function'))
        # Note: if we are accessing class method on class object, the cls argument is bound.
        # Annotated and/or explicit class methods go through other code paths above, for
        # unannotated implicit class methods we do this here.
        if node.node.is_class:
            typ = bind_self(typ, is_classmethod=True)
        return typ
Ejemplo n.º 11
0
def analyze_var(name: str,
                var: Var,
                itype: Instance,
                info: TypeInfo,
                mx: MemberContext,
                *,
                implicit: bool = False) -> Type:
    """Analyze access to an attribute via a Var node.

    This is conceptually part of analyze_member_access and the arguments are similar.

    itype is the class object in which var is defined
    original_type is the type of E in the expression E.var
    if implicit is True, the original Var was created as an assignment to self
    """
    # Found a member variable.
    itype = map_instance_to_supertype(itype, var.info)
    typ = var.type
    if typ:
        if isinstance(typ, PartialType):
            return mx.chk.handle_partial_var_type(typ, mx.is_lvalue, var,
                                                  mx.context)
        if mx.is_lvalue and var.is_property and not var.is_settable_property:
            # TODO allow setting attributes in subclass (although it is probably an error)
            mx.msg.read_only_property(name, itype.type, mx.context)
        if mx.is_lvalue and var.is_classvar:
            mx.msg.cant_assign_to_classvar(name, mx.context)
        t = get_proper_type(expand_type_by_instance(typ, itype))
        result = t  # type: Type
        typ = get_proper_type(typ)
        if var.is_initialized_in_class and isinstance(
                typ, FunctionLike) and not typ.is_type_obj():
            if mx.is_lvalue:
                if var.is_property:
                    if not var.is_settable_property:
                        mx.msg.read_only_property(name, itype.type, mx.context)
                else:
                    mx.msg.cant_assign_to_method(mx.context)

            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 = typ
                # Use meet to narrow original_type to the dispatched type.
                # For example, assume
                # * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A)
                # * B.f: Callable[[B1], None] where B1 <: B (maybe B1 == B)
                # * x: Union[A1, B1]
                # In `x.f`, when checking `x` against A1 we assume x is compatible with A
                # and similarly for B1 when checking agains B
                dispatched_type = meet.meet_types(mx.original_type, itype)
                signature = freshen_function_type_vars(functype)
                signature = check_self_arg(signature, dispatched_type,
                                           var.is_classmethod, mx.context,
                                           name, mx.msg)
                signature = bind_self(signature, mx.self_type,
                                      var.is_classmethod)
                expanded_signature = get_proper_type(
                    expand_type_by_instance(signature, itype))
                freeze_type_vars(expanded_signature)
                if var.is_property:
                    # A property cannot have an overloaded type => the cast is fine.
                    assert isinstance(expanded_signature, CallableType)
                    result = expanded_signature.ret_type
                else:
                    result = expanded_signature
    else:
        if not var.is_ready:
            mx.not_ready_callback(var.name, mx.context)
        # Implicit 'Any' type.
        result = AnyType(TypeOfAny.special_form)
    fullname = '{}.{}'.format(var.info.fullname, name)
    hook = mx.chk.plugin.get_attribute_hook(fullname)
    if result and not mx.is_lvalue and not implicit:
        result = analyze_descriptor_access(mx.original_type,
                                           result,
                                           mx.builtin_type,
                                           mx.msg,
                                           mx.context,
                                           chk=mx.chk)
    if hook:
        result = hook(
            AttributeContext(get_proper_type(mx.original_type), result,
                             mx.context, mx.chk))
    return result
Ejemplo n.º 12
0
def analyze_descriptor_access(instance_type: Type, descriptor_type: Type,
                              builtin_type: Callable[[str], Instance],
                              msg: MessageBuilder, context: Context, *,
                              chk: 'mypy.checker.TypeChecker') -> Type:
    """Type check descriptor access.

    Arguments:
        instance_type: The type of the instance on which the descriptor
            attribute is being accessed (the type of ``a`` in ``a.f`` when
            ``f`` is a descriptor).
        descriptor_type: The type of the descriptor attribute being accessed
            (the type of ``f`` in ``a.f`` when ``f`` is a descriptor).
        context: The node defining the context of this inference.
    Return:
        The return type of the appropriate ``__get__`` overload for the descriptor.
    """
    instance_type = get_proper_type(instance_type)
    descriptor_type = get_proper_type(descriptor_type)

    if isinstance(descriptor_type, UnionType):
        # Map the access over union types
        return make_simplified_union([
            analyze_descriptor_access(instance_type,
                                      typ,
                                      builtin_type,
                                      msg,
                                      context,
                                      chk=chk) for typ in descriptor_type.items
        ])
    elif not isinstance(descriptor_type, Instance):
        return descriptor_type

    if not descriptor_type.type.has_readable_member('__get__'):
        return descriptor_type

    dunder_get = descriptor_type.type.get_method('__get__')

    if dunder_get is None:
        msg.fail(
            message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(
                descriptor_type), context)
        return AnyType(TypeOfAny.from_error)

    function = function_type(dunder_get, builtin_type('builtins.function'))
    bound_method = bind_self(function, descriptor_type)
    typ = map_instance_to_supertype(descriptor_type, dunder_get.info)
    dunder_get_type = expand_type_by_instance(bound_method, typ)

    if isinstance(instance_type, FunctionLike) and instance_type.is_type_obj():
        owner_type = instance_type.items()[0].ret_type
        instance_type = NoneType()
    elif isinstance(instance_type, TypeType):
        owner_type = instance_type.item
        instance_type = NoneType()
    else:
        owner_type = instance_type

    _, inferred_dunder_get_type = chk.expr_checker.check_call(
        dunder_get_type, [
            TempNode(instance_type, context=context),
            TempNode(TypeType.make_normalized(owner_type), context=context)
        ], [ARG_POS, ARG_POS], context)

    inferred_dunder_get_type = get_proper_type(inferred_dunder_get_type)
    if isinstance(inferred_dunder_get_type, AnyType):
        # check_call failed, and will have reported an error
        return inferred_dunder_get_type

    if not isinstance(inferred_dunder_get_type, CallableType):
        msg.fail(
            message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(
                descriptor_type), context)
        return AnyType(TypeOfAny.from_error)

    return inferred_dunder_get_type.ret_type
Ejemplo n.º 13
0
def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo,
                              mx: MemberContext) -> 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, mx.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, mx.builtin_type))
        v.info = info

    if isinstance(vv, TypeAlias) and isinstance(get_proper_type(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, mx.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 mx.is_lvalue and not mx.chk.get_final_context():
            check_final_member(name, info, mx.msg, mx.context)

        return analyze_var(name, v, itype, info, mx, implicit=implicit)
    elif isinstance(v, FuncDef):
        assert False, "Did not expect a function"
    elif (not v
          and name not in ['__getattr__', '__setattr__', '__getattribute__']
          and not mx.is_operator):
        if not mx.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, mx.builtin_type('builtins.function'))
                    bound_method = bind_self(function, mx.self_type)
                    typ = map_instance_to_supertype(itype, method.info)
                    getattr_type = get_proper_type(
                        expand_type_by_instance(bound_method, typ))
                    if isinstance(getattr_type, CallableType):
                        result = getattr_type.ret_type

                        # Call the attribute hook before returning.
                        fullname = '{}.{}'.format(method.info.fullname, name)
                        hook = mx.chk.plugin.get_attribute_hook(fullname)
                        if hook:
                            result = hook(
                                AttributeContext(
                                    get_proper_type(mx.original_type), result,
                                    mx.context, mx.chk))
                        return result
        else:
            setattr_meth = info.get_method('__setattr__')
            if setattr_meth and setattr_meth.info.fullname != 'builtins.object':
                setattr_func = function_type(
                    setattr_meth, mx.builtin_type('builtins.function'))
                bound_type = bind_self(setattr_func, mx.self_type)
                typ = map_instance_to_supertype(itype, setattr_meth.info)
                setattr_type = get_proper_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 mx.is_super:
        mx.msg.undefined_in_superclass(name, mx.context)
        return AnyType(TypeOfAny.from_error)
    else:
        if mx.chk and mx.chk.should_suppress_optional_error([itype]):
            return AnyType(TypeOfAny.from_error)
        return mx.msg.has_no_attr(mx.original_type, itype, name, mx.context,
                                  mx.module_symbol_table)