From 6b1ada8320b71ce9674029de802b2349f6b73f55 Mon Sep 17 00:00:00 2001 From: "John W. O'Brien" Date: Thu, 22 May 2014 19:59:49 -0400 Subject: [PATCH] TST/DOC/BUG: Accommodate OpenPyXL incompatibility * Detect whether a compatible version is installed * Skip openpyxl unit tests instead of failing * Inhibit registration of `openpyxl` engine in `ExcelWriter` * Raise `UserWarning` * Document limitation as a known issue * Resolve issue #7169 --- README.md | 3 ++- doc/source/install.rst | 2 +- doc/source/v0.14.0.txt | 10 ++++++++++ pandas/compat/openpyxl_compat.py | 26 ++++++++++++++++++++++++++ pandas/io/excel.py | 8 +++++++- pandas/io/tests/test_excel.py | 6 +++++- 6 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 pandas/compat/openpyxl_compat.py diff --git a/README.md b/README.md index 6f50c1e2ea10e..79a84440d6a5c 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,8 @@ pip install pandas - [xlrd/xlwt](http://www.python-excel.org/) - Excel reading (xlrd) and writing (xlwt) - [openpyxl](http://packages.python.org/openpyxl/) - - openpyxl version 1.6.1 or higher, for writing .xlsx files + - openpyxl version 1.6.1 or higher, but lower than 2.0.0, for + writing .xlsx files - xlrd >= 0.9.0 - [XlsxWriter](https://pypi.python.org/pypi/XlsxWriter) - Alternative Excel writer. diff --git a/doc/source/install.rst b/doc/source/install.rst index 18ad031e8a880..7cce761445c51 100644 --- a/doc/source/install.rst +++ b/doc/source/install.rst @@ -97,7 +97,7 @@ Optional Dependencies * `statsmodels `__ * Needed for parts of :mod:`pandas.stats` * `openpyxl `__, `xlrd/xlwt `__ - * openpyxl version 1.6.1 or higher + * openpyxl version 1.6.1 or higher, but lower than 2.0.0 * Needed for Excel I/O * `XlsxWriter `__ * Alternative Excel writer. diff --git a/doc/source/v0.14.0.txt b/doc/source/v0.14.0.txt index 525b6f123a617..16cd23183e424 100644 --- a/doc/source/v0.14.0.txt +++ b/doc/source/v0.14.0.txt @@ -31,6 +31,8 @@ users upgrade to this version. - :ref:`Deprecations ` +- :ref:`Known Issues ` + - :ref:`Bug Fixes ` .. warning:: @@ -630,6 +632,14 @@ Deprecations in a future release. You can use the future behavior now by passing ``return_type='axes'`` to boxplot. +.. _whatsnew_0140.knownissues: + +Known Issues +~~~~~~~~~~~~ + +- OpenPyXL 2.0.0 breaks backwards compatibility (:issue:`7196`) + + .. _whatsnew_0140.enhancements: Enhancements diff --git a/pandas/compat/openpyxl_compat.py b/pandas/compat/openpyxl_compat.py new file mode 100644 index 0000000000000..4a48cdac98dd2 --- /dev/null +++ b/pandas/compat/openpyxl_compat.py @@ -0,0 +1,26 @@ +""" +Detect incompatible version of OpenPyXL + +GH7169 +""" + +from distutils.version import LooseVersion + +start_ver = '1.6.1' +stop_ver = '2.0.0' + +def is_compat(): + """ + Detect whether the installed version of openpyxl is supported + Returns True/False if openpyxl is installed, None otherwise + """ + try: + import openpyxl + except ImportError: + return None + + ver = LooseVersion(openpyxl.__version__) + if ver < LooseVersion(start_ver) or LooseVersion(stop_ver) <= ver: + return False + + return True diff --git a/pandas/io/excel.py b/pandas/io/excel.py index e81c279c5820d..36af8b1da0e30 100644 --- a/pandas/io/excel.py +++ b/pandas/io/excel.py @@ -16,6 +16,7 @@ from pandas.core import config from pandas.core.common import pprint_thing import pandas.compat as compat +import pandas.compat.openpyxl_compat as openpyxl_compat import pandas.core.common as com from warnings import warn from distutils.version import LooseVersion @@ -617,7 +618,12 @@ def _convert_to_style(cls, style_dict): return xls_style -register_writer(_OpenpyxlWriter) + +if openpyxl_compat.is_compat(): + register_writer(_OpenpyxlWriter) +else: + warn('Installed openpyxl is not supported at this time. Use >={} and <{}.' + .format(openpyxl_compat.start_ver, openpyxl_compat.stop_ver)) class _XlwtWriter(ExcelWriter): diff --git a/pandas/io/tests/test_excel.py b/pandas/io/tests/test_excel.py index 5d4c4e4a9fdd8..ef7e57076c908 100644 --- a/pandas/io/tests/test_excel.py +++ b/pandas/io/tests/test_excel.py @@ -1,6 +1,6 @@ # pylint: disable=E1101 -from pandas.compat import u, range, map +from pandas.compat import u, range, map, openpyxl_compat from datetime import datetime, date, time import os from distutils.version import LooseVersion @@ -45,6 +45,10 @@ def _skip_if_no_openpyxl(): except ImportError: raise nose.SkipTest('openpyxl not installed, skipping') + if not openpyxl_compat.is_compat(): + raise nose.SkipTest('need %s <= openpyxl < %s, skipping' % + (openpyxl_compat.start_ver, openpyxl_compat.stop_ver)) + def _skip_if_no_xlsxwriter(): try: