Exemple #1
0
def analyze_type_type_member_access(name: str, typ: TypeType, mx: MemberContext) -> Type:
    # Similar to analyze_type_callable_attribute_access.
    item = None
    fallback = mx.builtin_type('builtins.type')
    ignore_messages = mx.msg.copy()
    ignore_messages.disable_errors()
    if isinstance(typ.item, Instance):
        item = typ.item
    elif isinstance(typ.item, AnyType):
        mx = mx.copy_modified(messages=ignore_messages)
        return _analyze_member_access(name, fallback, mx)
    elif isinstance(typ.item, TypeVarType):
        if isinstance(typ.item.upper_bound, Instance):
            item = typ.item.upper_bound
    elif isinstance(typ.item, TupleType):
        item = tuple_fallback(typ.item)
    elif isinstance(typ.item, FunctionLike) and typ.item.is_type_obj():
        item = typ.item.fallback
    elif isinstance(typ.item, TypeType):
        # Access member on metaclass object via Type[Type[C]]
        if isinstance(typ.item.item, Instance):
            item = typ.item.item.type.metaclass_type
    if item and not mx.is_operator:
        # See comment above for why operators are skipped
        result = analyze_class_attribute_access(item, name, mx)
        if result:
            if not (isinstance(result, AnyType) and item.type.fallback_to_any):
                return result
            else:
                # We don't want errors on metaclass lookup for classes with Any fallback
                mx = mx.copy_modified(messages=ignore_messages)
    if item is not None:
        fallback = item.type.metaclass_type or fallback
    return _analyze_member_access(name, fallback, mx)
Exemple #2
0
def map_type_from_supertype(typ: Type,
                            sub_info: TypeInfo,
                            super_info: TypeInfo) -> Type:
    """Map type variables in a type defined in a supertype context to be valid
    in the subtype context. Assume that the result is unique; if more than
    one type is possible, return one of the alternatives.

    For example, assume

    . class D(Generic[S]) ...
    . class C(D[E[T]], Generic[T]) ...

    Now S in the context of D would be mapped to E[T] in the context of C.
    """
    # Create the type of self in subtype, of form t[a1, ...].
    inst_type = fill_typevars(sub_info)
    if isinstance(inst_type, TupleType):
        inst_type = tuple_fallback(inst_type)
    # Map the type of self to supertype. This gets us a description of the
    # supertype type variables in terms of subtype variables, i.e. t[t1, ...]
    # so that any type variables in tN are to be interpreted in subtype
    # context.
    inst_type = map_instance_to_supertype(inst_type, super_info)
    # Finally expand the type variables in type with those in the previously
    # constructed type. Note that both type and inst_type may have type
    # variables, but in type they are interpreted in supertype context while
    # in inst_type they are interpreted in subtype context. This works even if
    # the names of type variables in supertype and subtype overlap.
    return expand_type_by_instance(typ, inst_type)
Exemple #3
0
def _analyze_member_access(name: str,
                           typ: Type,
                           mx: MemberContext,
                           override_info: Optional[TypeInfo] = None) -> Type:
    # TODO: This and following functions share some logic with subtypes.find_member;
    #       consider refactoring.
    if isinstance(typ, Instance):
        return analyze_instance_member_access(name, typ, mx, override_info)
    elif isinstance(typ, AnyType):
        # The base object has dynamic type.
        return AnyType(TypeOfAny.from_another_any, source_any=typ)
    elif isinstance(typ, UnionType):
        return analyze_union_member_access(name, typ, mx)
    elif isinstance(typ, FunctionLike) and typ.is_type_obj():
        return analyze_type_callable_member_access(name, typ, mx)
    elif isinstance(typ, TypeType):
        return analyze_type_type_member_access(name, typ, mx)
    elif isinstance(typ, TupleType):
        # Actually look up from the fallback instance type.
        return _analyze_member_access(name, tuple_fallback(typ), mx)
    elif isinstance(typ, (TypedDictType, LiteralType, FunctionLike)):
        # Actually look up from the fallback instance type.
        return _analyze_member_access(name, typ.fallback, mx)
    elif isinstance(typ, NoneTyp):
        return analyze_none_member_access(name, typ, mx)
    elif isinstance(typ, TypeVarType):
        return _analyze_member_access(name, typ.upper_bound, mx)
    elif isinstance(typ, DeletedType):
        mx.msg.deleted_as_rvalue(typ, mx.context)
        return AnyType(TypeOfAny.from_error)
    if mx.chk.should_suppress_optional_error([typ]):
        return AnyType(TypeOfAny.from_error)
    return mx.msg.has_no_attr(mx.original_type, typ, name, mx.context)
