Example #1
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:
         frame = self.vm.frame
         var = abstract_utils.eval_expr(self.vm, state.node,
                                        frame.f_globals, frame.f_locals,
                                        comment)
     except abstract_utils.EvaluationError as e:
         self.vm.errorlog.invalid_type_comment(self.vm.frames,
                                               comment,
                                               details=utils.message(e))
         value = self.vm.new_unsolvable(state.node)
     else:
         try:
             typ = abstract_utils.get_atomic_value(var)
         except abstract_utils.ConversionError:
             self.vm.errorlog.invalid_type_comment(
                 self.vm.frames, comment, details="Must be constant.")
             value = self.vm.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
Example #2
0
 def apply_annotation(self, state, op, name, value):
     """If there is an annotation for the op, return its value."""
     assert op is self.vm.frame.current_opcode
     if op.code.co_filename != self.vm.filename:
         return None, value
     if not op.annotation:
         return None, value
     annot = op.annotation
     frame = self.vm.frame
     var, errorlog = abstract_utils.eval_expr(self.vm, state.node,
                                              frame.f_globals,
                                              frame.f_locals, annot)
     if errorlog:
         self.vm.errorlog.invalid_annotation(self.vm.frames,
                                             annot,
                                             details=errorlog.details)
     typ = self.extract_annotation(state.node,
                                   var,
                                   name,
                                   self.vm.simple_stack(),
                                   is_var=True)
     _, value = self.vm.init_class(state.node, typ)
     for d in value.data:
         d.from_annotation = name
     return typ, value
Example #3
0
    def _eval_expr_as_tuple(self, node, f_globals, f_locals, expr):
        """Evaluate an expression as a tuple."""
        if not expr:
            return ()

        result = abstract_utils.get_atomic_value(
            abstract_utils.eval_expr(self.vm, node, f_globals, f_locals, expr))
        # If the result is a tuple, expand it.
        if (isinstance(result, mixin.PythonConstant)
                and isinstance(result.pyval, tuple)):
            return tuple(
                abstract_utils.get_atomic_value(x) for x in result.pyval)
        else:
            return (result, )
Example #4
0
 def apply_annotation(self, state, op, name, value):
   """If there is an annotation for the op, return its value."""
   assert op is self.vm.frame.current_opcode
   if op.code.co_filename != self.vm.filename:
     return None, value
   if not op.annotation:
     return None, value
   annot = op.annotation
   frame = self.vm.frame
   var, errorlog = abstract_utils.eval_expr(
       self.vm, state.node, frame.f_globals, frame.f_locals, annot)
   if errorlog:
     self.vm.errorlog.invalid_annotation(
         self.vm.frames, annot, details=errorlog.details)
   if frame.func and isinstance(frame.func.data, abstract.BoundFunction):
     self_var = frame.f_locals.pyval.get("self")
     if self_var:
       allowed_type_params = []
       for v in self_var.data:
         if v.cls:
           allowed_type_params.extend(p.name for p in v.cls.template)
     else:
       allowed_type_params = ()
   else:
     self_var = None
     allowed_type_params = self.vm.frame.type_params
   typ = self.extract_annotation(
       state.node, var, name, self.vm.simple_stack(),
       allowed_type_params=allowed_type_params)
   if typ.formal and self_var:
     type_params = self.get_type_parameters(typ)
     substs = [
         abstract_utils.get_type_parameter_substitutions(v, type_params)
         for v in self_var.data]
     resolved_type = self.sub_one_annotation(state.node, typ, substs,
                                             instantiate_unbound=False)
     _, value = self.init_annotation(state.node, name, resolved_type)
   elif typ.formal:
     # The only time extract_annotation returns a formal type (i.e., one that
     # contains type parameters) is when we are in the body of a generic class.
     # Without `self_var`, we don't know what to instantiate those parameters
     # to. We set the value to empty to defer instantiation until the value is
     # being looked up on class instances, at which point the parameters will
     # have been filled in.
     value = self.vm.convert.empty.to_variable(state.node)
   else:
     _, value = self.init_annotation(state.node, name, typ)
   return typ, value
