コード例 #1
0
    def make_class(self, node, f_locals):
        # If BuildClass.call() hits max depth, f_locals will be [unsolvable]
        # Since we don't support defining NamedTuple subclasses in a nested scope
        # anyway, we can just return unsolvable here to prevent a crash, and let the
        # invalid namedtuple error get raised later.
        if f_locals.data[0].isinstance_Unsolvable():
            return node, self.vm.new_unsolvable(node)

        f_locals = abstract_utils.get_atomic_python_constant(f_locals)

        # retrieve __qualname__ to get the name of class
        name = f_locals["__qualname__"]

        # assemble the arguments that are compatible with NamedTupleFuncBuilder.call
        field_list = []
        defaults = []
        cls_locals = classgen.get_class_locals(
            abstract_utils.get_atomic_python_constant(name),
            allow_methods=True,
            ordering=classgen.Ordering.FIRST_ANNOTATE,
            vm=self.vm)
        for k, local in cls_locals.items():
            assert local.typ
            if k in f_locals:
                defaults.append(f_locals[k])
            k = self.vm.convert.constant_to_var(k, node=node)
            field_list.append(self.vm.convert.build_tuple(
                node, (k, local.typ)))
        anno = self.vm.convert.build_list(node, field_list)
        posargs = (name, anno)
        args = function.Args(posargs=posargs)
        node, cls_var = self.namedtuple.call(node, None, args)
        cls_val = abstract_utils.get_atomic_value(cls_var)

        if not isinstance(cls_val, abstract.Unsolvable):
            # set __new__.__defaults__
            defaults = abstract.Tuple(tuple(defaults),
                                      self.vm).to_variable(node)
            node, new_attr = self.vm.attribute_handler.get_attribute(
                node, cls_val, "__new__")
            new_attr = abstract_utils.get_atomic_value(new_attr)
            node = self.vm.attribute_handler.set_attribute(
                node, new_attr, "__defaults__", defaults)

            # set the attribute without overriding special namedtuple attributes
            node, fields = self.vm.attribute_handler.get_attribute(
                node, cls_val, "_fields")
            fields = abstract_utils.get_atomic_python_constant(fields, tuple)
            fields = [
                abstract_utils.get_atomic_python_constant(field, str)
                for field in fields
            ]
            for key in f_locals:
                if key in self._prohibited:
                    self.vm.errorlog.not_writable(self.vm.frames, cls_val, key)
                if key not in self._special and key not in fields:
                    node = self.vm.attribute_handler.set_attribute(
                        node, cls_val, key, f_locals[key])

        return node, cls_var
コード例 #2
0
 def _getargs(self, node, args):
     self.match_args(node, args)
     sig, = self.signatures
     callargs = {
         name: var
         for name, var, _ in sig.signature.iter_args(args)
     }
     # typing.NamedTuple doesn't support rename or verbose
     name_var = callargs["typename"]
     fields_var = callargs["fields"]
     fields = abstract_utils.get_atomic_python_constant(fields_var)
     # The fields is a list of tuples, so we need to deeply unwrap them.
     fields = [abstract_utils.get_atomic_python_constant(t) for t in fields]
     # We need the actual string for the field names and the AtomicAbstractValue
     # for the field types.
     names = []
     types = []
     for field in fields:
         if (len(field) != 2 or any(not self._is_str_instance(v)
                                    for v in field[0].data)):
             # Note that we don't need to check field[1] because both 'str'
             # (forward reference) and 'type' are valid for it.
             sig, = self.signatures
             bad_param = function.BadParam(name="fields",
                                           expected=self._fields_type)
             raise function.WrongArgTypes(sig.signature, args, self.vm,
                                          bad_param)
         name, typ = field
         names.append(abstract_utils.get_atomic_python_constant(name))
         types.append(abstract_utils.get_atomic_value(typ))
     return name_var, names, types
