Beispiel #1
0
def cached_function_method_signature(ctx: MethodSigContext) -> CallableType:
    """Fixes the `_CachedFunction.__call__` signature to be correct.

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

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

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

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

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

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

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

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

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

    return signature
Beispiel #2
0
    def test_none(self) -> None:
        self.assert_meet(NoneType(), NoneType(), NoneType())

        self.assert_meet(NoneType(), self.fx.anyt, NoneType())

        # Any type t joined with None results in None, unless t is Any.
        for t in [self.fx.a, self.fx.o, UnboundType('x'), self.fx.t,
                  self.tuple(), self.callable(self.fx.a, self.fx.b)]:
            self.assert_meet(t, NoneType(), NoneType())
Beispiel #3
0
    def build_newtype_typeinfo(self, name: str, old_type: Type, base_type: Instance) -> TypeInfo:
        info = self.api.basic_new_typeinfo(name, base_type)
        info.is_newtype = True

        # Add __init__ method
        args = [Argument(Var('self'), NoneType(), None, ARG_POS),
                self.make_argument('item', old_type)]
        signature = CallableType(
            arg_types=[Instance(info, []), old_type],
            arg_kinds=[arg.kind for arg in args],
            arg_names=['self', 'item'],
            ret_type=NoneType(),
            fallback=self.api.named_type('__builtins__.function'),
            name=name)
        init_func = FuncDef('__init__', args, Block([]), typ=signature)
        init_func.info = info
        init_func._fullname = info.fullname + '.__init__'
        info.names['__init__'] = SymbolTableNode(MDEF, init_func)

        return info
Beispiel #4
0
def trivial_meet(s: Type, t: Type) -> ProperType:
    """Return one of types (expanded) if it is a subtype of other, otherwise bottom type."""
    if is_subtype(s, t):
        return get_proper_type(s)
    elif is_subtype(t, s):
        return get_proper_type(t)
    else:
        if state.strict_optional:
            return UninhabitedType()
        else:
            return NoneType()
Beispiel #5
0
def dynamic_init_hook(ctx: ClassDefContext) -> None:
    if "__init__" in ctx.cls.info.names:
        return

    # transform
    from mypy.plugins.common import add_method_to_class
    from mypy.types import NoneType
    from mypy.nodes import (
        AssignmentStmt,
        NameExpr,
        PlaceholderNode,
        Var,
        Argument,
        ARG_NAMED,
    )

    # from: mypy.plugins.dataclasses
    args = []
    cls = ctx.cls
    for stmt in cls.defs.body:
        # Any assignment that doesn't use the new type declaration
        # syntax can be ignored out of hand.
        if not (isinstance(stmt, AssignmentStmt) and stmt.new_syntax):
            continue

        # a: int, b: str = 1, 'foo' is not supported syntax so we
        # don't have to worry about it.
        lhs = stmt.lvalues[0]
        if not isinstance(lhs, NameExpr):
            continue

        sym = cls.info.names.get(lhs.name)
        if sym is None:
            # This name is likely blocked by a star import. We don't need to defer because
            # defer() is already called by mark_incomplete().
            continue

        node = sym.node
        if isinstance(node, PlaceholderNode):
            # This node is not ready yet.
            return None
        assert isinstance(node, Var)

        # x: ClassVar[int] is ignored by dataclasses.
        if node.is_classvar:
            continue

        args.append(Argument(node, node.type, None, ARG_NAMED))

    add_method_to_class(ctx.api,
                        cls,
                        "__init__",
                        args=args,
                        return_type=NoneType())
Beispiel #6
0
 def test_create_mismatch(self) -> None:
     own_symbol = Var("x", NoneType())
     other_symbol = Var("x", None)
     data = {"name_a": "package.x", "name_b": "package.x"}
     result = ComparisonResult.create_mismatch(own_symbol,
                                               other_symbol,
                                               data=data)
     assert result.match_result is MatchResult.MISMATCH
     assert result.symbol is own_symbol
     assert result.reference is other_symbol
     assert result.message is not None
     assert result.data is data
Beispiel #7
0
def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
    """Return the declared type narrowed down to another type."""
    # TODO: check infinite recursion for aliases here.
    if isinstance(narrowed, TypeGuardedType):  # type: ignore[misc]
        # A type guard forces the new type even if it doesn't overlap the old.
        return narrowed.type_guard

    declared = get_proper_type(declared)
    narrowed = get_proper_type(narrowed)

    if declared == narrowed:
        return declared
    if isinstance(declared, UnionType):
        return make_simplified_union([
            narrow_declared_type(x, narrowed)
            for x in declared.relevant_items()
        ])
    if is_enum_overlapping_union(declared, narrowed):
        return narrowed
    elif not is_overlapping_types(
            declared, narrowed, prohibit_none_typevar_overlap=True):
        if state.strict_optional:
            return UninhabitedType()
        else:
            return NoneType()
    elif isinstance(narrowed, UnionType):
        return make_simplified_union([
            narrow_declared_type(declared, x)
            for x in narrowed.relevant_items()
        ])
    elif isinstance(narrowed, AnyType):
        return narrowed
    elif isinstance(narrowed, TypeVarType) and is_subtype(
            narrowed.upper_bound, declared):
        return narrowed
    elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType):
        return TypeType.make_normalized(
            narrow_declared_type(declared.item, narrowed.item))
    elif (isinstance(declared, TypeType) and isinstance(narrowed, Instance)
          and narrowed.type.is_metaclass()):
        # We'd need intersection types, so give up.
        return declared
    elif isinstance(declared, (Instance, TupleType, TypeType, LiteralType)):
        return meet_types(declared, narrowed)
    elif isinstance(declared, TypedDictType) and isinstance(
            narrowed, Instance):
        # Special case useful for selecting TypedDicts from unions using isinstance(x, dict).
        if (narrowed.type.fullname == 'builtins.dict' and all(
                isinstance(t, AnyType)
                for t in get_proper_types(narrowed.args))):
            return declared
        return meet_types(declared, narrowed)
    return narrowed
Beispiel #8
0
 def visit_instance(self, t: Instance) -> ProperType:
     if isinstance(self.s, Instance):
         si = self.s
         if t.type == si.type:
             if is_subtype(t, self.s) or is_subtype(self.s, t):
                 # Combine type arguments. We could have used join below
                 # equivalently.
                 args = []  # type: List[Type]
                 for i in range(len(t.args)):
                     args.append(self.meet(t.args[i], si.args[i]))
                 return Instance(t.type, args)
             else:
                 if state.strict_optional:
                     return UninhabitedType()
                 else:
                     return NoneType()
         else:
             if is_subtype(t, self.s):
                 return t
             elif is_subtype(self.s, t):
                 # See also above comment.
                 return self.s
             else:
                 if state.strict_optional:
                     return UninhabitedType()
                 else:
                     return NoneType()
     elif isinstance(self.s, FunctionLike) and t.type.is_protocol:
         call = unpack_callback_protocol(t)
         if call:
             return meet_types(call, self.s)
     elif isinstance(self.s, TypeType):
         return meet_types(t, self.s)
     elif isinstance(self.s, TupleType):
         return meet_types(t, self.s)
     elif isinstance(self.s, LiteralType):
         return meet_types(t, self.s)
     elif isinstance(self.s, TypedDictType):
         return meet_types(t, self.s)
     return self.default(self.s)
Beispiel #9
0
def transform_oas_type(
    oas_type: oas_model.OASType[Any],
    handler_type: ProperType,
    ctx: FunctionContext,
) -> Type:
    m_type: Optional[Type] = None
    union_members: List[Type] = []

    assert isinstance(ctx.api, TypeChecker)

    if isinstance(oas_type, oas_model.OASStringType):
        m_type = ctx.api.str_type()
    elif isinstance(oas_type, oas_model.OASBooleanType):
        m_type = ctx.api.named_type('bool')
    elif isinstance(oas_type, oas_model.OASNumberType):
        m_type = ctx.api.named_type(
            'int' if issubclass(int, oas_type.number_cls) else 'float', )
    elif isinstance(oas_type, oas_model.OASOneOfType):
        union_members = [
            transform_oas_type(
                nested_type,
                handler_type,
                ctx,
            ) for include_type, nested_type in oas_type.schemas if include_type
        ]
    elif isinstance(oas_type, oas_model.OASArrayType):
        m_type = ctx.api.named_generic_type(
            name='set' if oas_type.unique_items else 'list',
            args=[
                transform_oas_type(
                    oas_type.items_type,
                    handler_type,
                    ctx,
                )
            ],
        )
    elif isinstance(oas_type, oas_model.OASObjectType):
        m_type = transform_oas_object_type(oas_type, handler_type, ctx)
    else:
        raise NotImplementedError(f'{oas_type} not yet implemented')

    if m_type is not None:
        m_type.set_line(handler_type)
        union_members.append(m_type)

    if oas_type.nullable:
        union_members.append(NoneType())

    ut = simplify_union(UnionType.make_union(items=union_members))
    ut.set_line(handler_type)
    return ut
Beispiel #10
0
def make_fake_register_class_instance(api: CheckerPluginInterface, type_args: Sequence[Type]
                                      ) -> Instance:
    defn = ClassDef(REGISTER_RETURN_CLASS, Block([]))
    defn.fullname = f'functools.{REGISTER_RETURN_CLASS}'
    info = TypeInfo(SymbolTable(), defn, "functools")
    obj_type = api.named_generic_type('builtins.object', []).type
    info.bases = [Instance(obj_type, [])]
    info.mro = [info, obj_type]
    defn.info = info

    func_arg = Argument(Var('name'), AnyType(TypeOfAny.implementation_artifact), None, ARG_POS)
    add_method_to_class(api, defn, '__call__', [func_arg], NoneType())

    return Instance(info, type_args)
Beispiel #11
0
    def visit_singleton_pattern(self, o: SingletonPattern) -> PatternType:
        current_type = self.type_context[-1]
        value: Union[bool, None] = o.value
        if isinstance(value, bool):
            typ = self.chk.expr_checker.infer_literal_expr_type(
                value, "builtins.bool")
        elif value is None:
            typ = NoneType()
        else:
            assert False

        narrowed_type, rest_type = self.chk.conditional_types_with_intersection(
            current_type, [get_type_range(typ)], o, default=current_type)
        return PatternType(narrowed_type, rest_type, {})
Beispiel #12
0
    def get_suggestion(self, mod: str, node: FuncDef) -> PyAnnotateSignature:
        """Compute a suggestion for a function.

        Return the type and whether the first argument should be ignored.
        """
        graph = self.graph
        callsites, orig_errors = self.get_callsites(node)
        uses = get_arg_uses(self.manager.all_types, node)

        if self.no_errors and orig_errors:
            raise SuggestionFailure("Function does not typecheck.")

        is_method = bool(node.info) and not node.is_static

        if len(node.arg_names) >= 10:
            raise SuggestionFailure("Too many arguments")

        with strict_optional_set(graph[mod].options.strict_optional):
            guesses = self.get_guesses(
                is_method,
                self.get_starting_type(node),
                self.get_default_arg_types(graph[mod], node),
                callsites,
                uses,
            )
        guesses = self.filter_options(guesses, is_method)
        if len(guesses) > self.max_guesses:
            raise SuggestionFailure("Too many possibilities!")
        best, _ = self.find_best(node, guesses)

        # Now try to find the return type!
        self.try_type(node, best)
        returns = get_return_types(self.manager.all_types, node)
        with strict_optional_set(graph[mod].options.strict_optional):
            if returns:
                ret_types = generate_type_combinations(returns)
            else:
                ret_types = [NoneType()]

        guesses = [
            best.copy_modified(ret_type=refine_type(best.ret_type, t))
            for t in ret_types
        ]
        guesses = self.filter_options(guesses, is_method)
        best, errors = self.find_best(node, guesses)

        if self.no_errors and errors:
            raise SuggestionFailure("No annotation without errors")

        return self.pyannotate_signature(mod, is_method, best)
Beispiel #13
0
def strawberry_pydantic_class_callback(ctx: ClassDefContext) -> None:
    # in future we want to have a proper pydantic plugin, but for now
    # let's fallback to **kwargs for __init__, some resources are here:
    # https://github.com/samuelcolvin/pydantic/blob/master/pydantic/mypy.py
    # >>> model_index = ctx.cls.decorators[0].arg_names.index("model")
    # >>> model_name = ctx.cls.decorators[0].args[model_index].name

    # >>> model_type = ctx.api.named_type("UserModel")
    # >>> model_type = ctx.api.lookup(model_name, Context())

    model_expression = _get_argument(call=ctx.reason,
                                     name="model")  # type: ignore
    if model_expression is None:
        ctx.api.fail("model argument in decorator failed to be parsed",
                     ctx.reason)

    else:
        # Add __init__
        init_args = [
            Argument(Var("kwargs"), AnyType(TypeOfAny.explicit), None,
                     ARG_STAR2)
        ]
        add_method(ctx, "__init__", init_args, NoneType())

        model_type = _get_type_for_expr(model_expression, ctx.api)

        # Add to_pydantic
        add_method(
            ctx,
            "to_pydantic",
            args=[],
            return_type=model_type,
        )

        # Add from_pydantic
        model_argument = Argument(
            variable=Var(name="instance", type=model_type),
            type_annotation=model_type,
            initializer=None,
            kind=ARG_OPT,
        )

        add_static_method_to_class(
            ctx.api,
            ctx.cls,
            name="from_pydantic",
            args=[model_argument],
            return_type=fill_typevars(ctx.cls.info),
        )
Beispiel #14
0
def _unbound_to_instance(
    api: SemanticAnalyzerPluginInterface, typ: Type
) -> Type:
    """Take the UnboundType that we seem to get as the ret_type from a FuncDef
    and convert it into an Instance/TypeInfo kind of structure that seems
    to work as the left-hand type of an AssignmentStatement.

    """

    if not isinstance(typ, UnboundType):
        return typ

    # TODO: figure out a more robust way to check this.  The node is some
    # kind of _SpecialForm, there's a typing.Optional that's _SpecialForm,
    # but I cant figure out how to get them to match up
    if typ.name == "Optional":
        # convert from "Optional?" to the more familiar
        # UnionType[..., NoneType()]
        return _unbound_to_instance(
            api,
            UnionType(
                [_unbound_to_instance(api, typ_arg) for typ_arg in typ.args]
                + [NoneType()]
            ),
        )

    node = api.lookup_qualified(typ.name, typ)

    if (
        node is not None
        and isinstance(node, SymbolTableNode)
        and isinstance(node.node, TypeInfo)
    ):
        bound_type = node.node

        return Instance(
            bound_type,
            [
                _unbound_to_instance(api, arg)
                if isinstance(arg, UnboundType)
                else arg
                for arg in typ.args
            ],
        )
    else:
        return typ
Beispiel #15
0
    def get_suggestion(self, function: str) -> str:
        """Compute a suggestion for a function.

        Return the type and whether the first argument should be ignored.
        """
        graph = self.graph
        mod, _, node = self.find_node(function)
        callsites, orig_errors = self.get_callsites(node)

        if self.no_errors and orig_errors:
            raise SuggestionFailure("Function does not typecheck.")

        # FIXME: what about static and class methods?
        is_method = bool(node.info)

        if len(node.arg_names) >= 10:
            raise SuggestionFailure("Too many arguments")

        with strict_optional_set(graph[mod].options.strict_optional):
            guesses = self.get_guesses(
                is_method,
                self.get_trivial_type(node),
                self.get_default_arg_types(graph[mod], node),
                callsites)
        guesses = self.filter_options(guesses)
        if len(guesses) > self.max_guesses:
            raise SuggestionFailure("Too many possibilities!")
        best, _ = self.find_best(node, guesses)

        # Now try to find the return type!
        self.try_type(node, best)
        returns = get_return_types(self.manager.all_types, node)
        with strict_optional_set(graph[mod].options.strict_optional):
            if returns:
                ret_types = generate_type_combinations(returns)
            else:
                ret_types = [NoneType()]

        guesses = [best.copy_modified(ret_type=t) for t in ret_types]
        guesses = self.filter_options(guesses)
        best, errors = self.find_best(node, guesses)

        if self.no_errors and errors:
            raise SuggestionFailure("No annotation without errors")

        return self.format_callable(mod, is_method, best)
Beispiel #16
0
def _change_decorator_function_type(
    decorated: CallableType,
    decorator: CallableType,
    provided_arguments: List[str],
) -> CallableType:
    decorator.arg_kinds = decorated.arg_kinds
    decorator.arg_names = decorated.arg_names

    # Mark provided arguments as optional
    decorator.arg_types = copy.copy(decorated.arg_types)
    for argument in provided_arguments:
        index = decorated.arg_names.index(argument)
        decorated_type = decorated.arg_types[index]
        decorator.arg_types[index] = UnionType.make_union(
            [decorated_type, NoneType()])
        decorated.arg_kinds[index] = ARG_NAMED_OPT

    return decorator
Beispiel #17
0
def _add_init(ctx: 'mypy.plugin.ClassDefContext', attributes: List[Attribute],
              adder: 'MethodAdder') -> None:
    """Generate an __init__ method for the attributes and add it to the class."""
    args = [attribute.argument(ctx) for attribute in attributes if attribute.init]
    if all(
        # We use getattr rather than instance checks because the variable.type
        # might be wrapped into a Union or some other type, but even non-Any
        # types reliably track the fact that the argument was not annotated.
        getattr(arg.variable.type, "type_of_any", None) == TypeOfAny.unannotated
        for arg in args
    ):
        # This workaround makes --disallow-incomplete-defs usable with attrs,
        # but is definitely suboptimal as a long-term solution.
        # See https://github.com/python/mypy/issues/5954 for discussion.
        for a in args:
            a.variable.type = AnyType(TypeOfAny.implementation_artifact)
            a.type_annotation = AnyType(TypeOfAny.implementation_artifact)
    adder.add_method('__init__', args, NoneType())
Beispiel #18
0
    def test_generic_function_type(self) -> None:
        c = CallableType([self.x, self.y], [ARG_POS, ARG_POS], [None, None],
                         self.y,
                         self.function,
                         name=None,
                         variables=[TypeVarDef('X', 'X', -1, [], self.fx.o)])
        assert_equal(str(c), 'def [X] (X?, Y?) -> Y?')

        v = [
            TypeVarDef('Y', 'Y', -1, [], self.fx.o),
            TypeVarDef('X', 'X', -2, [], self.fx.o)
        ]
        c2 = CallableType([], [], [],
                          NoneType(),
                          self.function,
                          name=None,
                          variables=v)
        assert_equal(str(c2), 'def [Y, X] ()')
Beispiel #19
0
    def add_initializer(self, fields: List['PydanticModelField'], config: 'ModelConfigData', is_settings: bool) -> None:
        """
        Adds a fields-aware `__init__` method to the class.

        The added `__init__` will be annotated with types vs. all `Any` depending on the plugin settings.
        """
        ctx = self._ctx
        typed = self.plugin_config.init_typed
        use_alias = config.allow_population_by_field_name is not True
        force_all_optional = is_settings or bool(
            config.has_alias_generator and not config.allow_population_by_field_name
        )
        init_arguments = self.get_field_arguments(
            fields, typed=typed, force_all_optional=force_all_optional, use_alias=use_alias
        )
        if not self.should_init_forbid_extra(fields, config):
            var = Var('kwargs')
            init_arguments.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2))
        add_method(ctx, '__init__', init_arguments, NoneType())
Beispiel #20
0
    def add_construct_method(self, fields: List['PydanticModelField']) -> None:
        """
        Adds a fully typed `construct` classmethod to the class.

        Similar to the fields-aware __init__ method, but always uses the field names (not aliases),
        and does not treat settings fields as optional.
        """
        ctx = self._ctx
        set_str = ctx.api.named_type(
            f'{BUILTINS_NAME}.set',
            [ctx.api.named_type(f'{BUILTINS_NAME}.str')])
        optional_set_str = UnionType([set_str, NoneType()])
        fields_set_argument = Argument(Var('_fields_set', optional_set_str),
                                       optional_set_str, None, ARG_OPT)
        construct_arguments = self.get_field_arguments(
            fields, typed=True, force_all_optional=False, use_alias=False)
        construct_arguments = [fields_set_argument] + construct_arguments

        obj_type = ctx.api.named_type(f'{BUILTINS_NAME}.object')
        self_tvar_name = '_PydanticBaseModel'  # Make sure it does not conflict with other names in the class
        tvar_fullname = ctx.cls.fullname + '.' + self_tvar_name
        tvd = TypeVarDef(self_tvar_name, tvar_fullname, -1, [], obj_type)
        self_tvar_expr = TypeVarExpr(self_tvar_name, tvar_fullname, [],
                                     obj_type)
        ctx.cls.info.names[self_tvar_name] = SymbolTableNode(
            MDEF, self_tvar_expr)

        # Backward-compatible with TypeVarDef from Mypy 0.910.
        if isinstance(tvd, TypeVarType):
            self_type = tvd
        else:
            self_type = TypeVarType(tvd)  # type: ignore[call-arg]

        add_method(
            ctx,
            'construct',
            construct_arguments,
            return_type=self_type,
            self_type=self_type,
            tvar_def=tvd,
            is_classmethod=True,
        )
Beispiel #21
0
def transform_parameter_to_type(
    param: oas.OASParameter,
    handler_arg_type: ProperType,
    handler_arg_default_value: HandlerArgDefaultValue,
    ctx: FunctionContext,
) -> Type:
    oas_is_required = param.required
    handler_has_default = (handler_arg_default_value
                           is not _ARG_NO_DEFAULT_VALUE_MARKER)
    if not oas_is_required and not handler_has_default:
        needs_optional = True
    elif isinstance(handler_arg_default_value, type(None)):
        needs_optional = True
    else:
        needs_optional = False

    if isinstance(param.schema, tuple):
        oas_type, _ = param.schema
        items = [transform_oas_type(
            oas_type,
            handler_arg_type,
            ctx,
        )]
    else:
        items = [
            transform_oas_type(
                v.schema,
                handler_arg_type,
                ctx,
            ) for v in param.schema.values()
        ]

    if needs_optional:
        items.append(NoneType())

    return simplify_union(
        UnionType.make_union(
            items=items,
            line=(handler_arg_type.line or handler_arg_type.end_line) or -1,
            column=handler_arg_type.column,
        ), )
Beispiel #22
0
def attr_hook(ctx):
    assert isinstance(ctx.default_return_type, Instance)
    if ctx.default_return_type.type.fullname() == 'mod.Attr':
        attr_base = ctx.default_return_type
    else:
        attr_base = None
    for base in ctx.default_return_type.type.bases:
        if base.type.fullname() == 'mod.Attr':
            attr_base = base
            break
    assert attr_base is not None
    last_arg_exprs = ctx.args[-1]
    if any(
            isinstance(expr, NameExpr) and expr.name == 'True'
            for expr in last_arg_exprs):
        return attr_base
    assert len(attr_base.args) == 1
    arg_type = attr_base.args[0]
    return Instance(attr_base.type, [UnionType([arg_type, NoneType()])],
                    line=ctx.default_return_type.line,
                    column=ctx.default_return_type.column)
Beispiel #23
0
def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
    """Return the declared type narrowed down to another type."""
    # TODO: check infinite recursion for aliases here.
    declared = get_proper_type(declared)
    narrowed = get_proper_type(narrowed)

    if declared == narrowed:
        return declared
    if isinstance(declared, UnionType):
        return make_simplified_union([
            narrow_declared_type(x, narrowed)
            for x in declared.relevant_items()
        ])
    elif not is_overlapping_types(
            declared, narrowed, prohibit_none_typevar_overlap=True):
        if state.strict_optional:
            return UninhabitedType()
        else:
            return NoneType()
    elif isinstance(narrowed, UnionType):
        return make_simplified_union([
            narrow_declared_type(declared, x)
            for x in narrowed.relevant_items()
        ])
    elif isinstance(narrowed, AnyType):
        return narrowed
    elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType):
        return TypeType.make_normalized(
            narrow_declared_type(declared.item, narrowed.item))
    elif isinstance(declared, (Instance, TupleType, TypeType, LiteralType)):
        return meet_types(declared, narrowed)
    elif isinstance(declared, TypedDictType) and isinstance(
            narrowed, Instance):
        # Special case useful for selecting TypedDicts from unions using isinstance(x, dict).
        if (narrowed.type.fullname == 'builtins.dict' and all(
                isinstance(t, AnyType)
                for t in get_proper_types(narrowed.args))):
            return declared
        return meet_types(declared, narrowed)
    return narrowed
Beispiel #24
0
def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
    """Return the declared type narrowed down to another type."""
    if declared == narrowed:
        return declared
    if isinstance(declared, UnionType):
        return UnionType.make_simplified_union([narrow_declared_type(x, narrowed)
                                                for x in declared.relevant_items()])
    elif not is_overlapping_types(declared, narrowed,
                                  prohibit_none_typevar_overlap=True):
        if state.strict_optional:
            return UninhabitedType()
        else:
            return NoneType()
    elif isinstance(narrowed, UnionType):
        return UnionType.make_simplified_union([narrow_declared_type(declared, x)
                                                for x in narrowed.relevant_items()])
    elif isinstance(narrowed, AnyType):
        return narrowed
    elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType):
        return TypeType.make_normalized(narrow_declared_type(declared.item, narrowed.item))
    elif isinstance(declared, (Instance, TupleType, TypeType, LiteralType)):
        return meet_types(declared, narrowed)
    return narrowed
Beispiel #25
0
def layout_class_callback(ctx: ClassDefContext) -> None:
    path_type = ctx.api.builtin_type("pathlib.Path")
    config_type = ctx.api.named_type_or_none(
        "tts.config.Config")  # type: ignore

    # Change the types of class members when type checking the body to `pathlib.Path`.
    # `_unpacked_layout` expects the values to be paths.
    for stmt in ctx.cls.defs.body:
        assert isinstance(stmt, AssignmentStmt)
        assert len(stmt.lvalues) == 1
        lvalue = stmt.lvalues[0]
        assert isinstance(lvalue, NameExpr)

        lvalue.node = _make_name_lvalue_var(lvalue, path_type, ctx)

    add_method(
        ctx,
        "__init__",
        [
            Argument(Var("path"), path_type, None, ARG_POS),
            Argument(Var("config"), config_type, None, ARG_POS),
        ],
        NoneType(),
    )
    def transform(self) -> None:
        """Apply all the necessary transformations to the underlying
        dataclass so as to ensure it is fully type checked according
        to the rules in PEP 557.
        """
        ctx = self._ctx
        info = self._ctx.cls.info
        attributes = self.collect_attributes()
        if attributes is None:
            # Some definitions are not ready, defer() should be already called.
            return
        for attr in attributes:
            if attr.type is None:
                ctx.api.defer()
                return
        decorator_arguments = {
            'init': _get_decorator_bool_argument(self._ctx, 'init', True),
            'eq': _get_decorator_bool_argument(self._ctx, 'eq', True),
            'order': _get_decorator_bool_argument(self._ctx, 'order', False),
            'frozen': _get_decorator_bool_argument(self._ctx, 'frozen', False),
        }

        if info.get('replace') is None:
            obj_type = ctx.api.named_type('__builtins__.object')
            self_tvar_expr = TypeVarExpr(SELF_UVAR_NAME,
                                         info.fullname + '.' + SELF_UVAR_NAME,
                                         [], obj_type)
            info.names[SELF_UVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr)
            replace_tvar_def = TypeVarDef(SELF_UVAR_NAME,
                                          info.fullname + '.' + SELF_UVAR_NAME,
                                          -1, [], fill_typevars(info))
            replace_other_type = TypeVarType(replace_tvar_def)

            add_method(ctx,
                       'replace',
                       args=[
                           Argument(
                               Var('changes', AnyType(TypeOfAny.explicit)),
                               AnyType(TypeOfAny.explicit), None, ARG_STAR2)
                       ],
                       return_type=replace_other_type,
                       self_type=replace_other_type,
                       tvar_def=replace_tvar_def)

        # If there are no attributes, it may be that the semantic analyzer has not
        # processed them yet. In order to work around this, we can simply skip generating
        # __init__ if there are no attributes, because if the user truly did not define any,
        # then the object default __init__ with an empty signature will be present anyway.
        if (decorator_arguments['init']
                and ('__init__' not in info.names
                     or info.names['__init__'].plugin_generated)
                and attributes):
            add_method(
                ctx,
                '__init__',
                args=[
                    attr.to_argument() for attr in attributes
                    if attr.is_in_init
                ],
                return_type=NoneType(),
            )

        if (decorator_arguments['eq'] and info.get('__eq__') is None
                or decorator_arguments['order']):
            # Type variable for self types in generated methods.
            obj_type = ctx.api.named_type('__builtins__.object')
            self_tvar_expr = TypeVarExpr(SELF_TVAR_NAME,
                                         info.fullname + '.' + SELF_TVAR_NAME,
                                         [], obj_type)
            info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr)

        # Add <, >, <=, >=, but only if the class has an eq method.
        if decorator_arguments['order']:
            if not decorator_arguments['eq']:
                ctx.api.fail('eq must be True if order is True', ctx.cls)

            for method_name in ['__lt__', '__gt__', '__le__', '__ge__']:
                # Like for __eq__ and __ne__, we want "other" to match
                # the self type.
                obj_type = ctx.api.named_type('__builtins__.object')
                order_tvar_def = TypeVarDef(
                    SELF_TVAR_NAME, info.fullname + '.' + SELF_TVAR_NAME, -1,
                    [], obj_type)
                order_other_type = TypeVarType(order_tvar_def)
                order_return_type = ctx.api.named_type('__builtins__.bool')
                order_args = [
                    Argument(Var('other', order_other_type), order_other_type,
                             None, ARG_POS)
                ]

                existing_method = info.get(method_name)
                if existing_method is not None and not existing_method.plugin_generated:
                    assert existing_method.node
                    ctx.api.fail(
                        'You may not have a custom %s method when order=True' %
                        method_name,
                        existing_method.node,
                    )

                add_method(
                    ctx,
                    method_name,
                    args=order_args,
                    return_type=order_return_type,
                    self_type=order_other_type,
                    tvar_def=order_tvar_def,
                )

        if decorator_arguments['frozen']:
            self._freeze(attributes)

        self.reset_init_only_vars(info, attributes)

        info.metadata['dataclass'] = {
            'attributes': [attr.serialize() for attr in attributes],
            'frozen': decorator_arguments['frozen'],
        }
Beispiel #27
0
    def __init__(self, variance: int = COVARIANT) -> None:
        # The 'object' class
        self.oi = self.make_type_info('builtins.object')  # class object
        self.o = Instance(self.oi, [])  # object

        # Type variables (these are effectively global)

        def make_type_var(name: str, id: int, values: List[Type],
                          upper_bound: Type, variance: int) -> TypeVarType:
            return TypeVarType(name, name, id, values, upper_bound, variance)

        self.t = make_type_var('T', 1, [], self.o,
                               variance)  # T`1 (type variable)
        self.tf = make_type_var('T', -1, [], self.o,
                                variance)  # T`-1 (type variable)
        self.tf2 = make_type_var('T', -2, [], self.o,
                                 variance)  # T`-2 (type variable)
        self.s = make_type_var('S', 2, [], self.o,
                               variance)  # S`2 (type variable)
        self.s1 = make_type_var('S', 1, [], self.o,
                                variance)  # S`1 (type variable)
        self.sf = make_type_var('S', -2, [], self.o,
                                variance)  # S`-2 (type variable)
        self.sf1 = make_type_var('S', -1, [], self.o,
                                 variance)  # S`-1 (type variable)

        # Simple types
        self.anyt = AnyType(TypeOfAny.special_form)
        self.nonet = NoneType()
        self.uninhabited = UninhabitedType()

        # Abstract class TypeInfos

        # class F
        self.fi = self.make_type_info('F', is_abstract=True)

        # class F2
        self.f2i = self.make_type_info('F2', is_abstract=True)

        # class F3(F)
        self.f3i = self.make_type_info('F3', is_abstract=True, mro=[self.fi])

        # Class TypeInfos
        self.std_tuplei = self.make_type_info('builtins.tuple',
                                              mro=[self.oi],
                                              typevars=['T'],
                                              variances=[COVARIANT
                                                         ])  # class tuple
        self.type_typei = self.make_type_info('builtins.type')  # class type
        self.bool_type_info = self.make_type_info('builtins.bool')
        self.functioni = self.make_type_info(
            'builtins.function')  # function TODO
        self.ai = self.make_type_info('A', mro=[self.oi])  # class A
        self.bi = self.make_type_info('B', mro=[self.ai,
                                                self.oi])  # class B(A)
        self.ci = self.make_type_info('C', mro=[self.ai,
                                                self.oi])  # class C(A)
        self.di = self.make_type_info('D', mro=[self.oi])  # class D
        # class E(F)
        self.ei = self.make_type_info('E', mro=[self.fi, self.oi])
        # class E2(F2, F)
        self.e2i = self.make_type_info('E2', mro=[self.f2i, self.fi, self.oi])
        # class E3(F, F2)
        self.e3i = self.make_type_info('E3', mro=[self.fi, self.f2i, self.oi])

        # Generic class TypeInfos
        # G[T]
        self.gi = self.make_type_info('G',
                                      mro=[self.oi],
                                      typevars=['T'],
                                      variances=[variance])
        # G2[T]
        self.g2i = self.make_type_info('G2',
                                       mro=[self.oi],
                                       typevars=['T'],
                                       variances=[variance])
        # H[S, T]
        self.hi = self.make_type_info('H',
                                      mro=[self.oi],
                                      typevars=['S', 'T'],
                                      variances=[variance, variance])
        # GS[T, S] <: G[S]
        self.gsi = self.make_type_info('GS',
                                       mro=[self.gi, self.oi],
                                       typevars=['T', 'S'],
                                       variances=[variance, variance],
                                       bases=[Instance(self.gi, [self.s])])
        # GS2[S] <: G[S]
        self.gs2i = self.make_type_info('GS2',
                                        mro=[self.gi, self.oi],
                                        typevars=['S'],
                                        variances=[variance],
                                        bases=[Instance(self.gi, [self.s1])])
        # list[T]
        self.std_listi = self.make_type_info('builtins.list',
                                             mro=[self.oi],
                                             typevars=['T'],
                                             variances=[variance])

        # Instance types
        self.std_tuple = Instance(self.std_tuplei, [self.anyt])  # tuple
        self.type_type = Instance(self.type_typei, [])  # type
        self.function = Instance(self.functioni, [])  # function TODO
        self.a = Instance(self.ai, [])  # A
        self.b = Instance(self.bi, [])  # B
        self.c = Instance(self.ci, [])  # C
        self.d = Instance(self.di, [])  # D

        self.e = Instance(self.ei, [])  # E
        self.e2 = Instance(self.e2i, [])  # E2
        self.e3 = Instance(self.e3i, [])  # E3

        self.f = Instance(self.fi, [])  # F
        self.f2 = Instance(self.f2i, [])  # F2
        self.f3 = Instance(self.f3i, [])  # F3

        # Generic instance types
        self.ga = Instance(self.gi, [self.a])  # G[A]
        self.gb = Instance(self.gi, [self.b])  # G[B]
        self.gd = Instance(self.gi, [self.d])  # G[D]
        self.go = Instance(self.gi, [self.o])  # G[object]
        self.gt = Instance(self.gi, [self.t])  # G[T`1]
        self.gtf = Instance(self.gi, [self.tf])  # G[T`-1]
        self.gtf2 = Instance(self.gi, [self.tf2])  # G[T`-2]
        self.gs = Instance(self.gi, [self.s])  # G[S]
        self.gdyn = Instance(self.gi, [self.anyt])  # G[Any]
        self.gn = Instance(self.gi, [NoneType()])  # G[None]

        self.g2a = Instance(self.g2i, [self.a])  # G2[A]

        self.gsaa = Instance(self.gsi, [self.a, self.a])  # GS[A, A]
        self.gsab = Instance(self.gsi, [self.a, self.b])  # GS[A, B]
        self.gsba = Instance(self.gsi, [self.b, self.a])  # GS[B, A]

        self.gs2a = Instance(self.gs2i, [self.a])  # GS2[A]
        self.gs2b = Instance(self.gs2i, [self.b])  # GS2[B]
        self.gs2d = Instance(self.gs2i, [self.d])  # GS2[D]

        self.hab = Instance(self.hi, [self.a, self.b])  # H[A, B]
        self.haa = Instance(self.hi, [self.a, self.a])  # H[A, A]
        self.hbb = Instance(self.hi, [self.b, self.b])  # H[B, B]
        self.hts = Instance(self.hi, [self.t, self.s])  # H[T, S]
        self.had = Instance(self.hi, [self.a, self.d])  # H[A, D]
        self.hao = Instance(self.hi, [self.a, self.o])  # H[A, object]

        self.lsta = Instance(self.std_listi, [self.a])  # List[A]
        self.lstb = Instance(self.std_listi, [self.b])  # List[B]

        self.lit1 = LiteralType(1, self.a)
        self.lit2 = LiteralType(2, self.a)
        self.lit3 = LiteralType("foo", self.d)
        self.lit4 = LiteralType(4, self.a)
        self.lit1_inst = Instance(self.ai, [], last_known_value=self.lit1)
        self.lit2_inst = Instance(self.ai, [], last_known_value=self.lit2)
        self.lit3_inst = Instance(self.di, [], last_known_value=self.lit3)
        self.lit4_inst = Instance(self.ai, [], last_known_value=self.lit4)

        self.type_a = TypeType.make_normalized(self.a)
        self.type_b = TypeType.make_normalized(self.b)
        self.type_c = TypeType.make_normalized(self.c)
        self.type_d = TypeType.make_normalized(self.d)
        self.type_t = TypeType.make_normalized(self.t)
        self.type_any = TypeType.make_normalized(self.anyt)

        self._add_bool_dunder(self.bool_type_info)
        self._add_bool_dunder(self.ai)
Beispiel #28
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 #29
0
    def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument:
        """Return this attribute as an argument to __init__."""
        assert self.init

        init_type = self.init_type or self.info[self.name].type

        if self.converter.name:
            # When a converter is set the init_type is overridden by the first argument
            # of the converter method.
            converter = lookup_qualified_stnode(ctx.api.modules, self.converter.name, True)
            if not converter:
                # The converter may be a local variable. Check there too.
                converter = ctx.api.lookup_qualified(self.converter.name, self.info, True)

            # Get the type of the converter.
            converter_type = None  # type: Optional[Type]
            if converter and isinstance(converter.node, TypeInfo):
                from mypy.checkmember import type_object_type  # To avoid import cycle.
                converter_type = type_object_type(converter.node, ctx.api.builtin_type)
            elif converter and isinstance(converter.node, OverloadedFuncDef):
                converter_type = converter.node.type
            elif converter and converter.type:
                converter_type = converter.type

            init_type = None
            converter_type = get_proper_type(converter_type)
            if isinstance(converter_type, CallableType) and converter_type.arg_types:
                init_type = ctx.api.anal_type(converter_type.arg_types[0])
            elif isinstance(converter_type, Overloaded):
                types = []  # type: List[Type]
                for item in converter_type.items():
                    # Walk the overloads looking for methods that can accept one argument.
                    num_arg_types = len(item.arg_types)
                    if not num_arg_types:
                        continue
                    if num_arg_types > 1 and any(kind == ARG_POS for kind in item.arg_kinds[1:]):
                        continue
                    types.append(item.arg_types[0])
                # Make a union of all the valid types.
                if types:
                    args = make_simplified_union(types)
                    init_type = ctx.api.anal_type(args)

            if self.converter.is_attr_converters_optional and init_type:
                # If the converter was attr.converter.optional(type) then add None to
                # the allowed init_type.
                init_type = UnionType.make_union([init_type, NoneType()])

            if not init_type:
                ctx.api.fail("Cannot determine __init__ type from converter", self.context)
                init_type = AnyType(TypeOfAny.from_error)
        elif self.converter.name == '':
            # This means we had a converter but it's not of a type we can infer.
            # Error was shown in _get_converter_name
            init_type = AnyType(TypeOfAny.from_error)

        if init_type is None:
            if ctx.api.options.disallow_untyped_defs:
                # This is a compromise.  If you don't have a type here then the
                # __init__ will be untyped. But since the __init__ is added it's
                # pointing at the decorator. So instead we also show the error in the
                # assignment, which is where you would fix the issue.
                node = self.info[self.name].node
                assert node is not None
                ctx.api.msg.need_annotation_for_var(node, self.context)

            # Convert type not set to Any.
            init_type = AnyType(TypeOfAny.unannotated)

        if self.kw_only:
            arg_kind = ARG_NAMED_OPT if self.has_default else ARG_NAMED
        else:
            arg_kind = ARG_OPT if self.has_default else ARG_POS

        # Attrs removes leading underscores when creating the __init__ arguments.
        return Argument(Var(self.name.lstrip("_"), init_type), init_type,
                        None,
                        arg_kind)
Beispiel #30
0
    def assign_type(self, expr: Expression,
                    type: Type,
                    declared_type: Optional[Type],
                    restrict_any: bool = False) -> None:
        # We should erase last known value in binder, because if we are using it,
        # it means that the target is not final, and therefore can't hold a literal.
        type = remove_instance_last_known_values(type)

        type = get_proper_type(type)
        declared_type = get_proper_type(declared_type)

        if self.type_assignments is not None:
            # We are in a multiassign from union, defer the actual binding,
            # just collect the types.
            self.type_assignments[expr].append((type, declared_type))
            return
        if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)):
            return None
        if not literal(expr):
            return
        self.invalidate_dependencies(expr)

        if declared_type is None:
            # Not sure why this happens.  It seems to mainly happen in
            # member initialization.
            return
        if not is_subtype(type, declared_type):
            # Pretty sure this is only happens when there's a type error.

            # Ideally this function wouldn't be called if the
            # expression has a type error, though -- do other kinds of
            # errors cause this function to get called at invalid
            # times?
            return

        enclosing_type = get_proper_type(self.most_recent_enclosing_type(expr, type))
        if isinstance(enclosing_type, AnyType) and not restrict_any:
            # If x is Any and y is int, after x = y we do not infer that x is int.
            # This could be changed.
            # Instead, since we narrowed type from Any in a recent frame (probably an
            # isinstance check), but now it is reassigned, we broaden back
            # to Any (which is the most recent enclosing type)
            self.put(expr, enclosing_type)
        # As a special case, when assigning Any to a variable with a
        # declared Optional type that has been narrowed to None,
        # replace all the Nones in the declared Union type with Any.
        # This overrides the normal behavior of ignoring Any assignments to variables
        # in order to prevent false positives.
        # (See discussion in #3526)
        elif (isinstance(type, AnyType)
              and isinstance(declared_type, UnionType)
              and any(isinstance(item, NoneType) for item in declared_type.items)
              and isinstance(get_proper_type(self.most_recent_enclosing_type(expr, NoneType())),
                             NoneType)):
            # Replace any Nones in the union type with Any
            new_items = [type if isinstance(item, NoneType) else item
                         for item in declared_type.items]
            self.put(expr, UnionType(new_items))
        elif (isinstance(type, AnyType)
              and not (isinstance(declared_type, UnionType)
                       and any(isinstance(item, AnyType) for item in declared_type.items))):
            # Assigning an Any value doesn't affect the type to avoid false negatives, unless
            # there is an Any item in a declared union type.
            self.put(expr, declared_type)
        else:
            self.put(expr, type)

        for i in self.try_frames:
            # XXX This should probably not copy the entire frame, but
            # just copy this variable into a single stored frame.
            self.allow_jump(i)