Example #5
0
 def apply_annotation(self, node, op, name, value):
   """If there is an annotation for the op, return its value."""
   assert op is self.vm.frame.current_opcode
   if op.code.co_filename != self.vm.filename:
     return None, value
   if not op.annotation:
     return None, value
   annot = op.annotation
   frame = self.vm.frame
   var, errorlog = abstract_utils.eval_expr(
       self.vm, node, frame.f_globals, frame.f_locals, annot)
   if errorlog:
     self.vm.errorlog.invalid_annotation(
         self.vm.frames, annot, details=errorlog.details)
   return self.extract_and_init_annotation(node, name, var,
                                           use_not_supported_yet=True)
Example #6
0
  def _eval_expr_as_tuple(self, node, expr, stack):
    """Evaluate an expression as a tuple."""
    if not expr:
      return (), None

    f_globals, f_locals = self.vm.frame.f_globals, self.vm.frame.f_locals
    with self.vm.generate_late_annotations(stack):
      result_var, errorlog = abstract_utils.eval_expr(
          self.vm, node, f_globals, f_locals, expr)
    result = abstract_utils.get_atomic_value(result_var)
    # If the result is a tuple, expand it.
    if (isinstance(result, mixin.PythonConstant) and
        isinstance(result.pyval, tuple)):
      return (tuple(abstract_utils.get_atomic_value(x) for x in result.pyval),
              errorlog)
    else:
      return (result,), errorlog
