implementation of 'not in' is broken (ticket #455)
authorLisandro Dalcin <dalcinl@gmail.com>
Wed, 11 Nov 2009 15:11:31 +0000 (13:11 -0200)
committerLisandro Dalcin <dalcinl@gmail.com>
Wed, 11 Nov 2009 15:11:31 +0000 (13:11 -0200)
Cython/Compiler/ExprNodes.py
tests/run/contains_T455.pyx [new file with mode: 0644]

index bc05047b66e653f41ae49e72b2ba3a6b6b5ca53d..353a78e09f05b9c84129d4bc3dc7560bc9ca5ac0 100644 (file)
@@ -5226,9 +5226,14 @@ class CmpNode(object):
             coerce_result = "__Pyx_PyBool_FromLong"
         else:
             coerce_result = ""
-        if 'not' in op: negation = "!"
-        else: negation = ""
+        if 'not' in op: 
+            negation = "!"
+        else: 
+            negation = ""
         if op == 'in' or op == 'not_in':
+            assert not coerce_result
+            if op == 'not_in':
+                negation = "if (likely(%s != -1)) %s = !%s; " % ((result_code,)*3)
             if operand2.type is dict_type:
                 code.globalstate.use_utility_code(
                     raise_none_iter_error_utility_code)
@@ -5237,23 +5242,22 @@ class CmpNode(object):
                            code.error_goto(self.pos))
                 code.putln("} else {")
                 code.putln(
-                    "%s = %s(%sPyDict_Contains(%s, %s)); %s" % (
+                    "%s = PyDict_Contains(%s, %s); %s%s" % (
                         result_code, 
-                        coerce_result,
-                        negation,
                         operand2.py_result(), 
                         operand1.py_result(), 
+                        negation,
                         code.error_goto_if_neg(result_code, self.pos)))
                 code.putln("}")
             else:
                 code.putln(
-                    "%s = %s(%sPySequence_Contains(%s, %s)); %s" % (
+                    "%s = PySequence_Contains(%s, %s); %s%s" % (
                         result_code, 
-                        coerce_result,
-                        negation,
                         operand2.py_result(), 
                         operand1.py_result(), 
+                        negation,
                         code.error_goto_if_neg(result_code, self.pos)))
+                    
         elif (operand1.type.is_pyobject
             and op not in ('is', 'is_not')):
                 code.putln("%s = PyObject_RichCompare(%s, %s, %s); %s" % (
diff --git a/tests/run/contains_T455.pyx b/tests/run/contains_T455.pyx
new file mode 100644 (file)
index 0000000..669f89c
--- /dev/null
@@ -0,0 +1,82 @@
+def in_sequence(x, seq):
+    """
+    >>> in_sequence(1, [])
+    False
+    >>> in_sequence(1, ())
+    False
+    >>> in_sequence(1, {})
+    False
+    >>> in_sequence(1, [1])
+    True
+    >>> in_sequence(1, (1,))
+    True
+    >>> in_sequence(1, {1:None})
+    True
+
+    >>> in_sequence(1, None)
+    Traceback (most recent call last):
+    ...
+    TypeError: argument of type 'NoneType' is not iterable
+
+    >>> in_sequence(1, 1)
+    Traceback (most recent call last):
+    ...
+    TypeError: argument of type 'int' is not iterable
+    """
+    return x in seq
+
+def not_in_sequence(x, seq):
+    """
+    >>> not_in_sequence(1, [])
+    True
+    >>> not_in_sequence(1, ())
+    True
+    >>> not_in_sequence(1, {})
+    True
+    >>> not_in_sequence(1, [1])
+    False
+    >>> not_in_sequence(1, (1,))
+    False
+    >>> not_in_sequence(1, {1:None})
+    False
+
+    >>> not_in_sequence(1, None)
+    Traceback (most recent call last):
+    ...
+    TypeError: argument of type 'NoneType' is not iterable
+
+    >>> not_in_sequence(1, 1)
+    Traceback (most recent call last):
+    ...
+    TypeError: argument of type 'int' is not iterable
+    """
+    return x not in seq
+
+
+def in_dict(k, dict dct):
+    """
+    >>> in_dict(1, {})
+    False
+    >>> in_dict(1, {1:None})
+    True
+
+    >>> in_dict(1, None)
+    Traceback (most recent call last):
+    ...
+    TypeError: 'NoneType' object is not iterable
+    """
+    return k in dct
+
+def not_in_dict(k, dict dct):
+    """
+    >>> not_in_dict(1, {})
+    True
+    >>> not_in_dict(1, {1:None})
+    False
+
+    >>> not_in_dict(1, None)
+    Traceback (most recent call last):
+    ...
+    TypeError: 'NoneType' object is not iterable
+    """
+    return k not in dct