예제 #1
0
def _is_or_is_not_cmp(left, right, is_not=False):
  """Implementation of 'left is right' amd 'left is not right'."""
  if (isinstance(left, abstract.PythonConstant) and
      isinstance(right, abstract.PythonConstant)):
    if left.cls != right.cls:
      return is_not
    return is_not ^ (left.pyval == right.pyval)
  elif (isinstance(left, abstract.Instance) and
        isinstance(right, abstract.Instance)):
    try:
      left_class = abstract.get_atomic_value(left.cls)
      right_class = abstract.get_atomic_value(right.cls)
    except abstract.ConversionError:
      # If multiple classes are possible we cannot be sure what happens.
      # Therefore returning None is fine.
      return None
    if left_class != right_class:
      # If those were the same they could be the same but we can't be sure from
      # comparing types.
      return is_not
    return None
  elif isinstance(left, abstract.Class) and isinstance(right, abstract.Class):
    # types are singletons. We use the name so that, e.g., two different
    # TupleClass instances compare as identical.
    return is_not ^ (left.full_name == right.full_name)
  else:
    return None
예제 #2
0
 def test_getitem__concrete_index(self):
     t = abstract.Tuple((self._var, ), self._vm)
     index = self._vm.convert.constant_to_var(0)
     node, var = t.cls.data[0].getitem_slot(self._node, index)
     self.assertIs(node, self._node)
     self.assertIs(abstract.get_atomic_value(var),
                   abstract.get_atomic_value(self._var))
예제 #3
0
 def test_getitem__abstract_index(self):
     t = abstract.Tuple((self._var, ), self._vm)
     index = self._vm.convert.build_int(self._node)
     node, var = t.cls.data[0].getitem_slot(self._node, index)
     self.assertIs(node, self._node)
     self.assertIs(abstract.get_atomic_value(var),
                   abstract.get_atomic_value(self._var))
예제 #4
0
 def create_new_kwargs_value(self, arg_type):
     """Create a kwargs argument given its element type."""
     params = {
         abstract.K: abstract.get_atomic_value(self.str_type),
         abstract.V: arg_type
     }
     return abstract.ParameterizedClass(
         abstract.get_atomic_value(self.dict_type), params, self.vm)
예제 #5
0
    def _eval_expr_as_tuple(self, node, f_globals, f_locals, expr):
        if not expr:
            return ()

        result = abstract.get_atomic_value(
            self._eval_expr(node, f_globals, f_locals, expr))
        # If the result is a tuple, expand it.
        if (isinstance(result, abstract.PythonConstant)
                and isinstance(result.pyval, tuple)):
            return tuple(abstract.get_atomic_value(x) for x in result.pyval)
        else:
            return (result, )
