Esempio n. 1
0
    def value_to_pytd_type(self, node, v, seen, view):
        """Get a PyTD type representing this object, as seen at a node.

    Args:
      node: The node from which we want to observe this object.
      v: The object.
      seen: The set of values seen before while computing the type.
      view: A Variable -> binding map.

    Returns:
      A PyTD type.
    """
        if isinstance(v, (abstract.Empty, abstract.Nothing)):
            return pytd.NothingType()
        elif isinstance(v, abstract.TypeParameterInstance):
            if v.instance.type_parameters[v.name].bindings:
                # The type parameter was initialized.
                return pytd_utils.JoinTypes(
                    self.value_to_pytd_type(node, p, seen, view)
                    for p in v.instance.type_parameters[v.name].data)
            elif v.param.constraints:
                return pytd_utils.JoinTypes(
                    self.value_instance_to_pytd_type(node, p, None, seen, view)
                    for p in v.param.constraints)
            else:
                return pytd.AnythingType()
        elif isinstance(v, typing.TypeVar):
            return pytd.NamedType("__builtin__.type")
        elif isinstance(
                v,
            (abstract.InterpreterFunction, abstract.BoundInterpreterFunction)):
            sig, = abstract.get_signatures(v)
            return self.value_instance_to_pytd_type(
                node, self.signature_to_callable(sig, v.vm), None, seen, view)
        elif isinstance(v,
                        (abstract.PyTDFunction, abstract.BoundPyTDFunction)):
            signatures = abstract.get_signatures(v)
            if len(signatures) == 1:
                val = self.signature_to_callable(signatures[0], v.vm)
                if not v.vm.annotations_util.get_type_parameters(val):
                    # This is a workaround to make sure we don't put unexpected type
                    # parameters in call traces.
                    return self.value_instance_to_pytd_type(
                        node, val, None, seen, view)
            return pytd.NamedType("typing.Callable")
        elif isinstance(
                v,
            (special_builtins.IsInstance, abstract.ClassMethod,
             abstract.StaticMethod, special_builtins.ClassMethodCallable)):
            return pytd.NamedType("typing.Callable")
        elif isinstance(v, abstract.Class):
            param = self.value_instance_to_pytd_type(node, v, None, seen, view)
            return pytd.GenericType(
                base_type=pytd.NamedType("__builtin__.type"),
                parameters=(param, ))
        elif isinstance(v, abstract.Module):
            return pytd.NamedType("__builtin__.module")
        elif isinstance(v, abstract.SimpleAbstractValue):
            if v.cls:
                classvalues = self._get_values(node, v.cls, view)
                cls_types = []
                for cls in classvalues:
                    cls_types.append(
                        self.value_instance_to_pytd_type(node,
                                                         cls,
                                                         v,
                                                         seen=seen,
                                                         view=view))
                ret = pytd_utils.JoinTypes(cls_types)
                ret.Visit(
                    visitors.FillInLocalPointers(
                        {"__builtin__": v.vm.loader.builtins}))
                return ret
            else:
                # We don't know this type's __class__, so return AnythingType to
                # indicate that we don't know anything about what this is.
                # This happens e.g. for locals / globals, which are returned from the
                # code in class declarations.
                log.info("Using ? for %s", v.name)
                return pytd.AnythingType()
        elif isinstance(v, abstract.Union):
            return pytd.UnionType(
                tuple(
                    self.value_to_pytd_type(node, o, seen, view)
                    for o in v.options))
        elif isinstance(v, special_builtins.SuperInstance):
            return pytd.NamedType("__builtin__.super")
        elif isinstance(v, (abstract.Unsolvable, abstract.TypeParameter)):
            # Arguably, the type of a type parameter is NamedType("typing.TypeVar"),
            # but pytype doesn't know how to handle that, so let's just go with Any.
            return pytd.AnythingType()
        elif isinstance(v, abstract.Unknown):
            return pytd.NamedType(v.class_name)
        else:
            raise NotImplementedError(v.__class__.__name__)
