Beispiel #1
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)
Beispiel #2
0
 def visit_type_var(self, t: TypeVarType) -> Type:
     return AnyType(TypeOfAny.special_form)
Beispiel #3
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
        if isinstance(typ.item, Instance):
            item = typ.item
        elif isinstance(typ.item, AnyType):
            fallback = builtin_type('builtins.type')
            ignore_messages = msg.copy()
            ignore_messages.disable_errors()
            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
        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.should_suppress_optional_error([typ]):
        return AnyType(TypeOfAny.from_error)
    return msg.has_no_attr(original_type, typ, name, node)
Beispiel #4
0
    def build_namedtuple_typeinfo(
            self, name: str, items: List[str], types: List[Type],
            default_items: Mapping[str, Expression]) -> TypeInfo:
        strtype = self.api.named_type('__builtins__.str')
        implicit_any = AnyType(TypeOfAny.special_form)
        basetuple_type = self.api.named_type('__builtins__.tuple',
                                             [implicit_any])
        dictype = (self.api.named_type_or_none('builtins.dict',
                                               [strtype, implicit_any])
                   or self.api.named_type('__builtins__.object'))
        # Actual signature should return OrderedDict[str, Union[types]]
        ordereddictype = (self.api.named_type_or_none('builtins.dict',
                                                      [strtype, implicit_any])
                          or self.api.named_type('__builtins__.object'))
        fallback = self.api.named_type('__builtins__.tuple', [implicit_any])
        # Note: actual signature should accept an invariant version of Iterable[UnionType[types]].
        # but it can't be expressed. 'new' and 'len' should be callable types.
        iterable_type = self.api.named_type_or_none('typing.Iterable',
                                                    [implicit_any])
        function_type = self.api.named_type('__builtins__.function')

        info = self.api.basic_new_typeinfo(name, fallback)
        info.is_named_tuple = True
        tuple_base = TupleType(types, fallback)
        info.tuple_type = tuple_base

        # We can't calculate the complete fallback type until after semantic
        # analysis, since otherwise base classes might be incomplete. Postpone a
        # callback function that patches the fallback.
        self.api.schedule_patch(PRIORITY_FALLBACKS,
                                lambda: calculate_tuple_fallback(tuple_base))

        def add_field(var: Var,
                      is_initialized_in_class: bool = False,
                      is_property: bool = False) -> None:
            var.info = info
            var.is_initialized_in_class = is_initialized_in_class
            var.is_property = is_property
            var._fullname = '%s.%s' % (info.fullname(), var.name())
            info.names[var.name()] = SymbolTableNode(MDEF, var)

        vars = [Var(item, typ) for item, typ in zip(items, types)]
        for var in vars:
            add_field(var, is_property=True)

        tuple_of_strings = TupleType([strtype for _ in items], basetuple_type)
        add_field(Var('_fields', tuple_of_strings),
                  is_initialized_in_class=True)
        add_field(Var('_field_types', dictype), is_initialized_in_class=True)
        add_field(Var('_field_defaults', dictype),
                  is_initialized_in_class=True)
        add_field(Var('_source', strtype), is_initialized_in_class=True)
        add_field(Var('__annotations__', ordereddictype),
                  is_initialized_in_class=True)
        add_field(Var('__doc__', strtype), is_initialized_in_class=True)

        tvd = TypeVarDef(SELF_TVAR_NAME,
                         info.fullname() + '.' + SELF_TVAR_NAME, -1, [],
                         info.tuple_type)
        selftype = TypeVarType(tvd)

        def add_method(
            funcname: str,
            ret: Type,
            args: List[Argument],
            is_classmethod: bool = False,
            is_new: bool = False,
        ) -> None:
            if is_classmethod or is_new:
                first = [
                    Argument(Var('_cls'), TypeType.make_normalized(selftype),
                             None, ARG_POS)
                ]
            else:
                first = [Argument(Var('_self'), selftype, None, ARG_POS)]
            args = first + args

            types = [arg.type_annotation for arg in args]
            items = [arg.variable.name() for arg in args]
            arg_kinds = [arg.kind for arg in args]
            assert None not in types
            signature = CallableType(cast(List[Type], types), arg_kinds, items,
                                     ret, function_type)
            signature.variables = [tvd]
            func = FuncDef(funcname, args, Block([]))
            func.info = info
            func.is_class = is_classmethod
            func.type = set_callable_name(signature, func)
            func._fullname = info.fullname() + '.' + funcname
            if is_classmethod:
                v = Var(funcname, func.type)
                v.is_classmethod = True
                v.info = info
                v._fullname = func._fullname
                func.is_decorated = True
                dec = Decorator(func, [NameExpr('classmethod')], v)
                info.names[funcname] = SymbolTableNode(MDEF, dec)
            else:
                info.names[funcname] = SymbolTableNode(MDEF, func)

        add_method('_replace',
                   ret=selftype,
                   args=[
                       Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT)
                       for var in vars
                   ])

        def make_init_arg(var: Var) -> Argument:
            default = default_items.get(var.name(), None)
            kind = ARG_POS if default is None else ARG_OPT
            return Argument(var, var.type, default, kind)

        add_method('__new__',
                   ret=selftype,
                   args=[make_init_arg(var) for var in vars],
                   is_new=True)
        add_method('_asdict', args=[], ret=ordereddictype)
        special_form_any = AnyType(TypeOfAny.special_form)
        add_method('_make',
                   ret=selftype,
                   is_classmethod=True,
                   args=[
                       Argument(Var('iterable', iterable_type), iterable_type,
                                None, ARG_POS),
                       Argument(Var('new'), special_form_any, EllipsisExpr(),
                                ARG_NAMED_OPT),
                       Argument(Var('len'), special_form_any, EllipsisExpr(),
                                ARG_NAMED_OPT)
                   ])

        self_tvar_expr = TypeVarExpr(SELF_TVAR_NAME,
                                     info.fullname() + '.' + SELF_TVAR_NAME,
                                     [], info.tuple_type)
        info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr)
        return info
