コード例 #1
0
    def visit_callable_type(self, template: CallableType) -> List[Constraint]:
        if isinstance(self.actual, CallableType):
            res: List[Constraint] = []
            cactual = self.actual
            param_spec = template.param_spec()
            if param_spec is None:
                # FIX verify argument counts
                # FIX what if one of the functions is generic

                # We can't infer constraints from arguments if the template is Callable[..., T]
                # (with literal '...').
                if not template.is_ellipsis_args:
                    # The lengths should match, but don't crash (it will error elsewhere).
                    for t, a in zip(template.arg_types, cactual.arg_types):
                        # Negate direction due to function argument type contravariance.
                        res.extend(infer_constraints(t, a, neg_op(self.direction)))
            else:
                # TODO: Direction
                # TODO: Deal with arguments that come before param spec ones?
                res.append(Constraint(param_spec.id,
                                      SUBTYPE_OF,
                                      cactual.copy_modified(ret_type=NoneType())))

            template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type
            if template.type_guard is not None:
                template_ret_type = template.type_guard
            if cactual.type_guard is not None:
                cactual_ret_type = cactual.type_guard

            res.extend(infer_constraints(template_ret_type, cactual_ret_type,
                                         self.direction))
            return res
        elif isinstance(self.actual, AnyType):
            param_spec = template.param_spec()
            any_type = AnyType(TypeOfAny.from_another_any, source_any=self.actual)
            if param_spec is None:
                # FIX what if generic
                res = self.infer_against_any(template.arg_types, self.actual)
            else:
                res = [Constraint(param_spec.id,
                                  SUBTYPE_OF,
                                  callable_with_ellipsis(any_type, any_type, template.fallback))]
            res.extend(infer_constraints(template.ret_type, any_type, self.direction))
            return res
        elif isinstance(self.actual, Overloaded):
            return self.infer_against_overloaded(self.actual, template)
        elif isinstance(self.actual, TypeType):
            return infer_constraints(template.ret_type, self.actual.item, self.direction)
        elif isinstance(self.actual, Instance):
            # Instances with __call__ method defined are considered structural
            # subtypes of Callable with a compatible signature.
            call = mypy.subtypes.find_member('__call__', self.actual, self.actual,
                                             is_operator=True)
            if call:
                return infer_constraints(template, call, self.direction)
            else:
                return []
        else:
            return []
コード例 #2
0
    def visit_callable_type(self, t: CallableType) -> Type:
        param_spec = t.param_spec()
        if param_spec is not None:
            repl = get_proper_type(self.variables.get(param_spec.id))
            # If a ParamSpec in a callable type is substituted with a
            # callable type, we can't use normal substitution logic,
            # since ParamSpec is actually split into two components
            # *P.args and **P.kwargs in the original type. Instead, we
            # must expand both of them with all the argument types,
            # kinds and names in the replacement. The return type in
            # the replacement is ignored.
            if isinstance(repl, CallableType) or isinstance(repl, Parameters):
                # Substitute *args: P.args, **kwargs: P.kwargs
                prefix = param_spec.prefix
                # we need to expand the types in the prefix, so might as well
                # not get them in the first place
                t = t.expand_param_spec(repl, no_prefix=True)
                return t.copy_modified(
                    arg_types=self.expand_types(prefix.arg_types) +
                    t.arg_types,
                    arg_kinds=prefix.arg_kinds + t.arg_kinds,
                    arg_names=prefix.arg_names + t.arg_names,
                    ret_type=t.ret_type.accept(self),
                    type_guard=(t.type_guard.accept(self)
                                if t.type_guard is not None else None))

        return t.copy_modified(arg_types=self.expand_types(t.arg_types),
                               ret_type=t.ret_type.accept(self),
                               type_guard=(t.type_guard.accept(self) if
                                           t.type_guard is not None else None))
コード例 #3
0
def apply_generic_arguments(
        callable: CallableType, orig_types: Sequence[Optional[Type]],
        report_incompatible_typevar_value: Callable[[CallableType, Type, str, Context], None],
        context: Context,
        skip_unsatisfied: bool = False) -> CallableType:
    """Apply generic type arguments to a callable type.

    For example, applying [int] to 'def [T] (T) -> T' results in
    'def (int) -> int'.

    Note that each type can be None; in this case, it will not be applied.

    If `skip_unsatisfied` is True, then just skip the types that don't satisfy type variable
    bound or constraints, instead of giving an error.
    """
    tvars = callable.variables
    assert len(tvars) == len(orig_types)
    # Check that inferred type variable values are compatible with allowed
    # values and bounds.  Also, promote subtype values to allowed values.
    types = get_proper_types(orig_types)

    # Create a map from type variable id to target type.
    id_to_type: Dict[TypeVarId, Type] = {}

    for tvar, type in zip(tvars, types):
        assert not isinstance(type, PartialType), "Internal error: must never apply partial type"
        if type is None:
            continue

        target_type = get_target_type(
            tvar, type, callable, report_incompatible_typevar_value, context, skip_unsatisfied
        )
        if target_type is not None:
            id_to_type[tvar.id] = target_type

    param_spec = callable.param_spec()
    if param_spec is not None:
        nt = id_to_type.get(param_spec.id)
        if nt is not None:
            nt = get_proper_type(nt)
            if isinstance(nt, CallableType):
                callable = callable.expand_param_spec(nt)

    # Apply arguments to argument types.
    arg_types = [expand_type(at, id_to_type) for at in callable.arg_types]

    # The callable may retain some type vars if only some were applied.
    remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type]

    return callable.copy_modified(
        arg_types=arg_types,
        ret_type=expand_type(callable.ret_type, id_to_type),
        variables=remaining_tvars,
    )
コード例 #4
0
    def visit_callable_type(self, t: CallableType) -> Type:
        param_spec = t.param_spec()
        if param_spec is not None:
            repl = get_proper_type(self.variables.get(param_spec.id))
            # If a ParamSpec in a callable type is substituted with a
            # callable type, we can't use normal substitution logic,
            # since ParamSpec is actually split into two components
            # *P.args and **P.kwargs in the original type. Instead, we
            # must expand both of them with all the argument types,
            # kinds and names in the replacement. The return type in
            # the replacement is ignored.
            if isinstance(repl, CallableType):
                # Substitute *args: P.args, **kwargs: P.kwargs
                t = t.expand_param_spec(repl)
                # TODO: Substitute remaining arg types
                return t.copy_modified(
                    ret_type=t.ret_type.accept(self),
                    type_guard=(t.type_guard.accept(self)
                                if t.type_guard is not None else None))

        return t.copy_modified(arg_types=self.expand_types(t.arg_types),
                               ret_type=t.ret_type.accept(self),
                               type_guard=(t.type_guard.accept(self) if
                                           t.type_guard is not None else None))