Exemple #4
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))
Exemple #5
0
 def visit_tuple_type(self, t: TupleType) -> Type:
     if isinstance(self.s, TupleType) and self.s.length() == t.length():
         items = []  # type: List[Type]
         for i in range(t.length()):
             items.append(self.meet(t.items[i], self.s.items[i]))
         # TODO: What if the fallbacks are different?
         return TupleType(items, tuple_fallback(t))
     elif isinstance(self.s, Instance):
         # meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>].
         if self.s.type.fullname() == 'builtins.tuple' and self.s.args:
             return t.copy_modified(items=[meet_types(it, self.s.args[0]) for it in t.items])
         elif is_proper_subtype(t, self.s):
             # A named tuple that inherits from a normal class
             return t
     return self.default(self.s)
Exemple #6
0
def is_overlapping_types(left: Type,
                         right: Type,
                         ignore_promotions: bool = False,
                         prohibit_none_typevar_overlap: bool = False) -> bool:
    """Can a value of type 'left' also be of type 'right' or vice-versa?

    If 'ignore_promotions' is True, we ignore promotions while checking for overlaps.
    If 'prohibit_none_typevar_overlap' is True, we disallow None from overlapping with
    TypeVars (in both strict-optional and non-strict-optional mode).
    """
    left = get_proper_type(left)
    right = get_proper_type(right)

    def _is_overlapping_types(left: Type, right: Type) -> bool:
        '''Encode the kind of overlapping check to perform.

        This function mostly exists so we don't have to repeat keyword arguments everywhere.'''
        return is_overlapping_types(
            left,
            right,
            ignore_promotions=ignore_promotions,
            prohibit_none_typevar_overlap=prohibit_none_typevar_overlap)

    # We should never encounter this type.
    if isinstance(left, PartialType) or isinstance(right, PartialType):
        assert False, "Unexpectedly encountered partial type"

    # We should also never encounter these types, but it's possible a few
    # have snuck through due to unrelated bugs. For now, we handle these
    # in the same way we handle 'Any'.
    #
    # TODO: Replace these with an 'assert False' once we are more confident.
    illegal_types = (UnboundType, ErasedType, DeletedType)
    if isinstance(left, illegal_types) or isinstance(right, illegal_types):
        return True

    # 'Any' may or may not be overlapping with the other type
    if isinstance(left, AnyType) or isinstance(right, AnyType):
        return True

    # When running under non-strict optional mode, simplify away types of
    # the form 'Union[A, B, C, None]' into just 'Union[A, B, C]'.

    if not state.strict_optional:
        if isinstance(left, UnionType):
            left = UnionType.make_union(left.relevant_items())
        if isinstance(right, UnionType):
            right = UnionType.make_union(right.relevant_items())

    # We check for complete overlaps next as a general-purpose failsafe.
    # If this check fails, we start checking to see if there exists a
    # *partial* overlap between types.
    #
    # These checks will also handle the NoneType and UninhabitedType cases for us.

    if (is_proper_subtype(left, right, ignore_promotions=ignore_promotions)
            or is_proper_subtype(
                right, left, ignore_promotions=ignore_promotions)):
        return True

    # See the docstring for 'get_possible_variants' for more info on what the
    # following lines are doing.

    left_possible = get_possible_variants(left)
    right_possible = get_possible_variants(right)

    # We start by checking multi-variant types like Unions first. We also perform
    # the same logic if either type happens to be a TypeVar.
    #
    # Handling the TypeVars now lets us simulate having them bind to the corresponding
    # type -- if we deferred these checks, the "return-early" logic of the other
    # checks will prevent us from detecting certain overlaps.
    #
    # If both types are singleton variants (and are not TypeVars), we've hit the base case:
    # we skip these checks to avoid infinitely recursing.

    def is_none_typevar_overlap(t1: ProperType, t2: ProperType) -> bool:
        return isinstance(t1, NoneType) and isinstance(t2, TypeVarType)

    if prohibit_none_typevar_overlap:
        if is_none_typevar_overlap(left, right) or is_none_typevar_overlap(
                right, left):
            return False

    if (len(left_possible) > 1 or len(right_possible) > 1
            or isinstance(left, TypeVarType)
            or isinstance(right, TypeVarType)):
        for l in left_possible:
            for r in right_possible:
                if _is_overlapping_types(l, r):
                    return True
        return False

    # Now that we've finished handling TypeVars, we're free to end early
    # if one one of the types is None and we're running in strict-optional mode.
    # (None only overlaps with None in strict-optional mode).
    #
    # We must perform this check after the TypeVar checks because
    # a TypeVar could be bound to None, for example.

    if state.strict_optional and isinstance(left, NoneType) != isinstance(
            right, NoneType):
        return False

    # Next, we handle single-variant types that may be inherently partially overlapping:
    #
    # - TypedDicts
    # - Tuples
    #
    # If we cannot identify a partial overlap and end early, we degrade these two types
    # into their 'Instance' fallbacks.

    if isinstance(left, TypedDictType) and isinstance(right, TypedDictType):
        return are_typed_dicts_overlapping(left,
                                           right,
                                           ignore_promotions=ignore_promotions)
    elif typed_dict_mapping_pair(left, right):
        # Overlaps between TypedDicts and Mappings require dedicated logic.
        return typed_dict_mapping_overlap(left,
                                          right,
                                          overlapping=_is_overlapping_types)
    elif isinstance(left, TypedDictType):
        left = left.fallback
    elif isinstance(right, TypedDictType):
        right = right.fallback

    if is_tuple(left) and is_tuple(right):
        return are_tuples_overlapping(left,
                                      right,
                                      ignore_promotions=ignore_promotions)
    elif isinstance(left, TupleType):
        left = tuple_fallback(left)
    elif isinstance(right, TupleType):
        right = tuple_fallback(right)

    # Next, we handle single-variant types that cannot be inherently partially overlapping,
    # but do require custom logic to inspect.
    #
    # As before, we degrade into 'Instance' whenever possible.

    if isinstance(left, TypeType) and isinstance(right, TypeType):
        return _is_overlapping_types(left.item, right.item)

    def _type_object_overlap(left: ProperType, right: ProperType) -> bool:
        """Special cases for type object types overlaps."""
        # TODO: these checks are a bit in gray area, adjust if they cause problems.
        # 1. Type[C] vs Callable[..., C], where the latter is class object.
        if isinstance(left, TypeType) and isinstance(
                right, CallableType) and right.is_type_obj():
            return _is_overlapping_types(left.item, right.ret_type)
        # 2. Type[C] vs Meta, where Meta is a metaclass for C.
        if isinstance(left, TypeType) and isinstance(right, Instance):
            if isinstance(left.item, Instance):
                left_meta = left.item.type.metaclass_type
                if left_meta is not None:
                    return _is_overlapping_types(left_meta, right)
                # builtins.type (default metaclass) overlaps with all metaclasses
                return right.type.has_base('builtins.type')
            elif isinstance(left.item, AnyType):
                return right.type.has_base('builtins.type')
        # 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks.
        return False

    if isinstance(left, TypeType) or isinstance(right, TypeType):
        return _type_object_overlap(left, right) or _type_object_overlap(
            right, left)

    if isinstance(left, CallableType) and isinstance(right, CallableType):
        return is_callable_compatible(left,
                                      right,
                                      is_compat=_is_overlapping_types,
                                      ignore_pos_arg_names=True,
                                      allow_partial_overlap=True)
    elif isinstance(left, CallableType):
        left = left.fallback
    elif isinstance(right, CallableType):
        right = right.fallback

    if isinstance(left, LiteralType) and isinstance(right, LiteralType):
        if left.value == right.value:
            # If values are the same, we still need to check if fallbacks are overlapping,
            # this is done below.
            left = left.fallback
            right = right.fallback
        else:
            return False
    elif isinstance(left, LiteralType):
        left = left.fallback
    elif isinstance(right, LiteralType):
        right = right.fallback

    # Finally, we handle the case where left and right are instances.

    if isinstance(left, Instance) and isinstance(right, Instance):
        # First we need to handle promotions and structural compatibility for instances
        # that came as fallbacks, so simply call is_subtype() to avoid code duplication.
        if (is_subtype(left, right, ignore_promotions=ignore_promotions) or
                is_subtype(right, left, ignore_promotions=ignore_promotions)):
            return True

        # Two unrelated types cannot be partially overlapping: they're disjoint.
        if left.type.has_base(right.type.fullname()):
            left = map_instance_to_supertype(left, right.type)
        elif right.type.has_base(left.type.fullname()):
            right = map_instance_to_supertype(right, left.type)
        else:
            return False

        if len(left.args) == len(right.args):
            # Note: we don't really care about variance here, since the overlapping check
            # is symmetric and since we want to return 'True' even for partial overlaps.
            #
            # For example, suppose we have two types Wrapper[Parent] and Wrapper[Child].
            # It doesn't matter whether Wrapper is covariant or contravariant since
            # either way, one of the two types will overlap with the other.
            #
            # Similarly, if Wrapper was invariant, the two types could still be partially
            # overlapping -- what if Wrapper[Parent] happened to contain only instances of
            # specifically Child?
            #
            # Or, to use a more concrete example, List[Union[A, B]] and List[Union[B, C]]
            # would be considered partially overlapping since it's possible for both lists
            # to contain only instances of B at runtime.
            for left_arg, right_arg in zip(left.args, right.args):
                if _is_overlapping_types(left_arg, right_arg):
                    return True

        return False

    # We ought to have handled every case by now: we conclude the
    # two types are not overlapping, either completely or partially.
    #
    # Note: it's unclear however, whether returning False is the right thing
    # to do when inferring reachability -- see  https://github.com/python/mypy/issues/5529

    assert type(left) != type(right)
    return False