Beispiel #5
0
def fill_descriptor_types_for_related_field(
        ctx: FunctionContext, django_context: DjangoContext) -> MypyType:
    current_field = _get_current_field_from_assignment(ctx, django_context)
    if current_field is None:
        return AnyType(TypeOfAny.from_error)

    assert isinstance(current_field, RelatedField)

    related_model_cls = django_context.get_field_related_model_cls(
        current_field)
    if related_model_cls is None:
        return AnyType(TypeOfAny.from_error)

    default_related_field_type = set_descriptor_types_for_field(ctx)

    # self reference with abstract=True on the model where ForeignKey is defined
    current_model_cls = current_field.model
    if current_model_cls._meta.abstract and current_model_cls == related_model_cls:
        # for all derived non-abstract classes, set variable with this name to
        # __get__/__set__ of ForeignKey of derived model
        for model_cls in django_context.all_registered_model_classes:
            if issubclass(model_cls,
                          current_model_cls) and not model_cls._meta.abstract:
                derived_model_info = helpers.lookup_class_typeinfo(
                    helpers.get_typechecker_api(ctx), model_cls)
                if derived_model_info is not None:
                    fk_ref_type = Instance(derived_model_info, [])
                    derived_fk_type = reparametrize_related_field_type(
                        default_related_field_type,
                        set_type=fk_ref_type,
                        get_type=fk_ref_type)
                    helpers.add_new_sym_for_info(derived_model_info,
                                                 name=current_field.name,
                                                 sym_type=derived_fk_type)

    related_model = related_model_cls
    related_model_to_set = related_model_cls
    if related_model_to_set._meta.proxy_for_model is not None:
        related_model_to_set = related_model_to_set._meta.proxy_for_model

    typechecker_api = helpers.get_typechecker_api(ctx)

    related_model_info = helpers.lookup_class_typeinfo(typechecker_api,
                                                       related_model)
    if related_model_info is None:
        # maybe no type stub
        related_model_type = AnyType(TypeOfAny.unannotated)
    else:
        related_model_type = Instance(related_model_info, [])  # type: ignore

    related_model_to_set_info = helpers.lookup_class_typeinfo(
        typechecker_api, related_model_to_set)
    if related_model_to_set_info is None:
        # maybe no type stub
        related_model_to_set_type = AnyType(TypeOfAny.unannotated)
    else:
        related_model_to_set_type = Instance(related_model_to_set_info,
                                             [])  # type: ignore

    # replace Any with referred_to_type
    return reparametrize_related_field_type(default_related_field_type,
                                            set_type=related_model_to_set_type,
                                            get_type=related_model_type)
Beispiel #6
0
 def visit_type_list(self, t: TypeList) -> Type:
     self.fail('Invalid type', t)
     return AnyType()
