Пример #1
0
def analyze_type_callable_member_access(name: str, typ: FunctionLike,
                                        mx: MemberContext) -> Type:
    # Class attribute.
    # TODO super?
    ret_type = typ.items()[0].ret_type
    assert isinstance(ret_type, ProperType)
    if isinstance(ret_type, TupleType):
        ret_type = tuple_fallback(ret_type)
    if isinstance(ret_type, Instance):
        if not mx.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.
            # TODO: do not rely on same type variables being present in all constructor overloads.
            result = analyze_class_attribute_access(
                ret_type, name, mx, original_vars=typ.items()[0].variables)
            if result:
                return result
        # Look up from the 'type' type.
        return _analyze_member_access(name, typ.fallback, mx)
    else:
        assert False, 'Unexpected type {}'.format(repr(ret_type))
Пример #2
0
def type_object_type_from_function(signature: FunctionLike, info: TypeInfo,
                                   def_info: TypeInfo, fallback: Instance,
                                   is_new: bool) -> FunctionLike:
    # We first need to record all non-trivial (explicit) self types in __init__,
    # since they will not be available after we bind them. Note, we use explicit
    # self-types only in the defining class, similar to __new__ (but not exactly the same,
    # see comment in class_callable below). This is mostly useful for annotating library
    # classes such as subprocess.Popen.
    default_self = fill_typevars(info)
    if not is_new and not info.is_newtype:
        orig_self_types = [(it.arg_types[0]
                            if it.arg_types and it.arg_types[0] != default_self
                            and it.arg_kinds[0] == ARG_POS else None)
                           for it in signature.items()]
    else:
        orig_self_types = [None] * len(signature.items())

    # The __init__ method might come from a generic superclass 'def_info'
    # with type variables that do not map identically to the type variables of
    # the class 'info' being constructed. For example:
    #
    #   class A(Generic[T]):
    #       def __init__(self, x: T) -> None: ...
    #   class B(A[List[T]]):
    #      ...
    #
    # We need to map B's __init__ to the type (List[T]) -> None.
    signature = bind_self(signature,
                          original_type=default_self,
                          is_classmethod=is_new)
    signature = cast(FunctionLike,
                     map_type_from_supertype(signature, info, def_info))

    special_sig = None  # type: Optional[str]
    if def_info.fullname == 'builtins.dict':
        # Special signature!
        special_sig = 'dict'

    if isinstance(signature, CallableType):
        return class_callable(signature, info, fallback, special_sig, is_new,
                              orig_self_types[0])
    else:
        # Overloaded __init__/__new__.
        assert isinstance(signature, Overloaded)
        items = []  # type: List[CallableType]
        for item, orig_self in zip(signature.items(), orig_self_types):
            items.append(
                class_callable(item, info, fallback, special_sig, is_new,
                               orig_self))
        return Overloaded(items)
Пример #3
0
def check_self_arg(functype: FunctionLike, dispatched_arg_type: Type,
                   is_classmethod: bool, context: Context, name: str,
                   msg: MessageBuilder) -> None:
    """For x.f where A.f: A1 -> T, check that meet(type(x), A) <: A1 for each overload.

    dispatched_arg_type is meet(B, A) in the following example

        def g(x: B): x.f
        class A:
            f: Callable[[A1], None]
    """
    # TODO: this is too strict. We can return filtered overloads for matching definitions
    for item in functype.items():
        if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR):
            # No positional first (self) argument (*args is okay).
            msg.no_formal_self(name, item, context)
        else:
            selfarg = item.arg_types[0]
            if is_classmethod:
                dispatched_arg_type = TypeType.make_normalized(
                    dispatched_arg_type)
            if not subtypes.is_subtype(dispatched_arg_type,
                                       erase_to_bound(selfarg)):
                msg.incompatible_self_argument(name, dispatched_arg_type, item,
                                               is_classmethod, context)
