Author: Philip Jenvey <[email protected]>
Branch: py3k
Changeset: r63079:6cded36a0209
Date: 2013-04-05 17:25 -0700
http://bitbucket.org/pypy/pypy/changeset/6cded36a0209/

Log:    match cpython's stricter handling of extended unpacking

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
@@ -6,6 +6,7 @@
 # help the annotator.  To it, unfortunately, everything is not so obvious.  If
 # you figure out a way to remove them, great, but try a translation first,
 # please.
+import struct
 
 from pypy.interpreter.astcompiler import ast, assemble, symtable, consts, misc
 from pypy.interpreter.astcompiler import optimize # For side effects
@@ -13,6 +14,7 @@
 from pypy.tool import stdlib_opcode as ops
 from pypy.interpreter.error import OperationError
 
+C_INT_MAX = (2 ** (struct.calcsize('i') * 8)) / 2 - 1
 
 def compile_ast(space, module, info):
     """Generate a code object from AST."""
@@ -771,6 +773,9 @@
             return False
         for target in targets:
             if not isinstance(target, ast.Name):
+                if isinstance(target, ast.Starred):
+                    # these require extra checks
+                    return False
                 break
         else:
             self.visit_sequence(values)
@@ -935,28 +940,24 @@
 
     def _visit_list_or_tuple(self, node, elts, ctx, op):
         elt_count = len(elts) if elts else 0
-        star_pos = -1
         if ctx == ast.Store:
-            if elt_count > 0:
-                for i, elt in enumerate(elts):
-                    if isinstance(elt, ast.Starred):
-                        if star_pos != -1:
-                            msg = "too many starred expressions in assignment"
-                            self.error(msg, node)
-                        star_pos = i
-            if star_pos != -1:
-                self.emit_op_arg(ops.UNPACK_EX, star_pos | 
(elt_count-star_pos-1)<<8)
-            else:
+            seen_star = False
+            for i in range(elt_count):
+                elt = elts[i]
+                is_starred = isinstance(elt, ast.Starred)
+                if is_starred and not seen_star:
+                    if i >= 1 << 8 or elt_count - i - 1 >= (C_INT_MAX >> 8):
+                        self.error("too many expressions in star-unpacking "
+                                   "assignment", node)
+                    self.emit_op_arg(ops.UNPACK_EX,
+                                     i + ((elt_count - i - 1) << 8))
+                    seen_star = True
+                    elts[i] = elt.value
+                elif is_starred:
+                    self.error("two starred expressions in assignment", node)
+            if not seen_star:
                 self.emit_op_arg(ops.UNPACK_SEQUENCE, elt_count)
-        if elt_count > 0:
-            if star_pos != -1:
-                for elt in elts:
-                    if isinstance(elt, ast.Starred):
-                        elt.value.walkabout(self)
-                    else:
-                        elt.walkabout(self)
-            else:
-                self.visit_sequence(elts)
+        self.visit_sequence(elts)
         if ctx == ast.Load:
             self.emit_op_arg(op, elt_count)
 
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py 
b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -884,12 +884,30 @@
                 return a, b, c
         """
         yield self.st, func, "f()", (1, [2, 3], 4)
-        py.test.raises(SyntaxError, self.simple_test, "*a, *b = [1, 2]",
-                       None, None)
-        py.test.raises(SyntaxError, self.simple_test, "a = [*b, c]",
-                       None, None)
-        py.test.raises(SyntaxError, self.simple_test, "for *a in x: pass",
-                       None, None)
+
+    def test_extended_unpacking_fail(self):
+        exc = py.test.raises(SyntaxError, self.simple_test, "*a, *b = [1, 2]",
+                             None, None).value
+        assert exc.msg == "two starred expressions in assignment"
+        exc = py.test.raises(SyntaxError, self.simple_test,
+                             "[*b, *c] = range(10)", None, None).value
+        assert exc.msg == "two starred expressions in assignment"
+
+        exc = py.test.raises(SyntaxError, self.simple_test, "a = [*b, c]",
+                             None, None).value
+        assert exc.msg == "can use starred expression only as assignment 
target"
+        exc = py.test.raises(SyntaxError, self.simple_test, "for *a in x: 
pass",
+                             None, None).value
+        assert exc.msg == "starred assignment target must be in a list or 
tuple"
+
+        s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range(1<<8 
+ 1)"
+        exc = py.test.raises(SyntaxError, self.simple_test, s, None,
+                             None).value
+        assert exc.msg == "too many expressions in star-unpacking assignment"
+        s = ", ".join("a%d" % i for i in range(1<<8 + 1)) + ", *rest = 
range(1<<8 + 2)"
+        exc = py.test.raises(SyntaxError, self.simple_test, s, None,
+                             None).value
+        assert exc.msg == "too many expressions in star-unpacking assignment"
 
 
 class AppTestCompiler:
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to