Beispiel #7
0
 def visit_unbound_type(self, t: UnboundType) -> Type:
     sym = self.lookup(t.name, t)
     if sym is not None:
         fullname = sym.node.fullname()
         if sym.kind == BOUND_TVAR:
             if len(t.args) > 0:
                 self.fail(
                     'Type variable "{}" used with arguments'.format(
                         t.name), t)
             tvar_expr = cast(TypeVarExpr, sym.node)
             return TypeVarType(t.name, sym.tvar_id, tvar_expr.values,
                                self.builtin_type('builtins.object'),
                                tvar_expr.variance, t.line)
         elif fullname == 'builtins.None':
             return Void()
         elif fullname == 'typing.Any':
             return AnyType()
         elif fullname == 'typing.Tuple':
             if len(t.args) == 2 and isinstance(t.args[1], EllipsisType):
                 # Tuple[T, ...] (uniform, variable-length tuple)
                 node = self.lookup_fqn_func('builtins.tuple')
                 info = cast(TypeInfo, node.node)
                 return Instance(info, [t.args[0].accept(self)], t.line)
             return TupleType(self.anal_array(t.args),
                              self.builtin_type('builtins.tuple'))
         elif fullname == 'typing.Union':
             items = self.anal_array(t.args)
             items = [item for item in items if not isinstance(item, Void)]
             return UnionType.make_union(items)
         elif fullname == 'typing.Optional':
             if len(t.args) != 1:
                 self.fail(
                     'Optional[...] must have exactly one type argument', t)
             items = self.anal_array(t.args)
             # Currently Optional[t] is just an alias for t.
             return items[0]
         elif fullname == 'typing.Callable':
             return self.analyze_callable_type(t)
         elif sym.kind == TYPE_ALIAS:
             # TODO: Generic type aliases.
             return sym.type_override
         elif not isinstance(sym.node, TypeInfo):
             name = sym.fullname
             if name is None:
                 name = sym.node.name()
             if isinstance(sym.node, Var) and isinstance(
                     sym.node.type, AnyType):
                 # Something with an Any type -- make it an alias for Any in a type
                 # context. This is slightly problematic as it allows using the type 'Any'
                 # as a base class -- however, this will fail soon at runtime so the problem
                 # is pretty minor.
                 return AnyType()
             self.fail('Invalid type "{}"'.format(name), t)
             return t
         info = cast(TypeInfo, sym.node)
         if len(t.args) > 0 and info.fullname() == 'builtins.tuple':
             return TupleType(self.anal_array(t.args),
                              Instance(info, [AnyType()], t.line), t.line)
         else:
             # Analyze arguments and construct Instance type. The
             # number of type arguments and their values are
             # checked only later, since we do not always know the
             # valid count at this point. Thus we may construct an
             # Instance with an invalid number of type arguments.
             instance = Instance(info, self.anal_array(t.args), t.line)
             if info.tuple_type is None:
                 return instance
             else:
                 # The class has a Tuple[...] base class so it will be
                 # represented as a tuple type.
                 if t.args:
                     self.fail('Generic tuple types not supported', t)
                     return AnyType()
                 return TupleType(self.anal_array(info.tuple_type.items),
                                  fallback=instance,
                                  line=t.line)
     else:
         return t
Beispiel #8
0
 def test_any(self):
     assert_equal(str(AnyType()), 'Any')
Beispiel #9
0
 def test_generic_unbound_type(self):
     u = UnboundType('Foo', [UnboundType('T'), AnyType()])
     assert_equal(str(u), 'Foo?[T?, Any]')