Пример #4
0
def check_method_type(functype: FunctionLike, itype: Instance,
                      is_classmethod: bool, context: Context,
                      msg: MessageBuilder) -> None:
    for item in functype.items():
        if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR):
            # No positional first (self) argument (*args is okay).
            msg.invalid_method_type(item, context)
        elif not is_classmethod:
            # Check that self argument has type 'Any' or valid instance type.
            selfarg = item.arg_types[0]
            # If this is a method of a tuple class, correct for the fact that
            # we passed to typ.fallback in analyze_member_access. See #1432.
            if isinstance(selfarg, TupleType):
                selfarg = selfarg.fallback
            if not subtypes.is_subtype(selfarg, itype):
                msg.invalid_method_type(item, context)
        else:
            # Check that cls argument has type 'Any' or valid class type.
            # (This is sufficient for the current treatment of @classmethod,
            # but probably needs to be revisited when we implement Type[C]
            # or advanced variants of it like Type[<args>, C].)
            clsarg = item.arg_types[0]
            if isinstance(clsarg, CallableType) and clsarg.is_type_obj():
                if not subtypes.is_equivalent(clsarg.ret_type, itype):
                    msg.invalid_class_method_type(item, context)
            else:
                if not subtypes.is_equivalent(clsarg, AnyType()):
                    msg.invalid_class_method_type(item, context)
Пример #5
0
def check_method_type(
    functype: FunctionLike, itype: Instance, is_classmethod: bool, context: Context, msg: MessageBuilder
) -> None:
    for item in functype.items():
        if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR):
            # No positional first (self) argument (*args is okay).
            msg.invalid_method_type(item, context)
        elif not is_classmethod:
            # Check that self argument has type 'Any' or valid instance type.
            selfarg = item.arg_types[0]
            # If this is a method of a tuple class, correct for the fact that
            # we passed to typ.fallback in analyze_member_access. See #1432.
            if isinstance(selfarg, TupleType):
                selfarg = selfarg.fallback
            if not subtypes.is_equivalent(selfarg, itype):
                msg.invalid_method_type(item, context)
        else:
            # Check that cls argument has type 'Any' or valid class type.
            # (This is sufficient for the current treatment of @classmethod,
            # but probably needs to be revisited when we implement Type[C]
            # or advanced variants of it like Type[<args>, C].)
            clsarg = item.arg_types[0]
            if isinstance(clsarg, CallableType) and clsarg.is_type_obj():
                if not subtypes.is_equivalent(clsarg.ret_type, itype):
                    msg.invalid_class_method_type(item, context)
            else:
                if not subtypes.is_equivalent(clsarg, AnyType()):
                    msg.invalid_class_method_type(item, context)
Пример #6
0
def check_self_arg(functype: FunctionLike,
                   dispatched_arg_type: Type,
                   is_classmethod: bool,
                   context: Context, name: str,
                   msg: MessageBuilder) -> None:
    """For x.f where A.f: A1 -> T, check that meet(type(x), A) <: A1 for each overload.

    dispatched_arg_type is meet(B, A) in the following example

        def g(x: B): x.f
        class A:
            f: Callable[[A1], None]
    """
    # TODO: this is too strict. We can return filtered overloads for matching definitions
    for item in functype.items():
        if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR):
            # No positional first (self) argument (*args is okay).
            msg.no_formal_self(name, item, context)
        else:
            selfarg = item.arg_types[0]
            if is_classmethod:
                dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type)
            if not subtypes.is_subtype(dispatched_arg_type, erase_to_bound(selfarg)):
                msg.incompatible_self_argument(name, dispatched_arg_type, item,
                                               is_classmethod, context)
Пример #7
0
def analyze_type_callable_member_access(name: str,
                                        typ: FunctionLike,
                                        mx: MemberContext) -> Type:
    # Class attribute.
    # TODO super?
    ret_type = typ.items()[0].ret_type
    if isinstance(ret_type, TupleType):
        ret_type = tuple_fallback(ret_type)
    if isinstance(ret_type, Instance):
        if not mx.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, mx)
            if result:
                return result
        # Look up from the 'type' type.
        return _analyze_member_access(name, typ.fallback, mx)
    else:
        assert False, 'Unexpected type {}'.format(repr(ret_type))
