Repository: pandas-dev/pandas

Issue:
TST/COMPAT: python 3.5 compat
A few changes are needed: https://travis-ci.org/jreback/pandas/jobs/80331174 (since merged to master)

so the Call node as changed
need to do something like this: https://bitbucket.org/pytest-dev/pytest/pull-requests/296/astcall-signature-changed-on-35/diff

If this is fixed I think everything will pass. I guess this is an API change in python.

```
======================================================================
ERROR: test_df_use_case (pandas.computation.tests.test_eval.TestMathNumExprPandas)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/build/jreback/pandas/pandas/computation/tests/test_eval.py", line 1488, in test_df_use_case
    parser=self.parser)
  File "/home/travis/build/jreback/pandas/pandas/core/frame.py", line 2102, in eval
    return _eval(expr, **kwargs)
  File "/home/travis/build/jreback/pandas/pandas/computation/eval.py", line 230, in eval
    truediv=truediv)
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 656, in __init__
    self.terms = self.parse()
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 673, in parse
    return self._visitor.visit(self.expr)
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 314, in visit
    return visitor(node, **kwargs)
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 320, in visit_Module
    return self.visit(expr, **kwargs)
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 314, in visit
    return visitor(node, **kwargs)
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 498, in visit_Assign
    return self.visit(node.value, **kwargs)
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 314, in visit
    return visitor(node, **kwargs)
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 543, in visit_Call
    args = [self.visit(targ) for targ in node.args]
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 543, in <listcomp>
    args = [self.visit(targ) for targ in node.args]
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 314, in visit
    return visitor(node, **kwargs)
  File "/home/travis/build/jreback/pandas/pandas/computation/expr.py", line 545, in visit_Call
    if node.starargs is not None:
AttributeError: 'Call' object has no attribute 'starargs'
```



Discussion:

Resolving patch:
diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py
--- a/pandas/compat/__init__.py
+++ b/pandas/compat/__init__.py
@@ -36,9 +36,9 @@
 import sys
 import types
 
-PY3 = (sys.version_info[0] >= 3)
 PY2 = sys.version_info[0] == 2
-
+PY3 = (sys.version_info[0] >= 3)
+PY35 = (sys.version_info >= (3, 5))
 
 try:
     import __builtin__ as builtins
diff --git a/pandas/computation/expr.py b/pandas/computation/expr.py
--- a/pandas/computation/expr.py
+++ b/pandas/computation/expr.py
@@ -516,7 +516,54 @@ def visit_Attribute(self, node, **kwargs):
 
         raise ValueError("Invalid Attribute context {0}".format(ctx.__name__))
 
-    def visit_Call(self, node, side=None, **kwargs):
+    def visit_Call_35(self, node, side=None, **kwargs):
+        """ in 3.5 the starargs attribute was changed to be more flexible, #11097 """
+
+        if isinstance(node.func, ast.Attribute):
+            res = self.visit_Attribute(node.func)
+        elif not isinstance(node.func, ast.Name):
+            raise TypeError("Only named functions are supported")
+        else:
+            try:
+                res = self.visit(node.func)
+            except UndefinedVariableError:
+                # Check if this is a supported function name
+                try:
+                    res = FuncNode(node.func.id)
+                except ValueError:
+                    # Raise original error
+                    raise
+
+        if res is None:
+            raise ValueError("Invalid function call {0}".format(node.func.id))
+        if hasattr(res, 'value'):
+            res = res.value
+
+        if isinstance(res, FuncNode):
+
+            new_args = [ self.visit(arg) for arg in node.args ]
+
+            if node.keywords:
+                raise TypeError("Function \"{0}\" does not support keyword "
+                                "arguments".format(res.name))
+
+            return res(*new_args, **kwargs)
+
+        else:
+
+            new_args = [ self.visit(arg).value for arg in node.args ]
+
+            for key in node.keywords:
+                if not isinstance(key, ast.keyword):
+                    raise ValueError("keyword error in function call "
+                                     "'{0}'".format(node.func.id))
+
+                if key.arg:
+                    kwargs.append(ast.keyword(keyword.arg, self.visit(keyword.value)))
+
+            return self.const_type(res(*new_args, **kwargs), self.env)
+
+    def visit_Call_legacy(self, node, side=None, **kwargs):
 
         # this can happen with: datetime.datetime
         if isinstance(node.func, ast.Attribute):
@@ -607,6 +654,13 @@ def visitor(x, y):
         operands = node.values
         return reduce(visitor, operands)
 
+# ast.Call signature changed on 3.5,
+# conditionally change  which methods is named
+# visit_Call depending on Python version, #11097
+if compat.PY35:
+    BaseExprVisitor.visit_Call = BaseExprVisitor.visit_Call_35
+else:
+    BaseExprVisitor.visit_Call = BaseExprVisitor.visit_Call_legacy
 
 _python_not_supported = frozenset(['Dict', 'BoolOp', 'In', 'NotIn'])
 _numexpr_supported_calls = frozenset(_reductions + _mathops)
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -181,6 +181,7 @@ def build_extensions(self):
     'Programming Language :: Python :: 2.7',
     'Programming Language :: Python :: 3.3',
     'Programming Language :: Python :: 3.4',
