Author: Colin Valliant <alcarithe...@gmail.com>
Branch: pep526
Changeset: r93924:2588a45dab22
Date: 2018-02-14 22:14 -0800
http://bitbucket.org/pypy/pypy/changeset/2588a45dab22/

Log:    Support code generation with variable annotations.

diff --git a/pypy/interpreter/astcompiler/codegen.py 
b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -299,6 +299,12 @@
         else:
             return False
 
+    def _maybe_setup_annotations(self):
+        # if the scope contained an annotated variable assignemt,
+        # this will emit the requisite SETUP_ANNOTATIONS
+        if self.scope.contains_annotated and not isinstance(self, 
AbstractFunctionCodeGenerator):
+            self.emit_op(ops.SETUP_ANNOTATIONS)
+
     def visit_Module(self, mod):
         if not self._handle_body(mod.body):
             self.first_lineno = self.lineno = 1
@@ -925,6 +931,66 @@
         self.visit_sequence(targets)
         return True
 
+    def _annotation_evaluate(self, item):
+        # PEP 526 requires that some things be evaluated, to avoid bugs
+        # where a non-assigning variable annotation references invalid items
+        # this is effectively a NOP, but will fail if e.g. item is an
+        # Attribute and one of the chained names does not exist
+        item.walkabout(self)
+        self.emit_op(ops.POP_TOP)
+
+    def _annotation_eval_slice(self, target):
+        if isinstance(target, ast.Index):
+            self._annotation_evaluate(target.value)
+        elif isinstance(target, ast.Slice):
+            for val in [target.lower, target.upper, target.step]:
+                if val:
+                    self._annotation_evaluate(val)
+        elif isinstance(target, ast.ExtSlice):
+            for val in target.dims:
+                if isinstance(val, ast.Index) or isinstance(val, ast.Slice):
+                    self._annotation_eval_slice(val)
+                else:
+                    self.error("Invalid nested slice", val)
+        else:
+            self.error("Invalid slice?", target)
+
+    def visit_AnnAssign(self, assign):
+        self.update_position(assign.lineno, True)
+        target = assign.target
+        # if there's an assignment to be done, do it
+        if assign.value:
+            assign.value.walkabout(self)
+            target.walkabout(self)
+        # the PEP requires that certain parts of the target be evaluated at 
runtime
+        # to avoid silent annotation-related errors
+        if isinstance(target, ast.Name):
+            # if it's just a simple name and we're not in a function, store
+            # the annotation in __annotations__
+            if assign.simple and not isinstance(self.scope, 
symtable.FunctionScope):
+                assign.annotation.walkabout(self)
+                name = target.id
+                self.emit_op_arg(ops.STORE_ANNOTATION, 
self.add_name(self.names, name))
+        elif isinstance(target, ast.Attribute):
+            # the spec requires that `a.b: int` evaluates `a`
+            # and in a non-function scope, also evaluates `int`
+            # (N.B.: if the target is of the form `a.b.c`, `a.b` will be 
evaluated)
+            if not assign.value:
+                attr = target.value
+                self._annotation_evaluate(attr)
+        elif isinstance(target, ast.Subscript):
+            # similar to the above, `a[0:5]: int` evaluates the name and the 
slice argument
+            # and if not in a function, also evaluates the annotation
+            sl = target.slice
+            self._annotation_evaluate(target.value)
+            self._annotation_eval_slice(sl)
+        else:
+            self.error("can't handle annotation with %s" % (target,), target)
+        # if this is not in a function, evaluate the annotation
+        if not (assign.simple or isinstance(self.scope, 
symtable.FunctionScope)):
+            self._annotation_evaluate(assign.annotation)
+
+
     def visit_With(self, wih):
         self.update_position(wih.lineno, True)
         self.handle_withitem(wih, 0, is_async=False)
@@ -1527,6 +1593,7 @@
                                      symbols, compile_info, qualname=None)
 
     def _compile(self, tree):
+        self._maybe_setup_annotations()
         tree.walkabout(self)
 
     def _get_code_flags(self):
@@ -1656,6 +1723,7 @@
         w_qualname = self.space.newtext(self.qualname)
         self.load_const(w_qualname)
         self.name_op("__qualname__", ast.Store)
+        self._maybe_setup_annotations()
         # compile the body proper
         self._handle_body(cls.body)
         # return the (empty) __class__ cell
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to