fix for assignments to sequences
authorStefan Behnel <scoder@users.berlios.de>
Fri, 21 Nov 2008 10:16:40 +0000 (11:16 +0100)
committerStefan Behnel <scoder@users.berlios.de>
Fri, 21 Nov 2008 10:16:40 +0000 (11:16 +0100)
- in the case of an unpacking error, no assignment must take place
  => do the complete unpacking first, then the assignment
- a tiny bit slower for tuples (<5%)
- shorter, much more readable code

Cython/Compiler/ExprNodes.py
tests/run/tupleassign.pyx [new file with mode: 0644]

index 92c39544d0ef8d01015f4dbf099d13ba2e272a63..2f4560e96588d0f84527e2a238e216c855cbd22f 100644 (file)
@@ -2760,13 +2760,15 @@ class SequenceNode(ExprNode):
         self.iterator.allocate_temps(env)
         for arg, node in zip(self.args, self.coerced_unpacked_items):
             node.allocate_temps(env)
-            arg.allocate_target_temps(env, node)
+            arg.allocate_target_temps(env, None)
             #arg.release_target_temp(env)
             #node.release_temp(env)
         if rhs:
             rhs.release_temp(env)
         self.iterator.release_temp(env)
-    
+        for node in self.coerced_unpacked_items:
+            node.release_temp(env)
+
 #      def release_target_temp(self, env):
 #              #for arg in self.args:
 #              #       arg.release_target_temp(env)
@@ -2786,18 +2788,13 @@ class SequenceNode(ExprNode):
         code.putln("PyObject* tuple = %s;" % rhs.py_result())
         for i in range(len(self.args)):
             item = self.unpacked_items[i]
-            code.putln(
-                "%s = PyTuple_GET_ITEM(tuple, %s);" % (
+            code.put(
+                "%s = PyTuple_GET_ITEM(tuple, %s); " % (
                     item.result(),
                     i))
             code.put_incref(item.result(), item.ctype())
-            value_node = self.coerced_unpacked_items[i]
-            value_node.generate_evaluation_code(code)
-            self.args[i].generate_assignment_code(value_node, code)
-            
         rhs.generate_disposal_code(code)
-        code.putln("}")
-        code.putln("else {")
+        code.putln("} else {")
 
         code.putln(
             "%s = PyObject_GetIter(%s); %s" % (
@@ -2814,9 +2811,6 @@ class SequenceNode(ExprNode):
                     item.result(),
                     typecast(item.ctype(), py_object_type, unpack_code),
                     code.error_goto_if_null(item.result(), self.pos)))
-            value_node = self.coerced_unpacked_items[i]
-            value_node.generate_evaluation_code(code)
-            self.args[i].generate_assignment_code(value_node, code)
         code.put_error_if_neg(self.pos, 
             "__Pyx_EndUnpack(%s)" % (
                 self.iterator.py_result()))
@@ -2826,6 +2820,10 @@ class SequenceNode(ExprNode):
         self.iterator.generate_disposal_code(code)
 
         code.putln("}")
+        for i in range(len(self.args)):
+            value_node = self.coerced_unpacked_items[i]
+            value_node.generate_evaluation_code(code)
+            self.args[i].generate_assignment_code(value_node, code)
         
     def annotate(self, code):
         for arg in self.args:
diff --git a/tests/run/tupleassign.pyx b/tests/run/tupleassign.pyx
new file mode 100644 (file)
index 0000000..3149f2b
--- /dev/null
@@ -0,0 +1,34 @@
+__doc__ = u"""
+>>> assign3(l)
+(1, 2, 3)
+>>> assign3(t)
+(1, 2, 3)
+>>> 
+
+>>> a,b = 99,99
+>>> a,b = t
+Traceback (most recent call last):
+ValueError: too many values to unpack
+>>> a,b
+(99, 99)
+
+>>> test_overwrite(l)
+(99, 99)
+>>> test_overwrite(t)
+(99, 99)
+"""
+
+t = (1,2,3)
+l = [1,2,3]
+
+def assign3(t):
+    a,b,c = t
+    return (a,b,c)
+
+def test_overwrite(t):
+    a,b = 99,99
+    try:
+        a,b = t
+    except ValueError:
+        pass
+    return (a,b)