Beispiel #10
0
def type_object_type(info: TypeInfo,
                     builtin_type: Callable[[str], Instance]) -> ProperType:
    """Return the type of a type object.

    For a generic type G with type variables T and S the type is generally of form

      Callable[..., G[T, S]]

    where ... are argument types for the __init__/__new__ method (without the self
    argument). Also, the fallback type will be 'type' instead of 'function'.
    """

    # We take the type from whichever of __init__ and __new__ is first
    # in the MRO, preferring __init__ if there is a tie.
    init_method = info.get('__init__')
    new_method = info.get('__new__')
    if not init_method or not is_valid_constructor(init_method.node):
        # Must be an invalid class definition.
        return AnyType(TypeOfAny.from_error)
    # There *should* always be a __new__ method except the test stubs
    # lack it, so just copy init_method in that situation
    new_method = new_method or init_method
    if not is_valid_constructor(new_method.node):
        # Must be an invalid class definition.
        return AnyType(TypeOfAny.from_error)

    # The two is_valid_constructor() checks ensure this.
    assert isinstance(new_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator))
    assert isinstance(init_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator))

    init_index = info.mro.index(init_method.node.info)
    new_index = info.mro.index(new_method.node.info)

    fallback = info.metaclass_type or builtin_type('builtins.type')
    if init_index < new_index:
        method = init_method.node  # type: Union[FuncBase, Decorator]
        is_new = False
    elif init_index > new_index:
        method = new_method.node
        is_new = True
    else:
        if init_method.node.info.fullname == 'builtins.object':
            # Both are defined by object.  But if we've got a bogus
            # base class, we can't know for sure, so check for that.
            if info.fallback_to_any:
                # Construct a universal callable as the prototype.
                any_type = AnyType(TypeOfAny.special_form)
                sig = CallableType(arg_types=[any_type, any_type],
                                   arg_kinds=[ARG_STAR, ARG_STAR2],
                                   arg_names=["_args", "_kwds"],
                                   ret_type=any_type,
                                   fallback=builtin_type('builtins.function'))
                return class_callable(sig, info, fallback, None, is_new=False)

        # Otherwise prefer __init__ in a tie. It isn't clear that this
        # is the right thing, but __new__ caused problems with
        # typeshed (#5647).
        method = init_method.node
        is_new = False
    # Construct callable type based on signature of __init__. Adjust
    # return type and insert type arguments.
    if isinstance(method, FuncBase):
        t = function_type(method, fallback)
    else:
        assert isinstance(method.type, ProperType)
        assert isinstance(method.type,
                          FunctionLike)  # is_valid_constructor() ensures this
        t = method.type
    return type_object_type_from_function(t, info, method.info, fallback,
                                          is_new)
Beispiel #11
0
def relationship_hook(ctx: FunctionContext) -> Type:
    """Support basic use cases for relationships.

    Examples:
        from sqlalchemy.orm import relationship

        from one import OneModel
        if TYPE_CHECKING:
            from other import OtherModel

        class User(Base):
            __tablename__ = 'users'
            id = Column(Integer(), primary_key=True)
            one = relationship(OneModel)
            other = relationship("OtherModel")

    This also tries to infer the type argument for 'RelationshipProperty'
    using the 'uselist' flag.
    """
    assert isinstance(ctx.default_return_type, Instance)  # type: ignore[misc]
    original_type_arg = ctx.default_return_type.args[0]
    has_annotation = not isinstance(get_proper_type(original_type_arg),
                                    UninhabitedType)

    arg = get_argument_by_name(ctx, "argument")
    arg_type = get_proper_type(get_argtype_by_name(ctx, "argument"))

    uselist_arg = get_argument_by_name(ctx, "uselist")

    if isinstance(arg, StrExpr):
        name = arg.value
        sym = None  # type: Optional[SymbolTableNode]
        try:
            # Private API for local lookup, but probably needs to be public.
            sym = ctx.api.lookup_qualified(name)  # type: ignore
        except (KeyError, AssertionError):
            pass
        if sym and isinstance(sym.node, TypeInfo):
            new_arg = fill_typevars_with_any(sym.node)  # type: Type
        else:
            ctx.api.fail('Cannot find model "{}"'.format(name), ctx.context)
            # TODO: Add note() to public API.
            ctx.api.note(
                "Only imported models can be found;"  # type: ignore
                ' use "if TYPE_CHECKING: ..." to avoid import cycles',
                ctx.context,
            )
            new_arg = AnyType(TypeOfAny.from_error)
    else:
        if isinstance(arg_type, CallableType) and arg_type.is_type_obj():
            new_arg = fill_typevars_with_any(arg_type.type_object())
        else:
            # Something complex, stay silent for now.
            new_arg = AnyType(TypeOfAny.special_form)

    # We figured out, the model type. Now check if we need to wrap it in Iterable
    if uselist_arg:
        if parse_bool(uselist_arg):
            new_arg = ctx.api.named_generic_type("typing.Iterable", [new_arg])
    else:
        if has_annotation:
            # If there is an annotation we use it as a source of truth.
            # This will cause false negatives, but it is better than lots of false positives.
            new_arg = original_type_arg

    return Instance(
        ctx.default_return_type.type,
        [new_arg],
        line=ctx.default_return_type.line,
        column=ctx.default_return_type.column,
    )
Beispiel #12
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,
                                 itype,
                                 isuper,
                                 is_classmethod,
                                 mx.builtin_type,
                                 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
Beispiel #13
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))
                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
Beispiel #14
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
Beispiel #15
0
 def visit_unbound_type(self, t: UnboundType) -> ProperType:
     return AnyType(TypeOfAny.special_form)
Beispiel #16
0
 def test_tuple_type(self):
     assert_equal(str(TupleType([], None)), 'Tuple[]')
     assert_equal(str(TupleType([self.x], None)), 'Tuple[X?]')
     assert_equal(str(TupleType([self.x, AnyType()], None)), 'Tuple[X?, Any]')
Beispiel #17
0
Datei: join.py Projekt: vdt/mypy
 def visit_unbound_type(self, t: UnboundType) -> Type:
     return AnyType()
Beispiel #18
0
 def visit_raw_str(self, s: str) -> Type:
     # An escape hatch that allows the AST walker in fastparse2 to
     # directly hook into the Python 3.5 type converter in some cases
     # without needing to create an intermediary `ast3.Str` object.
     return (parse_type_comment(s.strip(), self.line, self.errors)
             or AnyType(TypeOfAny.from_error))
Beispiel #19
0
 def visit_ellipsis_type(self, t: EllipsisType) -> Type:
     self.fail("Unexpected '...'", t)
     return AnyType()
Beispiel #20
0
 def generic_visit(self, node: ast3.AST) -> Type:  # type: ignore
     self.fail(TYPE_COMMENT_AST_ERROR, self.line,
               getattr(node, 'col_offset', -1))
     return AnyType(TypeOfAny.from_error)
Beispiel #21
0
    def parse_namedtuple_args(
        self, call: CallExpr, fullname: str
    ) -> Optional[Tuple[List[str], List[Type], List[Expression], bool]]:
        """Parse a namedtuple() call into data needed to construct a type.

        Returns a 4-tuple:
        - List of argument names
        - List of argument types
        - Number of arguments that have a default value
        - Whether the definition typechecked.

        Return None if at least one of the types is not ready.
        """
        # TODO: Share code with check_argument_count in checkexpr.py?
        args = call.args
        if len(args) < 2:
            return self.fail_namedtuple_arg(
                "Too few arguments for namedtuple()", call)
        defaults = []  # type: List[Expression]
        if len(args) > 2:
            # Typed namedtuple doesn't support additional arguments.
            if fullname == 'typing.NamedTuple':
                return self.fail_namedtuple_arg(
                    "Too many arguments for NamedTuple()", call)
            for i, arg_name in enumerate(call.arg_names[2:], 2):
                if arg_name == 'defaults':
                    arg = args[i]
                    # We don't care what the values are, as long as the argument is an iterable
                    # and we can count how many defaults there are.
                    if isinstance(arg, (ListExpr, TupleExpr)):
                        defaults = list(arg.items)
                    else:
                        self.fail(
                            "List or tuple literal expected as the defaults argument to "
                            "namedtuple()", arg)
                    break
        if call.arg_kinds[:2] != [ARG_POS, ARG_POS]:
            return self.fail_namedtuple_arg(
                "Unexpected arguments to namedtuple()", call)
        if not isinstance(args[0], (StrExpr, BytesExpr, UnicodeExpr)):
            return self.fail_namedtuple_arg(
                "namedtuple() expects a string literal as the first argument",
                call)
        types = []  # type: List[Type]
        ok = True
        if not isinstance(args[1], (ListExpr, TupleExpr)):
            if (fullname == 'collections.namedtuple'
                    and isinstance(args[1],
                                   (StrExpr, BytesExpr, UnicodeExpr))):
                str_expr = args[1]
                items = str_expr.value.replace(',', ' ').split()
            else:
                return self.fail_namedtuple_arg(
                    "List or tuple literal expected as the second argument to namedtuple()",
                    call)
        else:
            listexpr = args[1]
            if fullname == 'collections.namedtuple':
                # The fields argument contains just names, with implicit Any types.
                if any(not isinstance(item, (StrExpr, BytesExpr, UnicodeExpr))
                       for item in listexpr.items):
                    return self.fail_namedtuple_arg(
                        "String literal expected as namedtuple() item", call)
                items = [
                    cast(Union[StrExpr, BytesExpr, UnicodeExpr], item).value
                    for item in listexpr.items
                ]
            else:
                # The fields argument contains (name, type) tuples.
                result = self.parse_namedtuple_fields_with_types(
                    listexpr.items, call)
                if result:
                    items, types, _, ok = result
                else:
                    # One of the types is not ready, defer.
                    return None
        if not types:
            types = [AnyType(TypeOfAny.unannotated) for _ in items]
        underscore = [item for item in items if item.startswith('_')]
        if underscore:
            self.fail(
                "namedtuple() field names cannot start with an underscore: " +
                ', '.join(underscore), call)
        if len(defaults) > len(items):
            self.fail("Too many defaults given in call to namedtuple()", call)
            defaults = defaults[:len(items)]
        return items, types, defaults, ok