+    'Programming Language :: Python :: 3.5',
     'Programming Language :: Cython',
     'Topic :: Scientific/Engineering',
 ]


Test patch:
diff --git a/pandas/io/tests/__init__.py b/pandas/io/tests/__init__.py
--- a/pandas/io/tests/__init__.py
+++ b/pandas/io/tests/__init__.py
@@ -1,4 +0,0 @@
-
-def setUp():
-    import socket
-    socket.setdefaulttimeout(5)
diff --git a/pandas/io/tests/test_data.py b/pandas/io/tests/test_data.py
--- a/pandas/io/tests/test_data.py
+++ b/pandas/io/tests/test_data.py
@@ -27,6 +27,13 @@ def _skip_if_no_lxml():
     except ImportError:
         raise nose.SkipTest("no lxml")
 
+def _skip_if_no_bs():
+    try:
+        import bs4
+        import html5lib
+    except ImportError:
+        raise nose.SkipTest("no html5lib/bs4")
+
 
 def assert_n_failed_equals_n_null_columns(wngs, obj, cls=SymbolWarning):
     all_nan_cols = pd.Series(dict((k, pd.isnull(v).all()) for k, v in
@@ -288,6 +295,7 @@ class TestYahooOptions(tm.TestCase):
     def setUpClass(cls):
         super(TestYahooOptions, cls).setUpClass()
         _skip_if_no_lxml()
+        _skip_if_no_bs()
 
         # aapl has monthlies
         cls.aapl = web.Options('aapl', 'yahoo')
diff --git a/pandas/io/tests/test_pytables.py b/pandas/io/tests/test_pytables.py
--- a/pandas/io/tests/test_pytables.py
+++ b/pandas/io/tests/test_pytables.py
@@ -2573,7 +2573,9 @@ def test_tuple_index(self):
         idx = [(0., 1.), (2., 3.), (4., 5.)]
         data = np.random.randn(30).reshape((3, 10))
         DF = DataFrame(data, index=idx, columns=col)
-        with tm.assert_produces_warning(expected_warning=PerformanceWarning):
+
+        expected_warning = Warning if compat.PY35 else PerformanceWarning
+        with tm.assert_produces_warning(expected_warning=expected_warning, check_stacklevel=False):
             self._check_roundtrip(DF, tm.assert_frame_equal)
 
     def test_index_types(self):
@@ -2585,23 +2587,25 @@ def test_index_types(self):
                                                    check_index_type=True,
                                                    check_series_type=True)
 
-        with tm.assert_produces_warning(expected_warning=PerformanceWarning):
+        # nose has a deprecation warning in 3.5
+        expected_warning = Warning if compat.PY35 else PerformanceWarning
+        with tm.assert_produces_warning(expected_warning=expected_warning, check_stacklevel=False):
             ser = Series(values, [0, 'y'])
             self._check_roundtrip(ser, func)
 
-        with tm.assert_produces_warning(expected_warning=PerformanceWarning):
+        with tm.assert_produces_warning(expected_warning=expected_warning, check_stacklevel=False):
             ser = Series(values, [datetime.datetime.today(), 0])
             self._check_roundtrip(ser, func)
 
-        with tm.assert_produces_warning(expected_warning=PerformanceWarning):
+        with tm.assert_produces_warning(expected_warning=expected_warning, check_stacklevel=False):
             ser = Series(values, ['y', 0])
             self._check_roundtrip(ser, func)
 
-        with tm.assert_produces_warning(expected_warning=PerformanceWarning):
+        with tm.assert_produces_warning(expected_warning=expected_warning, check_stacklevel=False):
             ser = Series(values, [datetime.date.today(), 'a'])
             self._check_roundtrip(ser, func)
 
-        with tm.assert_produces_warning(expected_warning=PerformanceWarning):
+        with tm.assert_produces_warning(expected_warning=expected_warning, check_stacklevel=False):
             ser = Series(values, [1.23, 'b'])
             self._check_roundtrip(ser, func)
 
@@ -3377,7 +3381,8 @@ def test_retain_index_attributes2(self):
 
         with ensure_clean_path(self.path) as path:
 
-            with tm.assert_produces_warning(expected_warning=AttributeConflictWarning):
+            expected_warning = Warning if compat.PY35 else AttributeConflictWarning
+            with tm.assert_produces_warning(expected_warning=expected_warning, check_stacklevel=False):
 
                 df  = DataFrame(dict(A = Series(lrange(3), index=date_range('2000-1-1',periods=3,freq='H'))))
                 df.to_hdf(path,'data',mode='w',append=True)
@@ -3391,7 +3396,7 @@ def test_retain_index_attributes2(self):
 
             self.assertEqual(read_hdf(path,'data').index.name, 'foo')
 
-            with tm.assert_produces_warning(expected_warning=AttributeConflictWarning):
+            with tm.assert_produces_warning(expected_warning=expected_warning, check_stacklevel=False):
 
                 idx2 = date_range('2001-1-1',periods=3,freq='H')
                 idx2.name = 'bar'