コード例 #3
0
  def _getargs(self, node, args):
    """Extracts the typename, field_names and rename arguments.

    collections.namedtuple takes potentially 4 arguments, but we only care about
    three of them. This function checks the argument count and ensures multiple
    values aren't passed for 'verbose' and 'rename'.

    Args:
      node: The current CFG node. Used by match_args.
      args: A function.Args object

    Returns:
      A tuple containing the typename, field_names and rename arguments passed
      to this call to collections.namedtuple.

    Raises:
      function.FailedFunctionCall: The arguments do not match those needed by
        the function call. See also: abstract.PyTDFunction.match_args().
      abstract_utils.ConversionError: One of the args could not be extracted.
        Typically occurs if typename or one of the field names is in unicode.
    """

    # abstract.PyTDFunction.match_args checks the args for this call.
    self.match_args(node, args)

    # namedtuple only has one signature
    sig, = self.signatures
    callargs = {name: var for name, var, _ in sig.signature.iter_args(args)}

    # The name of the namedtuple class is the first arg (a Variable)
    # We need the actual Variable later, so we'll just return name_var and
    # extract the name itself later.
    name_var = callargs["typename"]

    # The fields are also a Variable, which stores the field names as Variables.
    # Extract the list itself, we don't need the wrapper.
    fields_var = callargs["field_names"]
    fields = abstract_utils.get_atomic_python_constant(fields_var)
    # namedtuple fields can be given as a single string, e.g. "a, b, c" or as a
    # list [Variable('a'), Variable('b'), Variable('c')].
    # We just want a list of strings.
    if isinstance(fields, (bytes, six.text_type)):
      fields = compat.native_str(fields)
      field_names = fields.replace(",", " ").split()
    else:
      field_names = [abstract_utils.get_atomic_python_constant(f)
                     for f in fields]
      field_names = [compat.native_str(f) for f in field_names]

    # namedtuple also takes a "verbose" argument, but we don't care about that.

    # rename will take any problematic field names and give them a new name.
    # Like the other args, it's stored as a Variable, but we want just a bool.
    if callargs.get("rename", None):
      rename = abstract_utils.get_atomic_python_constant(callargs["rename"])
    else:
      rename = False

    return name_var, field_names, rename
コード例 #4
0
ファイル: typing_overlay.py プロジェクト: lavenzg/pytype
    def make_class(self, node, f_locals):
        f_locals = abstract_utils.get_atomic_python_constant(f_locals)

        # retrieve __qualname__ to get the name of class
        name = f_locals["__qualname__"]
        # retrieve __annotations__ to get the dict
        # with key-value pair of (variable, type)
        anno = f_locals.get("__annotations__", {})
        if anno:
            anno = abstract_utils.get_atomic_value(anno)

        # assemble the arguments that are compatible with NamedTupleFuncBuilder.call
        field_list = []
        defaults = []
        for k, v in anno.items():
            if k in f_locals:
                defaults.append(f_locals.get(k))
                # TODO(ahxun): check if the value matches the declared type
            k = self.vm.convert.constant_to_var(k, node=node)
            field_list.append(self.vm.convert.build_tuple(node, (k, v)))
        anno = self.vm.convert.build_list(node, field_list)
        posargs = (name, anno)
        args = function.Args(posargs=posargs)
        node, cls_var = self.namedtuple.call(node, None, args)
        cls_val = abstract_utils.get_atomic_value(cls_var)

        if not isinstance(cls_val, abstract.Unsolvable):
            # set __new__.__defaults__
            defaults = abstract.Tuple(tuple(defaults),
                                      self.vm).to_variable(node)
            node, new_attr = self.vm.attribute_handler.get_attribute(
                node, cls_val, "__new__")
            new_attr = abstract_utils.get_atomic_value(new_attr)
            node = self.vm.attribute_handler.set_attribute(
                node, new_attr, "__defaults__", defaults)

            # set the attribute without overriding special namedtuple attributes
            node, fields = self.vm.attribute_handler.get_attribute(
                node, cls_val, "_fields")
            fields = abstract_utils.get_atomic_python_constant(fields, tuple)
            fields = [
                abstract_utils.get_atomic_python_constant(field, str)
                for field in fields
            ]
            for key in f_locals:
                if key in self._prohibited:
                    self.vm.errorlog.not_writable(self.vm.frames, cls_val, key)
                if key not in self._special and key not in fields:
                    node = self.vm.attribute_handler.set_attribute(
                        node, cls_val, key, f_locals[key])

        return node, cls_var