Пример #8
0
def type_object_type_from_function(signature: FunctionLike, info: TypeInfo,
                                   def_info: TypeInfo,
                                   fallback: Instance) -> FunctionLike:
    # The __init__ method might come from a generic superclass
    # (init_or_new.info) with type variables that do not map
    # identically to the type variables of the class being constructed
    # (info). For example
    #
    #   class A(Generic[T]): def __init__(self, x: T) -> None: pass
    #   class B(A[List[T]], Generic[T]): pass
    #
    # We need to first map B's __init__ to the type (List[T]) -> None.
    signature = bind_self(signature)
    signature = cast(FunctionLike,
                     map_type_from_supertype(signature, info, def_info))
    special_sig = None  # type: Optional[str]
    if def_info.fullname() == 'builtins.dict':
        # Special signature!
        special_sig = 'dict'

    if isinstance(signature, CallableType):
        return class_callable(signature, info, fallback, special_sig)
    else:
        # Overloaded __init__/__new__.
        assert isinstance(signature, Overloaded)
        items = []  # type: List[CallableType]
        for item in signature.items():
            items.append(class_callable(item, info, fallback, special_sig))
        return Overloaded(items)
Пример #9
0
def _unify_type(
    function: FunctionLike,
    fetch_type: Callable[[CallableType], MypyType],
) -> MypyType:
    return UnionType.make_union([
        fetch_type(case)
        for case in function.items()
    ])
Пример #10
0
 def _process_typeclass_def_return_type(
     self,
     typeclass_intermediate_def: Instance,
     defn: FunctionLike,
     ctx: FunctionContext,
 ) -> MypyType:
     type_info = defn.type_object()
     instance = Instance(type_info, [])
     typeclass_intermediate_def.args = (instance, )
     return typeclass_intermediate_def
Пример #11
0
def check_method_type(functype: FunctionLike, itype: Instance, context: Context, msg: MessageBuilder) -> None:
    for item in functype.items():
        if not item.arg_types or item.arg_kinds[0] != ARG_POS:
            # No positional first (self) argument.
            msg.invalid_method_type(item, context)
        else:
            # Check that self argument has type 'Any' or valid instance type.
            selfarg = item.arg_types[0]
            if not subtypes.is_equivalent(selfarg, itype):
                msg.invalid_method_type(item, context)
Пример #12
0
def check_method_type(functype: FunctionLike, itype: Instance,
                      context: Context, msg: MessageBuilder) -> None:
    for item in functype.items():
        if not item.arg_types or item.arg_kinds[0] != ARG_POS:
            # No positional first (self) argument.
            msg.invalid_method_type(item, context)
        else:
            # Check that self argument has type 'Any' or valid instance type.
            selfarg = item.arg_types[0]
            if not subtypes.is_equivalent(selfarg, itype):
                msg.invalid_method_type(item, context)
Пример #13
0
def check_self_arg(functype: FunctionLike,
                   dispatched_arg_type: Type,
                   is_classmethod: bool,
                   context: Context, name: str,
                   msg: MessageBuilder) -> FunctionLike:
    """Check that an instance has a valid type for a method with annotated 'self'.

    For example if the method is defined as:
        class A:
            def f(self: S) -> T: ...
    then for 'x.f' we check that meet(type(x), A) <: S. If the method is overloaded, we
    select only overloads items that satisfy this requirement. If there are no matching
    overloads, an error is generated.

    Note: dispatched_arg_type uses a meet to select a relevant item in case if the
    original type of 'x' is a union. This is done because several special methods
    treat union types in ad-hoc manner, so we can't use MemberContext.self_type yet.
    """
    items = functype.items()
    if not items:
        return functype
    new_items = []
    if is_classmethod:
        dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type)
    for item in items:
        if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR):
            # No positional first (self) argument (*args is okay).
            msg.no_formal_self(name, item, context)
            # This is pretty bad, so just return the original signature if
            # there is at least one such error.
            return functype
        else:
            selfarg = item.arg_types[0]
            if subtypes.is_subtype(dispatched_arg_type, erase_typevars(erase_to_bound(selfarg))):
                new_items.append(item)
    if not new_items:
        # Choose first item for the message (it may be not very helpful for overloads).
        msg.incompatible_self_argument(name, dispatched_arg_type, items[0],
                                       is_classmethod, context)
        return functype
    if len(new_items) == 1:
        return new_items[0]
    return Overloaded(new_items)