Beispiel #22
0
 def visit_Str(self, n: ast3.Str) -> Type:
     return (parse_type_comment(n.s.strip(), self.line, self.errors)
             or AnyType(TypeOfAny.from_error))
Beispiel #23
0
    def check_namedtuple_classdef(
        self, defn: ClassDef
    ) -> Optional[Tuple[List[str], List[Type], Dict[str, Expression]]]:
        """Parse and validate fields in named tuple class definition.

        Return a three tuple:
          * field names
          * field types
          * field default values
        or None, if any of the types are not ready.
        """
        if self.options.python_version < (3, 6):
            self.fail(
                'NamedTuple class syntax is only supported in Python 3.6',
                defn)
            return [], [], {}
        if len(defn.base_type_exprs) > 1:
            self.fail('NamedTuple should be a single base', defn)
        items = []  # type: List[str]
        types = []  # type: List[Type]
        default_items = {}  # type: Dict[str, Expression]
        for stmt in defn.defs.body:
            if not isinstance(stmt, AssignmentStmt):
                # Still allow pass or ... (for empty namedtuples).
                if (isinstance(stmt, PassStmt)
                        or (isinstance(stmt, ExpressionStmt)
                            and isinstance(stmt.expr, EllipsisExpr))):
                    continue
                # Also allow methods, including decorated ones.
                if isinstance(stmt, (Decorator, FuncBase)):
                    continue
                # And docstrings.
                if (isinstance(stmt, ExpressionStmt)
                        and isinstance(stmt.expr, StrExpr)):
                    continue
                self.fail(NAMEDTUP_CLASS_ERROR, stmt)
            elif len(stmt.lvalues) > 1 or not isinstance(
                    stmt.lvalues[0], NameExpr):
                # An assignment, but an invalid one.
                self.fail(NAMEDTUP_CLASS_ERROR, stmt)
            else:
                # Append name and type in this case...
                name = stmt.lvalues[0].name
                items.append(name)
                if stmt.type is None:
                    types.append(AnyType(TypeOfAny.unannotated))
                else:
                    analyzed = self.api.anal_type(stmt.type)
                    if analyzed is None:
                        # Something is incomplete. We need to defer this named tuple.
                        return None
                    types.append(analyzed)
                # ...despite possible minor failures that allow further analyzis.
                if name.startswith('_'):
                    self.fail(
                        'NamedTuple field name cannot start with an underscore: {}'
                        .format(name), stmt)
                if stmt.type is None or hasattr(
                        stmt, 'new_syntax') and not stmt.new_syntax:
                    self.fail(NAMEDTUP_CLASS_ERROR, stmt)
                elif isinstance(stmt.rvalue, TempNode):
                    # x: int assigns rvalue to TempNode(AnyType())
                    if default_items:
                        self.fail(
                            'Non-default NamedTuple fields cannot follow default fields',
                            stmt)
                else:
                    default_items[name] = stmt.rvalue
        return items, types, default_items