コード例 #5
0
 def convert_function_annotations(self, node, raw_annotations):
   """Convert raw annotations to a {name: annotation} dict."""
   if raw_annotations:
     # {"i": int, "return": str} is stored as (int, str, ("i", "return"))
     names = abstract_utils.get_atomic_python_constant(raw_annotations[-1])
     type_list = raw_annotations[:-1]
     annotations_list = []
     for name, t in zip(names, type_list):
       name = abstract_utils.get_atomic_python_constant(name)
       t = self.convert_function_type_annotation(name, t)
       annotations_list.append((name, t))
     return self.convert_annotations_list(node, annotations_list)
   else:
     return {}
コード例 #6
0
    def assert_type(self, stack, node, var, typ=None):
        """Check that a variable type matches its expected value."""

        types = [
            self._print_as_actual_type(b.data) for b in
            abstract_utils.expand_type_parameter_instances(var.bindings)
            if node.HasCombination([b])
        ]
        actual = self._join_printed_types(types)

        # assert_type(x) checks that x is not Any
        if typ is None:
            if types == ["Any"] or types == ["typing.Any"]:
                self.error(stack, f"Asserted type was {actual}")
            return

        try:
            expected = abstract_utils.get_atomic_python_constant(typ, str)
        except abstract_utils.ConversionError:
            # NOTE: Converting types to strings is provided as a fallback, but is not
            # really supported, since there are issues around name resolution.
            vm = typ.data[0].vm
            typ = vm.annotations_util.extract_annotation(
                node, typ, "assert_type", vm.simple_stack())
            node, typ = vm.init_class(node, typ)
            wanted = [
                self._print_as_actual_type(b.data) for b in
                abstract_utils.expand_type_parameter_instances(typ.bindings)
                if node.HasCombination([b])
            ]
            expected = self._join_printed_types(wanted)
        if actual != expected:
            details = f"Expected: {expected}\n  Actual: {actual}"
            self.error(stack, actual, details=details)
コード例 #7
0
ファイル: typing_overlay.py プロジェクト: lavenzg/pytype
    def call(self, node, _, args):
        try:
            name_var, field_names, field_types = self._getargs(node, args)
        except abstract_utils.ConversionError:
            return node, self.vm.new_unsolvable(node)

        try:
            name = abstract_utils.get_atomic_python_constant(name_var)
        except abstract_utils.ConversionError:
            return node, self.vm.new_unsolvable(node)

        try:
            field_names = self._validate_and_rename_args(
                name, field_names, False)
        except ValueError as e:
            self.vm.errorlog.invalid_namedtuple_arg(self.vm.frames,
                                                    utils.message(e))
            return node, self.vm.new_unsolvable(node)

        annots, late_annots = self.vm.annotations_util.convert_annotations_list(
            moves.zip(field_names, field_types))
        field_types = [
            annots.get(field_name, self.vm.convert.unsolvable)
            for field_name in field_names
        ]
        node, cls_var = self._build_namedtuple(name, field_names, field_types,
                                               late_annots, node)

        self.vm.trace_classdef(cls_var)
        return node, cls_var
コード例 #8
0
ファイル: special_builtins.py プロジェクト: ukulili/pytype
 def call(self, node, func, args):
     if len(args.posargs) == 4:
         self.match_args(node, args)  # May raise FailedFunctionCall.
         cls, name_var, bases_var, class_dict_var = args.posargs
         try:
             bases = list(
                 abstract_utils.get_atomic_python_constant(bases_var))
             if not bases:
                 bases = [
                     self.vm.convert.object_type.to_variable(
                         self.vm.root_cfg_node)
                 ]
             node, variable = self.vm.make_class(node, name_var, bases,
                                                 class_dict_var, cls)
         except abstract_utils.ConversionError:
             pass
         else:
             return node, variable
     elif (args.posargs and self.vm.callself_stack
           and args.posargs[-1].data == self.vm.callself_stack[-1].data):
         # We're calling type(self) in an __init__ method. A common pattern for
         # making a class non-instantiable is:
         #   class Foo:
         #     def __init__(self):
         #       if type(self) is Foo:
         #         raise ...
         # If we were to return 'Foo', pytype would think that this constructor
         # can never return. The correct return type is something like
         # TypeVar(bound=Foo), but we can't introduce a type parameter that isn't
         # bound to a class or function, so we'll go with Any.
         self.match_args(node, args)  # May raise FailedFunctionCall.
         return node, self.vm.new_unsolvable(node)
     return super(TypeNew, self).call(node, func, args)