예제 #6
0
    def make_class(self, node, f_locals):
        f_locals = abstract.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.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 = abstract.FunctionArgs(posargs=posargs)
        node, cls_var = self.namedtuple.call(node, None, args)
        cls_val = abstract.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.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.get_atomic_python_constant(fields, tuple)
            fields = [
                abstract.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
예제 #7
0
 def setUp(self):
     super(IsInstanceTest, self).setUp()
     self._is_instance = abstract.IsInstance(self._vm)
     # Easier access to some primitive instances.
     self._bool = self._vm.convert.primitive_class_instances[bool]
     self._int = self._vm.convert.primitive_class_instances[int]
     self._str = self._vm.convert.primitive_class_instances[str]
     # Values that represent primitive classes.
     self._obj_class = abstract.get_atomic_value(
         self._vm.convert.primitive_classes[object])
     self._int_class = abstract.get_atomic_value(
         self._vm.convert.primitive_classes[int])
     self._str_class = abstract.get_atomic_value(
         self._vm.convert.primitive_classes[str])
예제 #8
0
 def setUp(self):
   super(IsInstanceTest, self).setUp()
   self._is_instance = abstract.IsInstance(self._vm)
   # Easier access to some primitive instances.
   self._bool = self._vm.primitive_class_instances[bool]
   self._int = self._vm.primitive_class_instances[int]
   self._str = self._vm.primitive_class_instances[str]
   # Values that represent primitive classes.
   self._obj_class = abstract.get_atomic_value(
       self._vm.primitive_classes[object])
   self._int_class = abstract.get_atomic_value(
       self._vm.primitive_classes[int])
   self._str_class = abstract.get_atomic_value(
       self._vm.primitive_classes[str])
예제 #9
0
 def testInstantiateInterpreterClass(self):
     cls = abstract.InterpreterClass("X", [], {}, None, self._vm)
     # When there is no current frame, create a new instance every time.
     v1 = abstract.get_atomic_value(cls.instantiate(self._node))
     v2 = abstract.get_atomic_value(cls.instantiate(self._node))
     self.assertIsNot(v1, v2)
     # Create one instance per opcode.
     fake_opcode = object()
     self._vm.push_frame(frame_state.SimpleFrame(fake_opcode))
     v3 = abstract.get_atomic_value(cls.instantiate(self._node))
     v4 = abstract.get_atomic_value(cls.instantiate(self._node))
     self.assertIsNot(v1, v3)
     self.assertIsNot(v2, v3)
     self.assertIs(v3, v4)
예제 #10
0
 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.get_atomic_python_constant(name_arg, str)
   except abstract.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.get_atomic_value(type_arg)
   except abstract.ConversionError:
     # We need the type arg to be an atomic value. If not, we just
     # silently return unsolvable.
     return node, self.vm.convert.create_new_unsolvable(node)
   value_arg_name = "val"
   constructor = abstract.SimpleFunction(
       name="__init__",
       param_names=("self", value_arg_name),
       varargs_name=None,
       kwonly_params=(),
       kwargs_name=None,
       defaults={},
       annotations={value_arg_name: type_value},
       late_annotations={},
       vm=self.vm,
   ).to_variable(node)
   members = abstract.Dict(self.vm)
   members.set_str_item(node, "__init__", constructor)
   return node, self.vm.make_class(node, name_arg, (type_arg,),
                                   members.to_variable(node), None)
예제 #11
0
 def call(self, node, unused_func, args):
     """Adds a metaclass."""
     self.match_args(node, args)
     meta = abstract.get_atomic_value(args.posargs[0],
                                      default=self.vm.convert.unsolvable)
     return node, AddMetaclassInstance(meta, self.vm,
                                       self.module_name).to_variable(node)
예제 #12
0
 def _getargs(self, node, args):
     self._match_args(node, args)
     # Normally we would use typing.NamedTuple.__new__ to match args to
     # parameters, but we can't import typing.
     # TODO(tsudol): Replace with typing.NamedTuple.__new__.
     f = function.Signature.from_param_names("typing.NamedTuple",
                                             ["typename", "fields"])
     callargs = {
         arg_name: arg_var
         for arg_name, arg_var, _ in f.iter_args(args)
     }
     # typing.NamedTuple doesn't support rename or verbose
     name_var = callargs["typename"]
     fields_var = callargs["fields"]
     fields = abstract.get_atomic_python_constant(fields_var)
     # The fields is a list of tuples, so we need to deeply unwrap them.
     fields = [abstract.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 (name, typ) in fields:
         names.append(abstract.get_atomic_python_constant(name))
         types.append(abstract.get_atomic_value(typ))
     return name_var, names, types
예제 #13
0
    def _is_instance(self, obj, class_spec):
        """Check if the object matches a class specification.

    Args:
      obj: An AtomicAbstractValue, generally the left hand side of an
          isinstance() call.
      class_spec: An AtomicAbstractValue, generally the right hand side of an
          isinstance() call.

    Returns:
      True if the object is derived from a class in the class_spec, False if
      it is not, and None if it is ambiguous whether obj matches class_spec.
    """
        if isinstance(obj, abstract.AMBIGUOUS_OR_EMPTY):
            return None
        # Assume a single binding for the object's class variable.  If this isn't
        # the case, treat the call as ambiguous.
        cls_var = obj.get_class()
        if cls_var is None:
            return None
        try:
            obj_class = abstract.get_atomic_value(cls_var)
        except abstract.ConversionError:
            return None
        return _check_against_mro(self.vm, obj_class, class_spec)
예제 #14
0
 def _getargs(self, node, args):
     self._match_args(node, args)
     # Normally we would use typing.NamedTuple.__new__ to match args to
     # parameters, but we can't import typing.
     # TODO(tsudol): Replace with typing.NamedTuple.__new__.
     f = function.Signature.from_param_names("typing.NamedTuple",
                                             ["typename", "fields"])
     callargs = {
         arg_name: arg_var
         for arg_name, arg_var, _ in f.iter_args(args)
     }
     # typing.NamedTuple doesn't support rename or verbose
     name_var = callargs["typename"]
     fields_var = callargs["fields"]
     fields = abstract.get_atomic_python_constant(fields_var)
     # The fields is a list of tuples, so we need to deeply unwrap them.
     fields = [abstract.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 = abstract.BadParam(name="fields",
                                           expected=self._fields_type)
             raise abstract.WrongArgTypes(sig.signature, args, self.vm,
                                          bad_param)
         name, typ = field
         names.append(abstract.get_atomic_python_constant(name))
         types.append(abstract.get_atomic_value(typ))
     return name_var, names, types
예제 #15
0
 def test_callable_with_args(self):
     ast = self._load_ast(
         "a", """
   from typing import Callable
   x = ...  # type: Callable[[int, bool], str]
 """)
     x = ast.Lookup("a.x").type
     cls = self._vm.convert.constant_to_value(x, {}, self._vm.root_cfg_node)
     instance = self._vm.convert.constant_to_value(abstract.AsInstance(x),
                                                   {},
                                                   self._vm.root_cfg_node)
     self.assertIsInstance(cls, abstract.Callable)
     self.assertItemsEqual(
         cls.type_parameters.items(),
         [(0, self._vm.convert.int_type),
          (1, self._vm.convert.primitive_classes[bool]),
          (abstract.ARGS,
           abstract.Union([cls.type_parameters[0], cls.type_parameters[1]],
                          self._vm)),
          (abstract.RET, self._vm.convert.str_type)])
     self.assertIsInstance(instance, abstract.Instance)
     self.assertEqual(abstract.get_atomic_value(instance.cls), cls)
     self.assertItemsEqual([
         (name, set(var.data))
         for name, var in instance.type_parameters.items()
     ], [(abstract.ARGS, {
         self._vm.convert.primitive_class_instances[int],
         self._vm.convert.primitive_class_instances[bool]
     }), (abstract.RET, {self._vm.convert.primitive_class_instances[str]})])
예제 #16
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.get_atomic_python_constant(fields_var)
     # The fields is a list of tuples, so we need to deeply unwrap them.
     fields = [abstract.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 = abstract.BadParam(name="fields",
                                           expected=self._fields_type)
             raise abstract.WrongArgTypes(sig.signature, args, self.vm,
                                          bad_param)
         name, typ = field
         names.append(abstract.get_atomic_python_constant(name))
         types.append(abstract.get_atomic_value(typ))
     return name_var, names, types
예제 #17
0
 def apply_type_comment(self, state, op, name, value):
     """If there is a type comment for the op, return its value."""
     assert op is self.vm.frame.current_opcode
     if op.code.co_filename != self.vm.filename:
         return value
     if not op.type_comment:
         return value
     comment = op.type_comment
     try:
         var = self._eval_expr(state.node, self.vm.frame.f_globals,
                               self.vm.frame.f_locals, comment)
     except EvaluationError as e:
         self.vm.errorlog.invalid_type_comment(self.vm.frames,
                                               comment,
                                               details=e.message)
         value = self.vm.convert.create_new_unsolvable(state.node)
     else:
         try:
             typ = abstract.get_atomic_value(var)
         except abstract.ConversionError:
             self.vm.errorlog.invalid_type_comment(
                 self.vm.frames, comment, details="Must be constant.")
             value = self.vm.convert.create_new_unsolvable(state.node)
         else:
             if self.get_type_parameters(typ):
                 self.vm.errorlog.not_supported_yet(
                     self.vm.frames, "using type parameter in type comment")
             try:
                 value = self.init_annotation(typ, name, self.vm.frames,
                                              state.node)
             except self.LateAnnotationError:
                 value = LateAnnotation(typ, name, self.vm.simple_stack())
     return value
예제 #18
0
 def call(self, node, unused_func, args):
     """Creates an anonymous class to act as a metaclass."""
     self.match_args(node, args)
     meta = abstract.get_atomic_value(args.posargs[0],
                                      default=self.vm.convert.unsolvable)
     bases = args.posargs[1:]
     result = WithMetaclassInstance(self.vm, meta, bases).to_variable(node)
     return node, result
예제 #19
0
 def test_call_wrong_argcount(self):
   self._vm.push_frame(FakeFrame())
   node, result = self._is_instance.call(self._node, None, (), self.new_dict())
   self.assertEquals(self._node, node)
   self.assertIsInstance(abstract.get_atomic_value(result),
                         abstract.Unsolvable)
   self.assertRegexpMatches(
       str(self._vm.errorlog),
       r"isinstance .* 0 args .* expected 2.*\[wrong-arg-count\]")
예제 #20
0
 def test_call_wrong_argcount(self):
     self._vm.push_frame(frame_state.SimpleFrame())
     node, result = self._is_instance.call(
         self._node, None,
         abstract.FunctionArgs((), self.new_dict(), None, None))
     self.assertEqual(self._node, node)
     self.assertIsInstance(abstract.get_atomic_value(result),
                           abstract.Unsolvable)
     self.assertRegexpMatches(str(self._vm.errorlog), "missing-parameter")
예제 #21
0
 def test_call_wrong_keywords(self):
     self._vm.push_frame(FakeFrame())
     x = self.new_var("x", abstract.Unknown(self._vm))
     node, result = self._is_instance.call(self._node, None, (x, x),
                                           self.new_dict(foo=x))
     self.assertEquals(self._node, node)
     self.assertIsInstance(abstract.get_atomic_value(result),
                           abstract.Unsolvable)
     self.assertRegexpMatches(str(self._vm.errorlog),
                              r"foo.*isinstance.*\[wrong-keyword-args\]")
예제 #22
0
 def test_call_wrong_argcount(self):
     self._vm.push_frame(FakeFrame())
     node, result = self._is_instance.call(self._node, None, (),
                                           self.new_dict())
     self.assertEquals(self._node, node)
     self.assertIsInstance(abstract.get_atomic_value(result),
                           abstract.Unsolvable)
     self.assertRegexpMatches(
         str(self._vm.errorlog),
         r"isinstance .* 0 args .* expected 2.*\[wrong-arg-count\]")
예제 #23
0
 def test_call_wrong_keywords(self):
   self._vm.push_frame(FakeFrame())
   x = self.new_var("x", abstract.Unknown(self._vm))
   node, result = self._is_instance.call(
       self._node, None, (x, x), self.new_dict(foo=x))
   self.assertEquals(self._node, node)
   self.assertIsInstance(abstract.get_atomic_value(result),
                         abstract.Unsolvable)
   self.assertRegexpMatches(
       str(self._vm.errorlog),
       r"foo.*isinstance.*\[wrong-keyword-args\]")
예제 #24
0
 def test_generic_with_any_param(self):
     ast = self._load_ast(
         "a", """
   from typing import Dict
   x = ...  # type: Dict[str]
 """)
     val = self._vm.convert.constant_to_value("x",
                                              ast.Lookup("a.x").type, {},
                                              self._vm.root_cfg_node)
     self.assertIs(val.type_parameters["K"],
                   abstract.get_atomic_value(self._vm.convert.str_type))
     self.assertIs(val.type_parameters["V"], self._vm.convert.unsolvable)
예제 #25
0
 def test_callable_no_args(self):
   ast = self._load_ast("a", """
     from typing import Callable
     x = ... # type: Callable[[], ...]
   """)
   x = ast.Lookup("a.x").type
   cls = self._vm.convert.constant_to_value(x, {}, self._vm.root_cfg_node)
   instance = self._vm.convert.constant_to_value(
       abstract.AsInstance(x), {}, self._vm.root_cfg_node)
   self.assertIsInstance(cls.type_parameters[abstract.ARGS], abstract.Empty)
   self.assertEqual(
       abstract.get_atomic_value(instance.type_parameters[abstract.ARGS]),
       self._vm.convert.empty)
예제 #26
0
 def test_signature_annotations(self):
     # def f(self: Any, *args: Any)
     self_param = pytd.Parameter("self", pytd.AnythingType(), False, False,
                                 None)
     args_param = pytd.Parameter("args", pytd.AnythingType(), False, True,
                                 None)
     sig = function.Signature.from_pytd(
         self._vm, "f",
         pytd.Signature((self_param, ), args_param, None,
                        pytd.AnythingType(), (), ()))
     self.assertIs(sig.annotations["self"], self._vm.convert.unsolvable)
     args_type = sig.annotations["args"]  # Should be Tuple[Any]
     self.assertIsInstance(args_type, abstract.ParameterizedClass)
     self.assertIs(args_type.base_cls,
                   abstract.get_atomic_value(self._vm.convert.tuple_type))
     self.assertDictEqual(args_type.type_parameters,
                          {abstract.T: self._vm.convert.unsolvable})
     self.assertIs(sig.drop_first_parameter().annotations["args"],
                   args_type)
예제 #27
0
 def test_plain_callable(self):
   ast = self._load_ast("a", """
     from typing import Callable
     x = ...  # type: Callable[..., int]
   """)
   x = ast.Lookup("a.x").type
   cls = self._vm.convert.constant_to_value(x, {}, self._vm.root_cfg_node)
   instance = self._vm.convert.constant_to_value(
       abstract.AsInstance(x), {}, self._vm.root_cfg_node)
   self.assertIsInstance(cls, abstract.ParameterizedClass)
   self.assertItemsEqual(cls.type_parameters.items(),
                         [(abstract.ARGS, self._vm.convert.unsolvable),
                          (abstract.RET, self._vm.convert.int_type)])
   self.assertIsInstance(instance, abstract.Instance)
   self.assertEqual(abstract.get_atomic_value(instance.cls), cls.base_cls)
   self.assertItemsEqual(
       [(name, var.data) for name, var in instance.type_parameters.items()],
       [(abstract.ARGS, [self._vm.convert.unsolvable]),
        (abstract.RET, [self._vm.convert.primitive_class_instances[int]])])
예제 #28
0
 def __init__(self, name, vm):
     base = abstract.get_atomic_value(vm.convert.function_type)
     super(Callable, self).__init__(name, vm, base)
예제 #29
0
 def setUp(self):
     self.vm = vm.VirtualMachine(errors.ErrorLog(), config.Options([""]))
     self.type_type = abstract.get_atomic_value(self.vm.convert.type_type)
예제 #30
0
 def _static_method_to_def(self, node, v, name, kind):
     """Convert a staticmethod to a PyTD definition."""
     # This line may raise abstract.ConversionError
     func = abstract.get_atomic_value(v.func, abstract.Function)
     return self.value_to_pytd_def(node, func, name).Replace(kind=kind)
예제 #31
0
 def create_new_varargs_value(self, arg_type):
     """Create a varargs argument given its element type."""
     params = {abstract.T: arg_type}
     return abstract.ParameterizedClass(
         abstract.get_atomic_value(self.tuple_type), params, self.vm)