From be65922fd388dba800228d14df0c8afff7213f98 Mon Sep 17 00:00:00 2001
From: Stefan Behnel <stefan_ml@behnel.de>
Date: Mon, 16 Dec 2013 21:54:06 +0100
Subject: [PATCH] implement Python corner case of passing a tuple as assert
 message

---
 Cython/Compiler/ExprNodes.py |  3 +-
 Cython/Compiler/Nodes.py     |  8 +++++-
 tests/run/assert.pyx         | 55 ++++++++++++++++++++++++++++++++++++
 3 files changed, 64 insertions(+), 2 deletions(-)

diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
index a9700d06f..44d42d4fb 100644
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -5673,6 +5673,7 @@ class SequenceNode(ExprNode):
     is_sequence_constructor = 1
     unpacked_items = None
     mult_factor = None
+    slow = False  # trade speed for code size (e.g. use PyTuple_Pack())
 
     def compile_time_value_list(self, denv):
         return [arg.compile_time_value(denv) for arg in self.args]
@@ -5754,7 +5755,7 @@ class SequenceNode(ExprNode):
                 else:
                     size_factor = ' * ((%s<0) ? 0:%s)' % (c_mult, c_mult)
 
-        if self.type is Builtin.tuple_type and self.is_literal and not c_mult:
+        if self.type is Builtin.tuple_type and (self.is_literal or self.slow) and not c_mult:
             # use PyTuple_Pack() to avoid generating huge amounts of one-time code
             code.putln('%s = PyTuple_Pack(%d, %s); %s' % (
                 target,
diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py
index f7ff83422..897285b8a 100644
--- a/Cython/Compiler/Nodes.py
+++ b/Cython/Compiler/Nodes.py
@@ -5222,7 +5222,13 @@ class AssertStatNode(StatNode):
         self.cond = self.cond.analyse_boolean_expression(env)
         if self.value:
             value = self.value.analyse_types(env)
-            self.value = value.coerce_to_pyobject(env)
+            if value.type is Builtin.tuple_type or not value.type.is_builtin_type:
+                # prevent tuple values from being interpreted as argument value tuples
+                from ExprNodes import TupleNode
+                value = TupleNode(value.pos, args=[value], slow=True)
+                self.value = value.analyse_types(env, skip_children=True)
+            else:
+                self.value = value.coerce_to_pyobject(env)
         return self
 
     nogil_check = Node.gil_error
diff --git a/tests/run/assert.pyx b/tests/run/assert.pyx
index b431001d9..1e6cc17be 100644
--- a/tests/run/assert.pyx
+++ b/tests/run/assert.pyx
@@ -1,3 +1,7 @@
+# mode: run
+
+cimport cython
+
 def f(a, b, int i):
     """
     >>> f(1, 2, 1)
@@ -15,11 +19,62 @@ def f(a, b, int i):
     assert a+b
     assert i
 
+
+@cython.test_assert_path_exists(
+    '//AssertStatNode',
+    '//AssertStatNode//TupleNode')
 def g(a, b):
     """
     >>> g(1, "works")
     >>> g(0, "fails")
     Traceback (most recent call last):
     AssertionError: fails
+    >>> g(0, (1, 2))
+    Traceback (most recent call last):
+    AssertionError: (1, 2)
     """
     assert a, b
+
+
+@cython.test_assert_path_exists(
+    '//AssertStatNode',
+    '//AssertStatNode//TupleNode')
+def g(a, b):
+    """
+    >>> g(1, "works")
+    >>> g(0, "fails")
+    Traceback (most recent call last):
+    AssertionError: fails
+    >>> g(0, (1, 2))
+    Traceback (most recent call last):
+    AssertionError: (1, 2)
+    """
+    assert a, b
+
+
+@cython.test_assert_path_exists(
+    '//AssertStatNode',
+    '//AssertStatNode//TupleNode',
+    '//AssertStatNode//TupleNode//TupleNode')
+def assert_with_tuple_arg(a):
+    """
+    >>> assert_with_tuple_arg(True)
+    >>> assert_with_tuple_arg(False)
+    Traceback (most recent call last):
+    AssertionError: (1, 2)
+    """
+    assert a, (1, 2)
+
+
+@cython.test_assert_path_exists(
+    '//AssertStatNode')
+@cython.test_fail_if_path_exists(
+    '//AssertStatNode//TupleNode')
+def assert_with_str_arg(a):
+    """
+    >>> assert_with_str_arg(True)
+    >>> assert_with_str_arg(False)
+    Traceback (most recent call last):
+    AssertionError: abc
+    """
+    assert a, 'abc'
-- 
2.30.9