コード例 #9
0
ファイル: typing_overlay.py プロジェクト: Anthchirp/pytype
 def _get_constant(self, var, name, arg_type, arg_type_desc=None):
   try:
     ret = abstract_utils.get_atomic_python_constant(var, arg_type)
   except abstract_utils.ConversionError:
     raise TypeVarError("%s must be %s" % (
         name, arg_type_desc or "a constant " + arg_type.__name__))
   return ret
コード例 #10
0
    def call(self, node, _, args):
        try:
            name_var, field_names, field_types = self._getargs(node, args)
        except abstract_utils.ConversionError:
            return node, self.vm.convert.unsolvable.to_variable(node)

        try:
            name = abstract_utils.get_atomic_python_constant(name_var)
        except abstract_utils.ConversionError:
            return node, self.vm.convert.unsolvable.to_variable(node)

        try:
            field_names = self._validate_and_rename_args(
                name, field_names, False)
        except ValueError as e:
            self.vm.errorlog.invalid_namedtuple_arg(self.vm.frames,
                                                    utils.message(e))
            return node, self.vm.convert.unsolvable.to_variable(node)

        annots, late_annots = self.vm.annotations_util.convert_annotations_list(
            moves.zip(field_names, field_types))
        if late_annots:
            # We currently don't support forward references. Report if we find any,
            # then continue by using Unsolvable instead.
            self.vm.errorlog.not_supported_yet(
                self.vm.frames, "Forward references in typing.NamedTuple")
        field_types = [
            annots.get(field_name, self.vm.convert.unsolvable)
            for field_name in field_names
        ]
        cls_var = self._build_namedtuple(name, field_names, field_types, node)
        self.vm.trace_classdef(cls_var)
        return node, cls_var
コード例 #11
0
 def starstarargs_as_dict(self):
     try:
         args = self.starstarargs and abstract_utils.get_atomic_python_constant(
             self.starstarargs, dict)
     except abstract_utils.ConversionError:
         args = None
     return args
コード例 #12
0
ファイル: typing_overlay.py プロジェクト: lavenzg/pytype
 def call(self, node, func, args):
     args = args.simplify(node)
     self.match_args(node, args, match_all_views=True)
     # As long as the types match we do not really care about the actual
     # class name. But, if we have a string literal value as the name arg,
     # we will use it.
     name_arg = args.namedargs.get(self._name_arg_name) or args.posargs[0]
     try:
         _ = abstract_utils.get_atomic_python_constant(name_arg, str)
     except abstract_utils.ConversionError:
         name_arg = self.vm.convert.constant_to_var(
             "_NewType_Internal_Class_Name_%d_" %
             self.internal_name_counter)
     type_arg = args.namedargs.get(self._type_arg_name) or args.posargs[1]
     try:
         type_value = abstract_utils.get_atomic_value(type_arg)
     except abstract_utils.ConversionError:
         # We need the type arg to be an atomic value. If not, we just
         # silently return unsolvable.
         return node, self.vm.new_unsolvable(node)
     value_arg_name = "val"
     constructor = overlay_utils.make_method(
         self.vm,
         node,
         name="__init__",
         params=[Param(value_arg_name, type_value)])
     members = abstract.Dict(self.vm)
     members.set_str_item(node, "__init__", constructor)
     return self.vm.make_class(node, name_arg, (type_arg, ),
                               members.to_variable(node), None)
コード例 #13
0
 def build_slice(self, node, start, stop, step=None):
     const_types = (int, type(None))
     try:
         if start:
             start = abstract_utils.get_atomic_python_constant(
                 start, const_types)
         if stop:
             stop = abstract_utils.get_atomic_python_constant(
                 stop, const_types)
         if step:
             step = abstract_utils.get_atomic_python_constant(
                 step, const_types)
     except abstract_utils.ConversionError:
         return self.primitive_class_instances[slice].to_variable(node)
     return abstract.AbstractOrConcreteValue(slice(start, stop, step),
                                             self.primitive_classes[slice],
                                             self.vm).to_variable(node)
コード例 #14
0
 def _update_kwargs(self, args):
     for k, v in args.namedargs.items():
         if k in self.args:
             try:
                 self.args[k] = abstract_utils.get_atomic_python_constant(v)
             except abstract_utils.ConversionError:
                 self.vm.errorlog.not_supported_yet(
                     self.vm.frames, "Non-constant attr.s argument %r" % k)