Exemple #7
0
 def visit_tuple_type(self, left: TupleType) -> bool:
     if isinstance(self.right, TupleType):
         return (is_same_type(tuple_fallback(left), tuple_fallback(self.right))
                 and is_same_types(left.items, self.right.items))
     else:
         return False
Exemple #8
0
 def visit_tuple_type(self, left: TupleType) -> bool:
     if isinstance(self.right, TupleType):
         return (is_same_type(tuple_fallback(left), tuple_fallback(self.right))
                 and is_same_types(left.items, self.right.items))
     else:
         return False
Exemple #9
0
def is_overlapping_types(left: Type,
                         right: Type,
                         ignore_promotions: bool = False,
                         prohibit_none_typevar_overlap: bool = False) -> bool:
    """Can a value of type 'left' also be of type 'right' or vice-versa?

    If 'ignore_promotions' is True, we ignore promotions while checking for overlaps.
    If 'prohibit_none_typevar_overlap' is True, we disallow None from overlapping with
    TypeVars (in both strict-optional and non-strict-optional mode).
    """

    def _is_overlapping_types(left: Type, right: Type) -> bool:
        '''Encode the kind of overlapping check to perform.

        This function mostly exists so we don't have to repeat keyword arguments everywhere.'''
        return is_overlapping_types(
            left, right,
            ignore_promotions=ignore_promotions,
            prohibit_none_typevar_overlap=prohibit_none_typevar_overlap)

    # We should never encounter this type.
    if isinstance(left, PartialType) or isinstance(right, PartialType):
        assert False, "Unexpectedly encountered partial type"

    # We should also never encounter these types, but it's possible a few
    # have snuck through due to unrelated bugs. For now, we handle these
    # in the same way we handle 'Any'.
    #
    # TODO: Replace these with an 'assert False' once we are more confident.
    illegal_types = (UnboundType, ErasedType, DeletedType)
    if isinstance(left, illegal_types) or isinstance(right, illegal_types):
        return True

    # 'Any' may or may not be overlapping with the other type
    if isinstance(left, AnyType) or isinstance(right, AnyType):
        return True

    # When running under non-strict optional mode, simplify away types of
    # the form 'Union[A, B, C, None]' into just 'Union[A, B, C]'.

    if not state.strict_optional:
        if isinstance(left, UnionType):
            left = UnionType.make_union(left.relevant_items())
        if isinstance(right, UnionType):
            right = UnionType.make_union(right.relevant_items())

    # We check for complete overlaps next as a general-purpose failsafe.
    # If this check fails, we start checking to see if there exists a
    # *partial* overlap between types.
    #
    # These checks will also handle the NoneType and UninhabitedType cases for us.

    if (is_proper_subtype(left, right, ignore_promotions=ignore_promotions)
            or is_proper_subtype(right, left, ignore_promotions=ignore_promotions)):
        return True

    # See the docstring for 'get_possible_variants' for more info on what the
    # following lines are doing.

    left_possible = get_possible_variants(left)
    right_possible = get_possible_variants(right)

    # We start by checking multi-variant types like Unions first. We also perform
    # the same logic if either type happens to be a TypeVar.
    #
    # Handling the TypeVars now lets us simulate having them bind to the corresponding
    # type -- if we deferred these checks, the "return-early" logic of the other
    # checks will prevent us from detecting certain overlaps.
    #
    # If both types are singleton variants (and are not TypeVars), we've hit the base case:
    # we skip these checks to avoid infinitely recursing.

    def is_none_typevar_overlap(t1: Type, t2: Type) -> bool:
        return isinstance(t1, NoneType) and isinstance(t2, TypeVarType)

    if prohibit_none_typevar_overlap:
        if is_none_typevar_overlap(left, right) or is_none_typevar_overlap(right, left):
            return False

    if (len(left_possible) > 1 or len(right_possible) > 1
            or isinstance(left, TypeVarType) or isinstance(right, TypeVarType)):
        for l in left_possible:
            for r in right_possible:
                if _is_overlapping_types(l, r):
                    return True
        return False

    # Now that we've finished handling TypeVars, we're free to end early
    # if one one of the types is None and we're running in strict-optional mode.
    # (None only overlaps with None in strict-optional mode).
    #
    # We must perform this check after the TypeVar checks because
    # a TypeVar could be bound to None, for example.

    if state.strict_optional and isinstance(left, NoneType) != isinstance(right, NoneType):
        return False

    # Next, we handle single-variant types that may be inherently partially overlapping:
    #
    # - TypedDicts
    # - Tuples
    #
    # If we cannot identify a partial overlap and end early, we degrade these two types
    # into their 'Instance' fallbacks.

    if isinstance(left, TypedDictType) and isinstance(right, TypedDictType):
        return are_typed_dicts_overlapping(left, right, ignore_promotions=ignore_promotions)
    elif isinstance(left, TypedDictType):
        left = left.fallback
    elif isinstance(right, TypedDictType):
        right = right.fallback

    if is_tuple(left) and is_tuple(right):
        return are_tuples_overlapping(left, right, ignore_promotions=ignore_promotions)
    elif isinstance(left, TupleType):
        left = tuple_fallback(left)
    elif isinstance(right, TupleType):
        right = tuple_fallback(right)

    # Next, we handle single-variant types that cannot be inherently partially overlapping,
    # but do require custom logic to inspect.
    #
    # As before, we degrade into 'Instance' whenever possible.

    if isinstance(left, TypeType) and isinstance(right, TypeType):
        return _is_overlapping_types(left.item, right.item)

    def _type_object_overlap(left: Type, right: Type) -> bool:
        """Special cases for type object types overlaps."""
        # TODO: these checks are a bit in gray area, adjust if they cause problems.
        # 1. Type[C] vs Callable[..., C], where the latter is class object.
        if isinstance(left, TypeType) and isinstance(right, CallableType) and right.is_type_obj():
            return _is_overlapping_types(left.item, right.ret_type)
        # 2. Type[C] vs Meta, where Meta is a metaclass for C.
        if (isinstance(left, TypeType) and isinstance(left.item, Instance) and
                isinstance(right, Instance)):
            left_meta = left.item.type.metaclass_type
            if left_meta is not None:
                return _is_overlapping_types(left_meta, right)
            # builtins.type (default metaclass) overlaps with all metaclasses
            return right.type.has_base('builtins.type')
        # 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks.
        return False

    if isinstance(left, TypeType) or isinstance(right, TypeType):
        return _type_object_overlap(left, right) or _type_object_overlap(right, left)

    if isinstance(left, CallableType) and isinstance(right, CallableType):
        return is_callable_compatible(left, right,
                                      is_compat=_is_overlapping_types,
                                      ignore_pos_arg_names=True,
                                      allow_partial_overlap=True)
    elif isinstance(left, CallableType):
        left = left.fallback
    elif isinstance(right, CallableType):
        right = right.fallback

    if isinstance(left, LiteralType) and isinstance(right, LiteralType):
        return left == right
    elif isinstance(left, LiteralType):
        left = left.fallback
    elif isinstance(right, LiteralType):
        right = right.fallback

    # Finally, we handle the case where left and right are instances.

    if isinstance(left, Instance) and isinstance(right, Instance):
        if left.type.is_protocol and is_protocol_implementation(right, left):
            return True
        if right.type.is_protocol and is_protocol_implementation(left, right):
            return True

        # Two unrelated types cannot be partially overlapping: they're disjoint.
        # We don't need to handle promotions because they've already been handled
        # by the calls to `is_subtype(...)` up above (and promotable types never
        # have any generic arguments we need to recurse on).
        if left.type.has_base(right.type.fullname()):
            left = map_instance_to_supertype(left, right.type)
        elif right.type.has_base(left.type.fullname()):
            right = map_instance_to_supertype(right, left.type)
        else:
            return False

        if len(left.args) == len(right.args):
            # Note: we don't really care about variance here, since the overlapping check
            # is symmetric and since we want to return 'True' even for partial overlaps.
            #
            # For example, suppose we have two types Wrapper[Parent] and Wrapper[Child].
            # It doesn't matter whether Wrapper is covariant or contravariant since
            # either way, one of the two types will overlap with the other.
            #
            # Similarly, if Wrapper was invariant, the two types could still be partially
            # overlapping -- what if Wrapper[Parent] happened to contain only instances of
            # specifically Child?
            #
            # Or, to use a more concrete example, List[Union[A, B]] and List[Union[B, C]]
            # would be considered partially overlapping since it's possible for both lists
            # to contain only instances of B at runtime.
            for left_arg, right_arg in zip(left.args, right.args):
                if _is_overlapping_types(left_arg, right_arg):
                    return True

        return False

    # We ought to have handled every case by now: we conclude the
    # two types are not overlapping, either completely or partially.
    #
    # Note: it's unclear however, whether returning False is the right thing
    # to do when inferring reachability -- see  https://github.com/python/mypy/issues/5529

    assert type(left) != type(right)
    return False