Beispiel #24
0
    def do_func_def(self,
                    n: Union[ast3.FunctionDef, ast3.AsyncFunctionDef],
                    is_coroutine: bool = False) -> Union[FuncDef, Decorator]:
        """Helper shared between visit_FunctionDef and visit_AsyncFunctionDef."""
        no_type_check = bool(
            n.decorator_list
            and any(is_no_type_check_decorator(d) for d in n.decorator_list))

        args = self.transform_args(n.args,
                                   n.lineno,
                                   no_type_check=no_type_check)

        arg_kinds = [arg.kind for arg in args]
        arg_names = [arg.variable.name()
                     for arg in args]  # type: List[Optional[str]]
        arg_names = [
            None if argument_elide_name(name) else name for name in arg_names
        ]
        if special_function_elide_names(n.name):
            arg_names = [None] * len(arg_names)
        arg_types = []  # type: List[Optional[Type]]
        if no_type_check:
            arg_types = [None] * len(args)
            return_type = None
        elif n.type_comment is not None:
            try:
                func_type_ast = ast3.parse(n.type_comment, '<func_type>',
                                           'func_type')
                assert isinstance(func_type_ast, ast3.FunctionType)
                # for ellipsis arg
                if (len(func_type_ast.argtypes) == 1 and isinstance(
                        func_type_ast.argtypes[0], ast3.Ellipsis)):
                    if n.returns:
                        # PEP 484 disallows both type annotations and type comments
                        self.fail(messages.DUPLICATE_TYPE_SIGNATURES, n.lineno,
                                  n.col_offset)
                    arg_types = [
                        a.type_annotation if a.type_annotation is not None else
                        AnyType(TypeOfAny.unannotated) for a in args
                    ]
                else:
                    # PEP 484 disallows both type annotations and type comments
                    if n.returns or any(a.type_annotation is not None
                                        for a in args):
                        self.fail(messages.DUPLICATE_TYPE_SIGNATURES, n.lineno,
                                  n.col_offset)
                    translated_args = (TypeConverter(
                        self.errors, line=n.lineno).translate_expr_list(
                            func_type_ast.argtypes))
                    arg_types = [
                        a if a is not None else AnyType(TypeOfAny.unannotated)
                        for a in translated_args
                    ]
                return_type = TypeConverter(self.errors, line=n.lineno).visit(
                    func_type_ast.returns)

                # add implicit self type
                if self.in_class() and len(arg_types) < len(args):
                    arg_types.insert(0, AnyType(TypeOfAny.special_form))
            except SyntaxError:
                self.fail(TYPE_COMMENT_SYNTAX_ERROR, n.lineno, n.col_offset)
                if n.type_comment and n.type_comment[0] != "(":
                    self.note('Suggestion: wrap argument types in parentheses',
                              n.lineno, n.col_offset)
                arg_types = [AnyType(TypeOfAny.from_error)] * len(args)
                return_type = AnyType(TypeOfAny.from_error)
        else:
            arg_types = [a.type_annotation for a in args]
            return_type = TypeConverter(
                self.errors,
                line=n.returns.lineno if n.returns else n.lineno).visit(
                    n.returns)

        for arg, arg_type in zip(args, arg_types):
            self.set_type_optional(arg_type, arg.initializer)

        func_type = None
        if any(arg_types) or return_type:
            if len(arg_types) != 1 and any(
                    isinstance(t, EllipsisType) for t in arg_types):
                self.fail(
                    "Ellipses cannot accompany other argument types "
                    "in function type signature.", n.lineno, 0)
            elif len(arg_types) > len(arg_kinds):
                self.fail('Type signature has too many arguments', n.lineno, 0)
            elif len(arg_types) < len(arg_kinds):
                self.fail('Type signature has too few arguments', n.lineno, 0)
            else:
                func_type = CallableType([
                    a if a is not None else AnyType(TypeOfAny.unannotated)
                    for a in arg_types
                ], arg_kinds, arg_names, return_type if return_type is not None
                                         else AnyType(TypeOfAny.unannotated),
                                         _dummy_fallback)

        func_def = FuncDef(n.name, args,
                           self.as_required_block(n.body, n.lineno), func_type)
        if is_coroutine:
            func_def.is_coroutine = True
        if func_type is not None:
            func_type.definition = func_def
            func_type.line = n.lineno

        if n.decorator_list:
            var = Var(func_def.name())
            var.is_ready = False
            var.set_line(n.decorator_list[0].lineno)

            func_def.is_decorated = True
            func_def.set_line(n.lineno + len(n.decorator_list))
            func_def.body.set_line(func_def.get_line())
            return Decorator(func_def,
                             self.translate_expr_list(n.decorator_list), var)
        else:
            return func_def
Beispiel #25
0
 def visit_instance(self, t: Instance) -> Type:
     return Instance(t.type,
                     [AnyType(TypeOfAny.special_form)] * len(t.args),
                     t.line)
Beispiel #26
0
def make_tuple(api: 'TypeChecker', fields: List[MypyType]) -> TupleType:
    # fallback for tuples is any builtins.tuple instance
    fallback = api.named_generic_type('builtins.tuple',
                                      [AnyType(TypeOfAny.special_form)])
    return TupleType(fields, fallback=fallback)
Beispiel #27
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(v, Var):
        return analyze_var(name,
                           v,
                           itype,
                           info,
                           node,
                           is_lvalue,
                           msg,
                           original_type,
                           not_ready_callback,
                           chk=chk)
    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)
Beispiel #28
0
def fill_typevars_with_any(instance: Instance) -> Instance:
    return reparametrize_instance(instance, [AnyType(TypeOfAny.unannotated)])