コード例 #15
0
ファイル: classgen.py プロジェクト: zenefits/pytype
 def get_kwarg(self, args, name, default):
   if name not in args.namedargs:
     return default
   try:
     return abstract_utils.get_atomic_python_constant(args.namedargs[name])
   except abstract_utils.ConversionError:
     self.vm.errorlog.not_supported_yet(
         self.vm.frames, "Non-constant argument %r" % name)
コード例 #16
0
 def init_from_annotations(self, node, name, annots_var):
   """Instantiate `name` from the given annotations dict, calling __init__."""
   try:
     annots = abstract_utils.get_atomic_python_constant(annots_var, dict)
   except abstract_utils.ConversionError:
     return None
   if name not in annots:
     return None
   return self.init_annotation_var(node, name, annots[name])
コード例 #17
0
ファイル: classgen.py プロジェクト: zenefits/pytype
 def update_kwargs(self, args):
   self._current_args = Decorator._DEFAULT_ARGS.copy()
   for k, v in args.namedargs.items():
     if k in self._current_args:
       try:
         self._current_args[k] = abstract_utils.get_atomic_python_constant(v)
       except abstract_utils.ConversionError:
         self.vm.errorlog.not_supported_yet(
             self.vm.frames, "Non-constant argument to decorator: %r" % k)
コード例 #18
0
 def get_annotations_dict(self, members):
   """Get __annotations__ from a members map."""
   if "__annotations__" not in members:
     return {}
   annots_var = members["__annotations__"]
   try:
     annots = abstract_utils.get_atomic_python_constant(annots_var, dict)
   except abstract_utils.ConversionError:
     return {}
   return annots
コード例 #19
0
ファイル: function.py プロジェクト: srittau/pytype
 def starargs_as_tuple(self, node, vm):
   try:
     args = self.starargs and abstract_utils.get_atomic_python_constant(
         self.starargs, tuple)
   except abstract_utils.ConversionError:
     args = None
   if not args:
     return args
   return tuple(var if var.bindings else vm.convert.empty.to_variable(node)
                for var in args)
コード例 #20
0
def _get_constant_tuple_prefix(value: abstract.Tuple):
    """Given a tuple, get its longest prefix of constant elements."""
    elements = []
    for element_var in value.pyval:
        try:
            element = abstract_utils.get_atomic_python_constant(
                element_var, tuple(value.vm.convert.primitive_classes))
        except abstract_utils.ConversionError:
            return tuple(elements)
        elements.append(element)
    return tuple(elements)
コード例 #21
0
 def _get_annotation(self, var, name):
     try:
         ret = abstract_utils.get_atomic_value(var, self._CLASS_TYPE)
         if isinstance(ret, abstract.AbstractOrConcreteValue):
             ret = abstract_utils.get_atomic_python_constant(
                 var, six.string_types)
     except abstract_utils.ConversionError:
         raise TypeVarError("%s must be constant" % name)
     if not ret:
         raise TypeVarError("%s cannot be an empty string" % name)
     return ret
コード例 #22
0
ファイル: typing_overlay.py プロジェクト: lavenzg/pytype
 def _getargs(self, node, args):
     self.match_args(node, args)
     sig, = self.signatures
     callargs = {
         name: var
         for name, var, _ in sig.signature.iter_args(args)
     }
     # typing.NamedTuple doesn't support rename or verbose
     name_var = callargs["typename"]
     fields_var = callargs["fields"]
     fields = abstract_utils.get_atomic_python_constant(fields_var)
     if isinstance(fields, six.string_types):
         # Since str matches Sequence, we have to manually check for it.
         raise function.WrongArgTypes(sig.signature, args, self.vm,
                                      self._fields_param)
     # The fields is a list of tuples, so we need to deeply unwrap them.
     fields = [abstract_utils.get_atomic_python_constant(t) for t in fields]
     # We need the actual string for the field names and the AtomicAbstractValue
     # for the field types.
     names = []
     types = []
     for field in fields:
         if isinstance(field, six.string_types):
             # Since str matches Sequence, we have to manually check for it.
             raise function.WrongArgTypes(sig.signature, args, self.vm,
                                          self._fields_param)
         if (len(field) != 2 or any(not self._is_str_instance(v)
                                    for v in field[0].data)):
             # Note that we don't need to check field[1] because both 'str'
             # (forward reference) and 'type' are valid for it.
             raise function.WrongArgTypes(sig.signature, args, self.vm,
                                          self._fields_param)
         name, typ = field
         name_py_constant = abstract_utils.get_atomic_python_constant(name)
         if name_py_constant.__class__ is compat.UnicodeType:
             # Unicode values should be ASCII.
             name_py_constant = compat.native_str(
                 name_py_constant.encode("ascii"))
         names.append(name_py_constant)
         types.append(abstract_utils.get_atomic_value(typ))
     return name_var, names, types