Esempio n. 2
0
    def _match_type_against_type(self, left, other_type, subst, node, view):
        """Checks whether a type is compatible with a (formal) type.

    Args:
      left: A type.
      other_type: A formal type. E.g. abstract.Class or abstract.Union.
      subst: The current type parameter assignment.
      node: The current CFG node.
      view: The current mapping of Variable to Value.
    Returns:
      A new type parameter assignment if the matching succeeded, None otherwise.
    """
        if (isinstance(left, abstract.Empty)
                and isinstance(other_type, abstract.Empty)):
            return subst
        elif isinstance(left, abstract.AMBIGUOUS_OR_EMPTY):
            params = self.vm.annotations_util.get_type_parameters(other_type)
            if isinstance(left, abstract.Empty):
                value = self.vm.convert.empty
            else:
                value = self.vm.convert.unsolvable
            return self._mutate_type_parameters(params, value, subst, node)
        elif isinstance(left, abstract.Class):
            if (other_type.full_name == "__builtin__.type"
                    and isinstance(other_type, abstract.ParameterizedClass)):
                other_type = other_type.type_parameters[abstract.T]
                return self._instantiate_and_match(left, other_type, subst,
                                                   node, view)
            elif (other_type.full_name == "typing.Callable"
                  and isinstance(other_type, abstract.ParameterizedClass)):
                other_type = other_type.type_parameters[abstract.RET]
                return self._instantiate_and_match(left, other_type, subst,
                                                   node, view)
            elif other_type.full_name in [
                    "__builtin__.type", "__builtin__.object", "typing.Callable"
            ]:
                return subst
            elif left.cls:
                return self._match_instance_against_type(
                    left, other_type, subst, node, view)
        elif isinstance(left, abstract.Module):
            if other_type.full_name in [
                    "__builtin__.module", "__builtin__.object",
                    "types.ModuleType"
            ]:
                return subst
        elif isinstance(left, (abstract.Function, abstract.BoundFunction)):
            if other_type.full_name == "typing.Callable":
                if not isinstance(other_type, abstract.ParameterizedClass):
                    # The callable has no parameters, so any function matches it.
                    return subst
                if isinstance(left, abstract.NativeFunction):
                    # If we could get the class on which 'left' is defined (perhaps by
                    # using bound_class?), we could get the argument and return types
                    # from the underlying PyTDFunction, but we wouldn't get much value
                    # out of that additional matching, since most NativeFunction objects
                    # are magic methods like __getitem__ which aren't likely to be passed
                    # as function arguments.
                    return subst
                signatures = abstract.get_signatures(left)
                for sig in signatures:
                    new_subst = self._match_signature_against_callable(
                        sig, other_type, subst, node, view)
                    if new_subst is not None:
                        return new_subst
                return None
        elif isinstance(left, abstract.SimpleAbstractValue):
            return self._match_instance_against_type(left, other_type, subst,
                                                     node, view)
        elif isinstance(left, special_builtins.SuperInstance):
            return self._match_class_and_instance_against_type(
                left.super_cls, left.super_obj, other_type, subst, node, view)
        elif isinstance(left, abstract.ClassMethod):
            if other_type.full_name in [
                    "__builtin__.classmethod", "__builtin__.object"
            ]:
                return subst
        elif isinstance(left, abstract.StaticMethod):
            if other_type.full_name in [
                    "__builtin__.staticmethod", "__builtin__.object"
            ]:
                return subst
        elif isinstance(left, abstract.Union):
            for o in left.options:
                new_subst = self._match_type_against_type(
                    o, other_type, subst, node, view)
                if new_subst is not None:
                    return new_subst
        elif isinstance(left, abstract.TypeParameterInstance):
            return self._instantiate_and_match(left.param, other_type, subst,
                                               node, view)
        else:
            raise NotImplementedError(
                "Matching not implemented for %s against %s" %
                (type(left), type(other_type)))