Beispiel #29
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)
Beispiel #30
0
def _scan_declarative_decorator_stmt(
    cls: ClassDef,
    api: SemanticAnalyzerPluginInterface,
    stmt: Decorator,
    cls_metadata: util.DeclClassApplied,
) -> None:
    """Extract mapping information from a @declared_attr in a declarative
    class.

    E.g.::

        @reg.mapped
        class MyClass:
            # ...

            @declared_attr
            def updated_at(cls) -> Column[DateTime]:
                return Column(DateTime)

    Will resolve in mypy as::

        @reg.mapped
        class MyClass:
            # ...

            updated_at: Mapped[Optional[datetime.datetime]]

    """
    for dec in stmt.decorators:
        if (isinstance(dec, (NameExpr, MemberExpr, SymbolNode))
                and names._type_id_for_named_node(dec) is names.DECLARED_ATTR):
            break
    else:
        return

    dec_index = cls.defs.body.index(stmt)

    left_hand_explicit_type: Optional[ProperType] = None

    if isinstance(stmt.func.type, CallableType):
        func_type = stmt.func.type.ret_type
        if isinstance(func_type, UnboundType):
            type_id = names._type_id_for_unbound_type(func_type, cls, api)
        else:
            # this does not seem to occur unless the type argument is
            # incorrect
            return

        if (type_id in {
                names.MAPPED,
                names.RELATIONSHIP,
                names.COMPOSITE_PROPERTY,
                names.MAPPER_PROPERTY,
                names.SYNONYM_PROPERTY,
                names.COLUMN_PROPERTY,
        } and func_type.args):
            left_hand_explicit_type = get_proper_type(func_type.args[0])
        elif type_id is names.COLUMN and func_type.args:
            typeengine_arg = func_type.args[0]
            if isinstance(typeengine_arg, UnboundType):
                sym = api.lookup_qualified(typeengine_arg.name, typeengine_arg)
                if sym is not None and isinstance(sym.node, TypeInfo):
                    if names._has_base_type_id(sym.node, names.TYPEENGINE):
                        left_hand_explicit_type = UnionType([
                            infer._extract_python_type_from_typeengine(
                                api, sym.node, []),
                            NoneType(),
                        ])
                    else:
                        util.fail(
                            api,
                            "Column type should be a TypeEngine "
                            "subclass not '{}'".format(sym.node.fullname),
                            func_type,
                        )

    if left_hand_explicit_type is None:
        # no type on the decorated function.  our option here is to
        # dig into the function body and get the return type, but they
        # should just have an annotation.
        msg = ("Can't infer type from @declared_attr on function '{}';  "
               "please specify a return type from this function that is "
               "one of: Mapped[<python type>], relationship[<target class>], "
               "Column[<TypeEngine>], MapperProperty[<python type>]")
        util.fail(api, msg.format(stmt.var.name), stmt)

        left_hand_explicit_type = AnyType(TypeOfAny.special_form)

    left_node = NameExpr(stmt.var.name)
    left_node.node = stmt.var

    # totally feeling around in the dark here as I don't totally understand
    # the significance of UnboundType.  It seems to be something that is
    # not going to do what's expected when it is applied as the type of
    # an AssignmentStatement.  So do a feeling-around-in-the-dark version
    # of converting it to the regular Instance/TypeInfo/UnionType structures
    # we see everywhere else.
    if isinstance(left_hand_explicit_type, UnboundType):
        left_hand_explicit_type = get_proper_type(
            util._unbound_to_instance(api, left_hand_explicit_type))

    left_node.node.type = api.named_type("__sa_Mapped",
                                         [left_hand_explicit_type])

    # this will ignore the rvalue entirely
    # rvalue = TempNode(AnyType(TypeOfAny.special_form))

    # rewrite the node as:
    # <attr> : Mapped[<typ>] =
    # _sa_Mapped._empty_constructor(lambda: <function body>)
    # the function body is maintained so it gets type checked internally
    column_descriptor = nodes.NameExpr("__sa_Mapped")
    column_descriptor.fullname = "sqlalchemy.orm.attributes.Mapped"
    mm = nodes.MemberExpr(column_descriptor, "_empty_constructor")

    arg = nodes.LambdaExpr(stmt.func.arguments, stmt.func.body)
    rvalue = CallExpr(
        mm,
        [arg],
        [nodes.ARG_POS],
        ["arg1"],
    )

    new_stmt = AssignmentStmt([left_node], rvalue)
    new_stmt.type = left_node.node.type

    cls_metadata.mapped_attr_names.append(
        (left_node.name, left_hand_explicit_type))
    cls.defs.body[dec_index] = new_stmt