コード例 #23
0
 def call(self, node, func, args):
     if len(args.posargs) == 4:
         self.match_args(node, args)  # May raise FailedFunctionCall.
         cls, name_var, bases_var, class_dict_var = args.posargs
         try:
             bases = list(
                 abstract_utils.get_atomic_python_constant(bases_var))
             if not bases:
                 bases = [
                     self.vm.convert.object_type.to_variable(
                         self.vm.root_node)
                 ]
             node, variable = self.vm.make_class(node, name_var, bases,
                                                 class_dict_var, cls)
         except abstract_utils.ConversionError:
             pass
         else:
             return node, variable
     elif (args.posargs and self.vm.callself_stack
           and args.posargs[-1].data == self.vm.callself_stack[-1].data):
         # We're calling type(self) in an __init__ method. A common pattern for
         # making a class non-instantiable is:
         #   class Foo:
         #     def __init__(self):
         #       if type(self) is Foo:
         #         raise ...
         # If we were to return 'Foo', pytype would think that this constructor
         # can never return. The correct return type is something like
         # TypeVar(bound=Foo), but we can't introduce a type parameter that isn't
         # bound to a class or function, so we'll go with Any.
         self.match_args(node, args)  # May raise FailedFunctionCall.
         return node, self.vm.new_unsolvable(node)
     elif args.posargs and all(v.full_name == "typing.Protocol"
                               for v in args.posargs[-1].data):
         # type(Protocol) is a _ProtocolMeta class that inherits from abc.ABCMeta.
         # Changing the definition of Protocol in typing.pytd to include this
         # metaclass causes a bunch of weird breakages, so we instead return the
         # metaclass when type() or __class__ is accessed on Protocol. For
         # simplicity, we pretend the metaclass is ABCMeta rather than a subclass.
         self.match_args(node, args)  # May raise FailedFunctionCall.
         abc = self.vm.import_module("abc", "abc", 0).get_module("ABCMeta")
         abc.load_lazy_attribute("ABCMeta")
         return node, abc.members["ABCMeta"].AssignToNewVariable(node)
     node, raw_ret = super().call(node, func, args)
     # Removes TypeVars from the return value.
     # See test_typevar.TypeVarTest.test_type_of_typevar(_error).
     ret = self.vm.program.NewVariable()
     for b in raw_ret.bindings:
         value = self.vm.annotations_util.deformalize(b.data)
         ret.AddBinding(value, {b}, node)
     return node, ret
コード例 #24
0
 def call(self, node, func, args):
   if self.vm.PY3:
     # In Python 3, the type of IO object returned depends on the mode.
     self.match_args(node, args)  # May raise FailedFunctionCall.
     sig, = self.signatures
     callargs = {name: var for name, var, _ in sig.signature.iter_args(args)}
     try:
       if "mode" not in callargs:
         io_type = "Text"  # The default mode is 'r'.
       else:
         mode = abstract_utils.get_atomic_python_constant(callargs["mode"])
         io_type = "Binary" if "b" in mode else "Text"
     except abstract_utils.ConversionError:
       pass
     else:
       return node, self.vm.convert.constant_to_var(abstract_utils.AsInstance(
           self.vm.lookup_builtin("typing.%sIO" % io_type)), {}, node)
   return super(Open, self).call(node, func, args)
