Repository: pandas-dev/pandas

Issue:
Dataframe.eval(): Negative number in list passed to 'in'-expression causes crash on python 3.4.0
The following crashes on python 3.4.0. It works fine on Python 2.7.5.

```
>>> import pandas
>>> from io import StringIO
>>> data = "foo,bar\n11,12"
>>> df = pandas.read_csv(StringIO(data))
>>> df.eval('foo in [11, 32]')
0    True
dtype: bool
>>> df.eval('foo in [11, -32]')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/core/frame.py", line 1987, in eval
    return _eval(expr, **kwargs)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/eval.py", line 230, in eval
    truediv=truediv)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 635, in __init__
    self.terms = self.parse()
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 652, in parse
    return self._visitor.visit(self.expr)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 314, in visit
    return visitor(node, **kwargs)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 320, in visit_Module
    return self.visit(expr, **kwargs)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 314, in visit
    return visitor(node, **kwargs)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 323, in visit_Expr
    return self.visit(node.value, **kwargs)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 314, in visit
    return visitor(node, **kwargs)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 560, in visit_Compare
    return self.visit(binop)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 314, in visit
    return visitor(node, **kwargs)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 404, in visit_BinOp
    op, op_class, left, right = self._possibly_transform_eq_ne(node)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 357, in _possibly_transform_eq_ne
    right = self.visit(node.right, side='right')
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 314, in visit
    return visitor(node, **kwargs)
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 430, in visit_List
    name = self.env.add_tmp([self.visit(e).value for e in node.elts])
  File "/home/tobias/Envs/qcache-py3/lib/python3.4/site-packages/pandas/computation/expr.py", line 430, in <listcomp>
    name = self.env.add_tmp([self.visit(e).value for e in node.elts])
AttributeError: 'UnaryOp' object has no attribute 'value'
>>> 
```



Discussion:
pls show a reproducible example, e.g. show how the actual frame is created so it can be copy-pasted

pls show `pd.show_versions()`

Sorry, my bad. The lines actually defining the data fell away in during copy-paste. I've updated the example above.

Here's show_versions()

```
>>> import pandas
>>> pandas.show_versions()

INSTALLED VERSIONS
------------------
commit: None
python: 3.4.0.final.0
python-bits: 64
OS: Linux
OS-release: 3.13.0-48-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8

pandas: 0.16.2
nose: None
Cython: None
numpy: 1.9.3
scipy: None
statsmodels: None
IPython: None
sphinx: None
patsy: None
dateutil: 2.4.2
pytz: 2015.6
bottleneck: None
tables: None
numexpr: 2.4.4
matplotlib: None
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: None
httplib2: None
apiclient: None
sqlalchemy: None
pymysql: None
psycopg2: None
```

yeh I guess this is parsed differently in 3.4 that in 2.7. 

pull-requests to fix are welcome


Resolving patch:
diff --git a/pandas/computation/expr.py b/pandas/computation/expr.py
--- a/pandas/computation/expr.py
+++ b/pandas/computation/expr.py
@@ -427,7 +427,7 @@ def visit_Str(self, node, **kwargs):
         return self.term_type(name, self.env)
 
     def visit_List(self, node, **kwargs):
-        name = self.env.add_tmp([self.visit(e).value for e in node.elts])
+        name = self.env.add_tmp([self.visit(e)(self.env) for e in node.elts])
         return self.term_type(name, self.env)
 
     visit_Tuple = visit_List
@@ -655,7 +655,7 @@ def visitor(x, y):
         return reduce(visitor, operands)
 
 # ast.Call signature changed on 3.5,
-# conditionally change  which methods is named
+# conditionally change which methods is named
 # visit_Call depending on Python version, #11097
 if compat.PY35:
     BaseExprVisitor.visit_Call = BaseExprVisitor.visit_Call_35


Test patch:
diff --git a/pandas/computation/tests/test_eval.py b/pandas/computation/tests/test_eval.py
--- a/pandas/computation/tests/test_eval.py
+++ b/pandas/computation/tests/test_eval.py
@@ -29,7 +29,7 @@
 import pandas.computation.expr as expr
 import pandas.util.testing as tm
 from pandas.util.testing import (assert_frame_equal, randbool,
-                                 assertRaisesRegexp,
+                                 assertRaisesRegexp, assert_numpy_array_equal,
                                  assert_produces_warning, assert_series_equal)
 from pandas.compat import PY3, u, reduce
 
@@ -609,6 +609,16 @@ def test_scalar_unary(self):
         self.assertEqual(
             pd.eval('+False', parser=self.parser, engine=self.engine), +False)
 
+    def test_unary_in_array(self):
+        # GH 11235
+        assert_numpy_array_equal(
+            pd.eval('[-True, True, ~True, +True,'
+                    '-False, False, ~False, +False,'
+                    '-37, 37, ~37, +37]'),
+            np.array([-True, True, ~True, +True,
+             -False, False, ~False, +False,
+             -37, 37, ~37, +37]))
+
     def test_disallow_scalar_bool_ops(self):
         exprs = '1 or 2', '1 and 2'
         exprs += 'a and b', 'a or b'
@@ -1256,6 +1266,13 @@ def f():
         expected['c'] = expected['a'] + expected['b']
         assert_frame_equal(df, expected)
 
+    def test_column_in(self):
+        # GH 11235
+        df = DataFrame({'a': [11], 'b': [-32]})
+        result = df.eval('a in [11, -32]')
+        expected = Series([True])
+        assert_series_equal(result, expected)
+
     def test_basic_period_index_boolean_expression(self):
         df = mkdf(2, 2, data_gen_f=f, c_idx_type='p', r_idx_type='i')