diff --git a/.travis.yml b/.travis.yml index 6460a9a726b48..4e46fb7ad85ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,13 +43,6 @@ matrix: - CLIPBOARD_GUI=gtk2 - BUILD_TYPE=conda - DOC_BUILD=true # if rst files were changed, build docs in parallel with tests - - python: 3.3 - env: - - JOB_NAME: "33_nslow" - - NOSE_ARGS="not slow and not disabled" - - FULL_DEPS=true - - CLIPBOARD=xsel - - BUILD_TYPE=conda - python: 3.4 env: - JOB_NAME: "34_nslow" @@ -64,6 +57,13 @@ matrix: - FULL_DEPS=true - CLIPBOARD=xsel - BUILD_TYPE=conda + - python: 3.3 + env: + - JOB_NAME: "33_nslow" + - NOSE_ARGS="not slow and not disabled" + - FULL_DEPS=true + - CLIPBOARD=xsel + - BUILD_TYPE=conda - python: 2.7 env: - JOB_NAME: "27_slow" @@ -104,10 +104,10 @@ matrix: - BUILD_TYPE=pydata - PANDAS_TESTING_MODE="deprecate" allow_failures: - - python: 3.5 + - python: 3.3 env: - - JOB_NAME: "35_nslow" - - NOSE_ARGS="not slow and not network and not disabled" + - JOB_NAME: "33_nslow" + - NOSE_ARGS="not slow and not disabled" - FULL_DEPS=true - CLIPBOARD=xsel - BUILD_TYPE=conda diff --git a/ci/requirements-3.5.txt b/ci/requirements-3.5.txt index 1e67cefa6c98e..7af2c473bceca 100644 --- a/ci/requirements-3.5.txt +++ b/ci/requirements-3.5.txt @@ -10,3 +10,15 @@ cython scipy numexpr pytables +html5lib +lxml + +# currently causing some warnings +#sqlalchemy +#pymysql +#psycopg2 + +# not available from conda +#beautiful-soup +#bottleneck +#matplotlib diff --git a/doc/source/install.rst b/doc/source/install.rst index 36a6c4038f8be..5c5f6bdcf0ddf 100644 --- a/doc/source/install.rst +++ b/doc/source/install.rst @@ -18,7 +18,7 @@ Instructions for installing from source, Python version support ---------------------- -Officially Python 2.6, 2.7, 3.3, and 3.4. +Officially Python 2.6, 2.7, 3.3, 3.4, and 3.5 Installing pandas ----------------- diff --git a/doc/source/release.rst b/doc/source/release.rst index 2010939724c5e..4fa8ba06cf8d0 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -64,6 +64,7 @@ Highlights include: - Support for reading SAS xport files, see :ref:`here ` - Documentation comparing SAS to *pandas*, see :ref:`here ` - Removal of the automatic TimeSeries broadcasting, deprecated since 0.8.0, see :ref:`here ` +- Compatibility with Python 3.5 See the :ref:`v0.17.0 Whatsnew ` overview for an extensive list of all enhancements and bugs that have been fixed in 0.17.0. diff --git a/doc/source/whatsnew/v0.17.0.txt b/doc/source/whatsnew/v0.17.0.txt index 615bfc9e23253..ab0e7a481f031 100644 --- a/doc/source/whatsnew/v0.17.0.txt +++ b/doc/source/whatsnew/v0.17.0.txt @@ -49,6 +49,7 @@ Highlights include: - Support for reading SAS xport files, see :ref:`here ` - Documentation comparing SAS to *pandas*, see :ref:`here ` - Removal of the automatic TimeSeries broadcasting, deprecated since 0.8.0, see :ref:`here ` +- Compatibility with Python 3.5 (:issue:`11097`) Check the :ref:`API Changes ` and :ref:`deprecations ` before updating. diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index 2ac81f15a6d6c..bad7192047e19 100644 --- 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 index 123051d802d7d..2ae6f29f74efc 100644 --- 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/pandas/io/tests/__init__.py b/pandas/io/tests/__init__.py index e6089154cd5e5..e69de29bb2d1d 100644 --- 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 index 96bac2c45340b..e39e71b474f58 100644 --- 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 index 5eef48c51d070..06338e576a2b1 100644 --- 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' diff --git a/setup.py b/setup.py index 370c49a754b95..2d1b9374f6c94 100755 --- 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', ]