コード例 #25
0
ファイル: typing_overlay.py プロジェクト: lavenzg/pytype
 def _get_class_or_constant(self, var, name, arg_type, arg_type_desc=None):
     if arg_type is self._CLASS_TYPE:
         convert_func = abstract_utils.get_atomic_value
         type_desc = arg_type_desc or "an unambiguous type"
     else:
         convert_func = abstract_utils.get_atomic_python_constant
         type_desc = arg_type_desc or "a constant " + arg_type.__name__
     try:
         ret = convert_func(var, arg_type)
         # If we have a class type as an AbstractOrConcreteValue, we want to return
         # it as a string.
         if isinstance(ret, abstract.AbstractOrConcreteValue):
             ret = abstract_utils.get_atomic_python_constant(var, str)
             if not ret:
                 raise TypeVarError("%s cannot be an empty string" % name)
         return ret
     except abstract_utils.ConversionError:
         raise TypeVarError("%s must be %s" % (name, type_desc))
コード例 #26
0
ファイル: enum_overlay.py プロジェクト: njues/pytype
 def call(self, node, _, args, alias_map=None):
     _, argmap = self.match_and_map_args(node, args, alias_map)
     cls_var = argmap["cls"]
     name_var = argmap["name"]
     try:
         cls = abstract_utils.get_atomic_value(cls_var)
     except abstract_utils.ConversionError:
         return node, self.vm.new_unsolvable(node)
     # If we can't get a concrete name, treat it like it matches and return a
     # canonical enum member.
     try:
         name = abstract_utils.get_atomic_python_constant(name_var, str)
     except abstract_utils.ConversionError:
         return node, cls.instantiate(node)
     inst = self._get_member_by_name(cls, name)
     if inst:
         return node, inst
     else:
         self.vm.errorlog.attribute_error(self.vm.frames,
                                          cls_var.bindings[0], name)
         return node, self.vm.new_unsolvable(node)
コード例 #27
0
    def annotations_to_instance_types(self, annotations_var):
        """Convert the members of an __annotations__ dict to instance types.

    Args:
      annotations_var: __annotations__, a cfg.Variable of an abstract.Dict.

    Yields:
      A tuple of member name and pytd types.
    """
        try:
            annots = abstract_utils.get_atomic_python_constant(
                annotations_var, dict)
        except abstract_utils.ConversionError:
            return
        for name, member in annots.items():
            yield name, [
                self.value_instance_to_pytd_type(node=None,
                                                 v=v,
                                                 instance=None,
                                                 seen=None,
                                                 view=None)
                for v in member.data
            ]
コード例 #28
0
def get_file_mode(sig, args):
    callargs = {name: var for name, var, _ in sig.signature.iter_args(args)}
    if "mode" in callargs:
        return abstract_utils.get_atomic_python_constant(callargs["mode"])
    else:
        return ""
コード例 #29
0
    def call(self, node, _, args):
        """Creates a namedtuple class definition.

    Performs the same argument checking as collections.namedtuple, e.g. making
    sure field names don't start with _ or digits, making sure no keywords are
    used for the typename or field names, and so on. Because the methods of the
    class have to be changed to match the number and names of the fields, we
    construct pytd.Function and pytd.Constant instances for each member of the
    class. Finally, the pytd.Class is wrapped in an abstract.PyTDClass.

    If incorrect arguments are passed, a subclass of function.FailedFunctionCall
    will be raised. Other cases may raise abstract_utils.ConversionError
    exceptions, such as when the arguments are in unicode or any of the
    arguments have multiple bindings, but these are caught and return Any. This
    also occurs if an argument to namedtuple is invalid, e.g. a keyword is used
    as a field name and rename is False.

    Arguments:
      node: the current CFG node
      _: the func binding, ignored.
      args: a function.Args instance

    Returns:
      a tuple of the given CFG node and an abstract.PyTDClass instance (wrapped
      in a Variable) representing the constructed namedtuple class.
      If a abstract_utils.ConversionError occurs or if field names are invalid,
      this function returns Unsolvable (in a Variable) instead of a PyTDClass.

    Raises:
      function.FailedFunctionCall: Raised by _getargs if any of the arguments
        do not match the function signature.
    """
        # If we can't extract the arguments, we take the easy way out and return Any
        try:
            name_var, field_names, rename = self._getargs(node, args)
        except abstract_utils.ConversionError:
            return node, self.vm.new_unsolvable(node)

        # We need the bare name for a few things, so pull that out now.
        # The same unicode issue can strike here, so again return Any.
        try:
            name = abstract_utils.get_atomic_python_constant(name_var)
        except abstract_utils.ConversionError:
            return node, self.vm.new_unsolvable(node)

        # namedtuple does some checking and optionally renaming of field names,
        # so we do too.
        try:
            field_names = self._validate_and_rename_args(
                name, field_names, rename)
        except ValueError as e:
            self.vm.errorlog.invalid_namedtuple_arg(self.vm.frames,
                                                    utils.message(e))
            return node, self.vm.new_unsolvable(node)

        name = namedtuple_name(name, field_names)
        ast = namedtuple_ast(name,
                             field_names,
                             python_version=self.vm.python_version)
        mapping = self._get_known_types_mapping()

        # A truly well-formed pyi for the namedtuple will have references to the new
        # namedtuple class itself for all `self` params and as the return type for
        # methods like __new__, _replace and _make. In order to do that, we need a
        # ClassType.
        cls_type = pytd.ClassType(name)
        mapping[name] = cls_type

        cls = ast.Lookup(name).Visit(visitors.ReplaceTypes(mapping))
        cls_type.cls = cls

        # Make sure all NamedType nodes have been replaced by ClassType nodes with
        # filled cls pointers.
        cls.Visit(visitors.VerifyLookup())

        # We can't build the PyTDClass directly, and instead must run it through
        # convert.constant_to_value first, for caching.
        instance = self.vm.convert.constant_to_value(cls, {},
                                                     self.vm.root_cfg_node)
        self.vm.trace_namedtuple(instance)
        return node, instance.to_variable(node)