Example #7
0
 def apply_annotation(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.annotation:
         return value
     annot = op.annotation
     frame = self.vm.frame
     var, errorlog = abstract_utils.eval_expr(self.vm, state.node,
                                              frame.f_globals,
                                              frame.f_locals, annot)
     if errorlog:
         self.vm.errorlog.invalid_annotation(self.vm.frames,
                                             annot,
                                             details=errorlog.details)
     try:
         typ = abstract_utils.get_atomic_value(var)
     except abstract_utils.ConversionError:
         self.vm.errorlog.invalid_annotation(self.vm.frames,
                                             annot,
                                             details="Must be constant.")
         value = self.vm.new_unsolvable(state.node)
     else:
         typ = self._process_one_annotation(state.node, typ, name,
                                            self.vm.simple_stack())
         if typ:
             if self.get_type_parameters(typ):
                 self.vm.errorlog.not_supported_yet(
                     self.vm.frames,
                     "using type parameter in variable annotation")
                 value = self.vm.new_unsolvable(state.node)
             else:
                 _, value = self.vm.init_class(state.node, typ)
         else:
             value = self.vm.new_unsolvable(state.node)
     return value
Example #8
0
  def _process_one_annotation(self, node, annotation, name, stack):
    """Change annotation / record errors where required."""
    # Make sure we pass in a frozen snapshot of the frame stack, rather than the
    # actual stack, since late annotations need to snapshot the stack at time of
    # creation in order to get the right line information for error messages.
    assert isinstance(stack, tuple), "stack must be an immutable sequence"

    if isinstance(annotation, abstract.AnnotationContainer):
      annotation = annotation.base_cls

    if isinstance(annotation, typing_overlay.Union):
      self.vm.errorlog.invalid_annotation(
          stack, annotation, "Needs options", name)
      return None
    elif (name is not None and name != "return"
          and isinstance(annotation, typing_overlay.NoReturn)):
      self.vm.errorlog.invalid_annotation(
          stack, annotation, "NoReturn is not allowed", name)
      return None
    elif isinstance(annotation, abstract.Instance) and (
        annotation.cls == self.vm.convert.str_type or
        annotation.cls == self.vm.convert.unicode_type
    ):
      # String annotations : Late evaluation
      if isinstance(annotation, mixin.PythonConstant):
        expr = annotation.pyval
        if not expr:
          self.vm.errorlog.invalid_annotation(
              stack, annotation, "Cannot be an empty string", name)
          return None
        frame = self.vm.frame
        # Immediately try to evaluate the reference, generating LateAnnotation
        # objects as needed. We don't store the entire string as a
        # LateAnnotation because:
        # - Starting in 3.8, or in 3.7 with __future__.annotations, all
        #   annotations look like forward references - most of them don't need
        #   to be late evaluated.
        # - Given an expression like "Union[str, NotYetDefined]", we want to
        #   evaluate the union immediately so we don't end up with a complex
        #   LateAnnotation, which can lead to bugs when instantiated.
        with self.vm.generate_late_annotations(stack):
          v, errorlog = abstract_utils.eval_expr(
              self.vm, node, frame.f_globals, frame.f_locals, expr)
        if errorlog:
          self.vm.errorlog.copy_from(errorlog.errors, stack)
        if len(v.data) == 1:
          return self._process_one_annotation(node, v.data[0], name, stack)
      self.vm.errorlog.ambiguous_annotation(stack, [annotation], name)
      return None
    elif annotation.cls == self.vm.convert.none_type:
      # PEP 484 allows to write "NoneType" as "None"
      return self.vm.convert.none_type
    elif isinstance(annotation, mixin.NestedAnnotation):
      if annotation.processed:
        return annotation
      annotation.processed = True
      for key, typ in annotation.get_inner_types():
        processed = self._process_one_annotation(node, typ, name, stack)
        if processed is None:
          return None
        elif isinstance(processed, typing_overlay.NoReturn):
          self.vm.errorlog.invalid_annotation(
              stack, typ, "NoReturn is not allowed as inner type", name)
          return None
        annotation.update_inner_type(key, processed)
      return annotation
    elif isinstance(annotation, (class_mixin.Class,
                                 abstract.AMBIGUOUS_OR_EMPTY,
                                 abstract.TypeParameter,
                                 typing_overlay.NoReturn)):
      return annotation
    else:
      self.vm.errorlog.invalid_annotation(stack, annotation, "Not a type", name)
      return None
Example #9
0
    def _process_one_annotation(self,
                                node,
                                annotation,
                                name,
                                stack,
                                seen=None):
        """Change annotation / record errors where required."""
        # Make sure we pass in a frozen snapshot of the frame stack, rather than the
        # actual stack, since late annotations need to snapshot the stack at time of
        # creation in order to get the right line information for error messages.
        assert isinstance(stack, tuple), "stack must be an immutable sequence"

        # Check for recursive type annotations so we can emit an error message
        # rather than crashing.
        if seen is None:
            seen = set()
        if annotation.is_late_annotation():
            if annotation in seen:
                self.vm.errorlog.not_supported_yet(
                    stack,
                    "Recursive type annotations",
                    details="In annotation '%s' on %s" %
                    (annotation.expr, name))
                return None
            seen = seen | {annotation}

        if isinstance(annotation, abstract.AnnotationContainer):
            annotation = annotation.base_cls

        if isinstance(annotation, typing_overlay.Union):
            self.vm.errorlog.invalid_annotation(stack, annotation,
                                                "Needs options", name)
            return None
        elif (name is not None and name != "return"
              and isinstance(annotation, typing_overlay.NoReturn)):
            self.vm.errorlog.invalid_annotation(stack, annotation,
                                                "NoReturn is not allowed",
                                                name)
            return None
        elif isinstance(annotation, abstract.Instance) and (
                annotation.cls == self.vm.convert.str_type
                or annotation.cls == self.vm.convert.unicode_type):
            # String annotations : Late evaluation
            if isinstance(annotation, mixin.PythonConstant):
                expr = annotation.pyval
                if not expr:
                    self.vm.errorlog.invalid_annotation(
                        stack, annotation, "Cannot be an empty string", name)
                    return None
                frame = self.vm.frame
                # Immediately try to evaluate the reference, generating LateAnnotation
                # objects as needed. We don't store the entire string as a
                # LateAnnotation because:
                # - Starting in 3.8, or in 3.7 with __future__.annotations, all
                #   annotations look like forward references - most of them don't need
                #   to be late evaluated.
                # - Given an expression like "Union[str, NotYetDefined]", we want to
                #   evaluate the union immediately so we don't end up with a complex
                #   LateAnnotation, which can lead to bugs when instantiated.
                with self.vm.generate_late_annotations(stack):
                    v, errorlog = abstract_utils.eval_expr(
                        self.vm, node, frame.f_globals, frame.f_locals, expr)
                if errorlog:
                    self.vm.errorlog.copy_from(errorlog.errors, stack)
                if len(v.data) == 1:
                    return self._process_one_annotation(
                        node, v.data[0], name, stack, seen)
            self.vm.errorlog.invalid_annotation(stack, annotation,
                                                "Must be constant", name)
            return None
        elif annotation.cls == self.vm.convert.none_type:
            # PEP 484 allows to write "NoneType" as "None"
            return self.vm.convert.none_type
        elif isinstance(annotation, abstract.ParameterizedClass):
            for param_name, param in annotation.formal_type_parameters.items():
                processed = self._process_one_annotation(
                    node, param, name, stack, seen)
                if processed is None:
                    return None
                elif isinstance(processed, typing_overlay.NoReturn):
                    self.vm.errorlog.invalid_annotation(
                        stack, param, "NoReturn is not allowed as inner type",
                        name)
                    return None
                annotation.formal_type_parameters[param_name] = processed
            return annotation
        elif isinstance(annotation, abstract.Union):
            options = []
            for option in annotation.options:
                processed = self._process_one_annotation(
                    node, option, name, stack, seen)
                if processed is None:
                    return None
                elif isinstance(processed, typing_overlay.NoReturn):
                    self.vm.errorlog.invalid_annotation(
                        stack, option, "NoReturn is not allowed as inner type",
                        name)
                    return None
                options.append(processed)
            annotation.options = tuple(options)
            return annotation
        elif isinstance(annotation,
                        (mixin.Class, abstract.AMBIGUOUS_OR_EMPTY,
                         abstract.TypeParameter, typing_overlay.NoReturn)):
            return annotation
        else:
            self.vm.errorlog.invalid_annotation(stack, annotation,
                                                "Not a type", name)
            return None
Example #10
0
    def _process_one_annotation(self,
                                annotation,
                                name,
                                stack,
                                node=None,
                                f_globals=None,
                                f_locals=None):
        """Change annotation / record errors where required."""
        if isinstance(annotation, abstract.AnnotationContainer):
            annotation = annotation.base_cls

        if isinstance(annotation, typing_overlay.Union):
            self.vm.errorlog.invalid_annotation(stack, annotation,
                                                "Needs options", name)
            return None
        elif (name is not None and name != "return"
              and isinstance(annotation, typing_overlay.NoReturn)):
            self.vm.errorlog.invalid_annotation(stack, annotation,
                                                "NoReturn is not allowed",
                                                name)
            return None
        elif isinstance(annotation, abstract.Instance) and (
                annotation.cls == self.vm.convert.str_type
                or annotation.cls == self.vm.convert.unicode_type):
            # String annotations : Late evaluation
            if isinstance(annotation, mixin.PythonConstant):
                if f_globals is None:
                    raise self.LateAnnotationError()
                else:
                    try:
                        v = abstract_utils.eval_expr(self.vm, node, f_globals,
                                                     f_locals,
                                                     annotation.pyval)
                    except abstract_utils.EvaluationError as e:
                        self.vm.errorlog.invalid_annotation(
                            stack, annotation, utils.message(e))
                        return None
                    if len(v.data) == 1:
                        return self._process_one_annotation(
                            v.data[0], name, stack, node, f_globals, f_locals)
            self.vm.errorlog.invalid_annotation(stack, annotation,
                                                "Must be constant", name)
            return None
        elif annotation.cls == self.vm.convert.none_type:
            # PEP 484 allows to write "NoneType" as "None"
            return self.vm.convert.none_type
        elif isinstance(annotation, abstract.ParameterizedClass):
            for param_name, param in annotation.formal_type_parameters.items():
                processed = self._process_one_annotation(
                    param, name, stack, node, f_globals, f_locals)
                if processed is None:
                    return None
                elif isinstance(processed, typing_overlay.NoReturn):
                    self.vm.errorlog.invalid_annotation(
                        stack, param, "NoReturn is not allowed as inner type",
                        name)
                    return None
                annotation.formal_type_parameters[param_name] = processed
            return annotation
        elif isinstance(annotation, abstract.Union):
            options = []
            for option in annotation.options:
                processed = self._process_one_annotation(
                    option, name, stack, node, f_globals, f_locals)
                if processed is None:
                    return None
                elif isinstance(processed, typing_overlay.NoReturn):
                    self.vm.errorlog.invalid_annotation(
                        stack, option, "NoReturn is not allowed as inner type",
                        name)
                    return None
                options.append(processed)
            annotation.options = tuple(options)
            return annotation
        elif isinstance(annotation,
                        (mixin.Class, abstract.AMBIGUOUS_OR_EMPTY,
                         abstract.TypeParameter, typing_overlay.NoReturn)):
            return annotation
        else:
            self.vm.errorlog.invalid_annotation(stack, annotation,
                                                "Not a type", name)
            return None