コード例 #30
0
ファイル: enum_overlay.py プロジェクト: njues/pytype
    def call(self, node, func, args, alias_map=None):
        """Implements the behavior of the enum functional API."""
        # Because of how this is called, we supply our own "self" argument.
        # See class_mixin.Class._call_new_and_init.
        args = args.simplify(node, self.vm)
        args = args.replace(posargs=(self.vm.new_unsolvable(node), ) +
                            args.posargs)
        self.load_lazy_attribute("__new__")
        new = abstract_utils.get_atomic_value(self.members["__new__"])
        # Note that super().call or _call_new_and_init won't work here, because
        # they don't raise FailedFunctionCall.
        new.match_args(node, args, alias_map)
        # There should only be 1 signature for Enum.__new__.
        assert len(
            new.signatures) == 1, "Expected only 1 Enum.__new__ signature."
        sig = new.signatures[0].signature
        argmap = {name: var for name, var, _ in sig.iter_args(args)}

        cls_name_var = argmap["value"]
        try:
            names = abstract_utils.get_atomic_python_constant(argmap["names"])
        except abstract_utils.ConversionError as e:
            log.info(
                "Failed to unwrap values in enum functional interface:\n%s", e)
            return node, self.vm.new_unsolvable(node)

        if isinstance(names, str):
            names = names.replace(",", " ").split()
            fields = {name: self.vm.convert.build_int(node) for name in names}
        elif isinstance(names, dict):
            # Dict keys are strings, not strings in variables. The values are
            # variables, they don't need to be changed.
            fields = names
        else:
            # List of names, or list of (name, value) pairs.
            try:
                possible_pairs = [
                    abstract_utils.get_atomic_python_constant(p) for p in names
                ]
            except abstract_utils.ConversionError as e:
                log.debug("Failed to unwrap possible enum field pairs:\n  %s",
                          e)
                return node, self.vm.new_unsolvable(node)
            if not possible_pairs:
                fields = {}
            elif isinstance(possible_pairs[0], str):
                fields = {
                    name: self.vm.convert.build_int(node)
                    for name in possible_pairs
                }
            else:
                # List of (name_var, value_var) pairs.
                # The earlier get_atomic_python_constant call only unwrapped the tuple,
                # so the values in the tuple still need to be unwrapped.
                try:
                    fields = {
                        abstract_utils.get_atomic_python_constant(name): value
                        for name, value in possible_pairs
                    }
                except abstract_utils.ConversionError as e:
                    log.debug("Failed to unwrap field names for enum:\n  %s",
                              e)
                    return node, self.vm.new_unsolvable(node)

        cls_dict = abstract.Dict(self.vm)
        cls_dict.update(node, fields)

        metaclass = self.vm.loaded_overlays["enum"].members["EnumMeta"]

        return self.vm.make_class(node=node,
                                  name_var=cls_name_var,
                                  bases=[self.to_variable(node)],
                                  class_dict_var=cls_dict.to_variable(node),
                                  cls_var=metaclass,
                                  class_type=EnumInstance)