diff --git a/ci/environment-dev.yaml b/ci/environment-dev.yaml
index c3d3d59f895c6..57748fef1a2e5 100644
--- a/ci/environment-dev.yaml
+++ b/ci/environment-dev.yaml
@@ -6,8 +6,8 @@ dependencies:
- Cython
- NumPy
- moto
- - pytest
- - python-dateutil
+ - pytest>=3.1
+ - python-dateutil>=2.5.0
- python=3
- pytz
- setuptools
diff --git a/ci/requirements-2.7.build b/ci/requirements-2.7.build
index 415df13179fcf..d1cc61df0a77c 100644
--- a/ci/requirements-2.7.build
+++ b/ci/requirements-2.7.build
@@ -1,5 +1,5 @@
python=2.7*
-python-dateutil=2.4.1
+python-dateutil=2.5.0
pytz=2013b
nomkl
numpy
diff --git a/ci/requirements-2.7.run b/ci/requirements-2.7.run
index a68e1d256058d..7c10b98fb6e14 100644
--- a/ci/requirements-2.7.run
+++ b/ci/requirements-2.7.run
@@ -1,11 +1,11 @@
-python-dateutil=2.4.1
+python-dateutil=2.5.0
pytz=2013b
numpy
xlwt=0.7.5
numexpr
pytables
matplotlib
-openpyxl=1.6.2
+openpyxl=2.4.0
xlrd=0.9.2
sqlalchemy=0.9.6
lxml
diff --git a/ci/requirements-2.7_COMPAT.build b/ci/requirements-2.7_COMPAT.build
index d9c932daa110b..aa767c1001196 100644
--- a/ci/requirements-2.7_COMPAT.build
+++ b/ci/requirements-2.7_COMPAT.build
@@ -1,5 +1,5 @@
python=2.7*
numpy=1.9.2
cython=0.23
-dateutil=1.5
+python-dateutil=2.5.0
pytz=2013b
diff --git a/ci/requirements-2.7_COMPAT.run b/ci/requirements-2.7_COMPAT.run
index 39bf720140733..c3daed6e6e1da 100644
--- a/ci/requirements-2.7_COMPAT.run
+++ b/ci/requirements-2.7_COMPAT.run
@@ -1,5 +1,5 @@
numpy=1.9.2
-dateutil=1.5
+python-dateutil=2.5.0
pytz=2013b
scipy=0.14.0
xlwt=0.7.5
diff --git a/ci/requirements-2.7_LOCALE.run b/ci/requirements-2.7_LOCALE.run
index 978bbf6a051c5..0a809a7dd6e5d 100644
--- a/ci/requirements-2.7_LOCALE.run
+++ b/ci/requirements-2.7_LOCALE.run
@@ -1,8 +1,8 @@
python-dateutil
-pytz=2013b
+pytz
numpy=1.9.2
xlwt=0.7.5
-openpyxl=1.6.2
+openpyxl=2.4.0
xlsxwriter=0.5.2
xlrd=0.9.2
bottleneck=1.0.0
diff --git a/ci/requirements-optional-pip.txt b/ci/requirements-optional-pip.txt
index 06b22bd8f2c63..8d4421ba2b681 100644
--- a/ci/requirements-optional-pip.txt
+++ b/ci/requirements-optional-pip.txt
@@ -1,11 +1,13 @@
# This file was autogenerated by scripts/convert_deps.py
-# Do not modify directlybeautifulsoup4
+# Do not modify directly
+beautifulsoup4
blosc
bottleneck
fastparquet
feather-format
html5lib
ipython
+ipykernel
jinja2
lxml
matplotlib
diff --git a/ci/requirements_dev.txt b/ci/requirements_dev.txt
index 2fb36b7cd70d8..e9840388203b1 100644
--- a/ci/requirements_dev.txt
+++ b/ci/requirements_dev.txt
@@ -3,8 +3,8 @@
Cython
NumPy
moto
-pytest
-python-dateutil
+pytest>=3.1
+python-dateutil>=2.5.0
pytz
setuptools
sphinx
\ No newline at end of file
diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml
index 2aee11772896f..8152af84228b8 100644
--- a/conda.recipe/meta.yaml
+++ b/conda.recipe/meta.yaml
@@ -16,13 +16,11 @@ requirements:
- cython
- numpy x.x
- setuptools
- - pytz
- - python-dateutil
run:
- python
- numpy x.x
- - python-dateutil
+ - python-dateutil >=2.5.0
- pytz
test:
diff --git a/doc/source/install.rst b/doc/source/install.rst
index 7c1fde119ceaa..ae89c64b6e91e 100644
--- a/doc/source/install.rst
+++ b/doc/source/install.rst
@@ -200,8 +200,8 @@ Dependencies
* `setuptools `__
* `NumPy `__: 1.9.0 or higher
-* `python-dateutil `__: 1.5 or higher
-* `pytz `__: Needed for time zone support
+* `python-dateutil /https://dateutil.readthedocs.io/en/stable/>`__: 2.5.0 or higher
+* `pytz `__
.. _install.recommended_dependencies:
@@ -244,8 +244,8 @@ Optional Dependencies
* For Excel I/O:
* `xlrd/xlwt `__: Excel reading (xlrd) and writing (xlwt)
- * `openpyxl `__: openpyxl version 1.6.1
- or higher (but lower than 2.0.0), or version 2.2 or higher, for writing .xlsx files (xlrd >= 0.9.0)
+ * `openpyxl `__: openpyxl version 2.4.0
+ for writing .xlsx files (xlrd >= 0.9.0)
* `XlsxWriter `__: Alternative Excel writer
* `Jinja2 `__: Template engine for conditional HTML formatting.
diff --git a/doc/source/io.rst b/doc/source/io.rst
index 2aeafd99f6e72..f96e33dbf9882 100644
--- a/doc/source/io.rst
+++ b/doc/source/io.rst
@@ -2935,7 +2935,7 @@ Writing Excel Files to Memory
+++++++++++++++++++++++++++++
Pandas supports writing Excel files to buffer-like objects such as ``StringIO`` or
-``BytesIO`` using :class:`~pandas.io.excel.ExcelWriter`. Pandas also supports Openpyxl >= 2.2.
+``BytesIO`` using :class:`~pandas.io.excel.ExcelWriter`.
.. code-block:: python
@@ -2991,9 +2991,7 @@ files if `Xlsxwriter`_ is not available.
To specify which writer you want to use, you can pass an engine keyword
argument to ``to_excel`` and to ``ExcelWriter``. The built-in engines are:
-- ``openpyxl``: This includes stable support for Openpyxl from 1.6.1. However,
- it is advised to use version 2.2 and higher, especially when working with
- styles.
+- ``openpyxl``: version 2.4 or higher is required
- ``xlsxwriter``
- ``xlwt``
diff --git a/doc/source/whatsnew/v0.22.0.txt b/doc/source/whatsnew/v0.22.0.txt
index e8f2823f32edd..5e605ecb7d8d5 100644
--- a/doc/source/whatsnew/v0.22.0.txt
+++ b/doc/source/whatsnew/v0.22.0.txt
@@ -86,9 +86,22 @@ Backwards incompatible API changes
- :func:`Series.fillna` now raises a ``TypeError`` instead of a ``ValueError`` when passed a list, tuple or DataFrame as a ``value`` (:issue:`18293`)
- :func:`pandas.DataFrame.merge` no longer casts a ``float`` column to ``object`` when merging on ``int`` and ``float`` columns (:issue:`16572`)
- The default NA value for :class:`UInt64Index` has changed from 0 to ``NaN``, which impacts methods that mask with NA, such as ``UInt64Index.where()`` (:issue:`18398`)
--
+.. _whatsnew_0220.api_breaking.deps:
+
+Dependencies have increased minimum versions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+We have updated our minimum supported versions of dependencies (:issue:`15184`).
+If installed, we now require:
+ +-----------------+-----------------+----------+
+ | Package | Minimum Version | Required |
+ +=================+=================+==========+
+ | python-dateutil | 2.5.0 | X |
+ +-----------------+-----------------+----------+
+ | openpyxl | 2.4.0 | |
+ +-----------------+-----------------+----------+
diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py
index a615e098135a9..2deb29dabe764 100644
--- a/pandas/compat/__init__.py
+++ b/pandas/compat/__init__.py
@@ -396,25 +396,13 @@ def raise_with_traceback(exc, traceback=Ellipsis):
If traceback is not passed, uses sys.exc_info() to get traceback."""
-# http://stackoverflow.com/questions/4126348
-# Thanks to @martineau at SO
-
+# dateutil minimum version
import dateutil
-if PY2 and LooseVersion(dateutil.__version__) == '2.0':
- # dateutil brokenness
- raise Exception('dateutil 2.0 incompatible with Python 2.x, you must '
- 'install version 1.5 or 2.1+!')
-
+if LooseVersion(dateutil.__version__) < '2.5':
+ raise ImportError('dateutil 2.5.0 is the minimum required version')
from dateutil import parser as _date_parser
-if LooseVersion(dateutil.__version__) < '2.0':
-
- @functools.wraps(_date_parser.parse)
- def parse_date(timestr, *args, **kwargs):
- timestr = bytes(timestr)
- return _date_parser.parse(timestr, *args, **kwargs)
-else:
- parse_date = _date_parser.parse
+parse_date = _date_parser.parse
# https://github.com/pandas-dev/pandas/pull/9123
diff --git a/pandas/compat/openpyxl_compat.py b/pandas/compat/openpyxl_compat.py
deleted file mode 100644
index 87cf52cf00fef..0000000000000
--- a/pandas/compat/openpyxl_compat.py
+++ /dev/null
@@ -1,35 +0,0 @@
-"""
-Detect incompatible version of OpenPyXL
-
-GH7169
-"""
-
-from distutils.version import LooseVersion
-
-start_ver = '1.6.1'
-stop_ver = '2.0.0'
-
-
-def is_compat(major_ver=1):
- """Detect whether the installed version of openpyxl is supported
-
- Parameters
- ----------
- ver : int
- 1 requests compatibility status among the 1.x.y series
- 2 requests compatibility status of 2.0.0 and later
- Returns
- -------
- compat : bool
- ``True`` if openpyxl is installed and is a compatible version.
- ``False`` otherwise.
- """
- import openpyxl
- ver = LooseVersion(openpyxl.__version__)
- if major_ver == 1:
- return LooseVersion(start_ver) <= ver < LooseVersion(stop_ver)
- elif major_ver == 2:
- return LooseVersion(stop_ver) <= ver
- else:
- raise ValueError('cannot test for openpyxl compatibility with ver {0}'
- .format(major_ver))
diff --git a/pandas/io/excel.py b/pandas/io/excel.py
index fec916dc52d20..882130bedcbf0 100644
--- a/pandas/io/excel.py
+++ b/pandas/io/excel.py
@@ -28,7 +28,6 @@
from pandas.core import config
from pandas.io.formats.printing import pprint_thing
import pandas.compat as compat
-import pandas.compat.openpyxl_compat as openpyxl_compat
from warnings import warn
from distutils.version import LooseVersion
from pandas.util._decorators import Appender, deprecate_kwarg
@@ -185,22 +184,6 @@ def _get_default_writer(ext):
def get_writer(engine_name):
- if engine_name == 'openpyxl':
- try:
- import openpyxl
-
- # with version-less openpyxl engine
- # make sure we make the intelligent choice for the user
- if LooseVersion(openpyxl.__version__) < '2.0.0':
- return _writers['openpyxl1']
- elif LooseVersion(openpyxl.__version__) < '2.2.0':
- return _writers['openpyxl20']
- else:
- return _writers['openpyxl22']
- except ImportError:
- # fall through to normal exception handling below
- pass
-
try:
return _writers[engine_name]
except KeyError:
@@ -828,20 +811,15 @@ def close(self):
return self.save()
-class _Openpyxl1Writer(ExcelWriter):
- engine = 'openpyxl1'
+class _OpenpyxlWriter(ExcelWriter):
+ engine = 'openpyxl'
supported_extensions = ('.xlsx', '.xlsm')
- openpyxl_majorver = 1
def __init__(self, path, engine=None, **engine_kwargs):
- if not openpyxl_compat.is_compat(major_ver=self.openpyxl_majorver):
- raise ValueError('Installed openpyxl is not supported at this '
- 'time. Use {majorver}.x.y.'
- .format(majorver=self.openpyxl_majorver))
# Use the openpyxl module as the Excel writer.
from openpyxl.workbook import Workbook
- super(_Openpyxl1Writer, self).__init__(path, **engine_kwargs)
+ super(_OpenpyxlWriter, self).__init__(path, **engine_kwargs)
# Create workbook object with default optimized_write=True.
self.book = Workbook()
@@ -861,72 +839,6 @@ def save(self):
"""
return self.book.save(self.path)
- def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0,
- freeze_panes=None):
- # Write the frame cells using openpyxl.
- from openpyxl.cell import get_column_letter
-
- sheet_name = self._get_sheet_name(sheet_name)
-
- if sheet_name in self.sheets:
- wks = self.sheets[sheet_name]
- else:
- wks = self.book.create_sheet()
- wks.title = sheet_name
- self.sheets[sheet_name] = wks
-
- for cell in cells:
- colletter = get_column_letter(startcol + cell.col + 1)
- xcell = wks.cell("{col}{row}".format(col=colletter,
- row=startrow + cell.row + 1))
- if (isinstance(cell.val, compat.string_types) and
- xcell.data_type_for_value(cell.val) != xcell.TYPE_STRING):
- xcell.set_value_explicit(cell.val)
- else:
- xcell.value = _conv_value(cell.val)
- style = None
- if cell.style:
- style = self._convert_to_style(cell.style)
- for field in style.__fields__:
- xcell.style.__setattr__(field,
- style.__getattribute__(field))
-
- if isinstance(cell.val, datetime):
- xcell.style.number_format.format_code = self.datetime_format
- elif isinstance(cell.val, date):
- xcell.style.number_format.format_code = self.date_format
-
- if cell.mergestart is not None and cell.mergeend is not None:
- cletterstart = get_column_letter(startcol + cell.col + 1)
- cletterend = get_column_letter(startcol + cell.mergeend + 1)
-
- wks.merge_cells('{start}{row}:{end}{mergestart}'
- .format(start=cletterstart,
- row=startrow + cell.row + 1,
- end=cletterend,
- mergestart=startrow +
- cell.mergestart + 1))
-
- # Excel requires that the format of the first cell in a merged
- # range is repeated in the rest of the merged range.
- if style:
- first_row = startrow + cell.row + 1
- last_row = startrow + cell.mergestart + 1
- first_col = startcol + cell.col + 1
- last_col = startcol + cell.mergeend + 1
-
- for row in range(first_row, last_row + 1):
- for col in range(first_col, last_col + 1):
- if row == first_row and col == first_col:
- # Ignore first cell. It is already handled.
- continue
- colletter = get_column_letter(col)
- xcell = wks.cell("{col}{row}"
- .format(col=colletter, row=row))
- for field in style.__fields__:
- xcell.style.__setattr__(
- field, style.__getattribute__(field))
-
@classmethod
def _convert_to_style(cls, style_dict):
"""
@@ -948,88 +860,6 @@ def _convert_to_style(cls, style_dict):
return xls_style
-
-register_writer(_Openpyxl1Writer)
-
-
-class _OpenpyxlWriter(_Openpyxl1Writer):
- engine = 'openpyxl'
-
-
-register_writer(_OpenpyxlWriter)
-
-
-class _Openpyxl20Writer(_Openpyxl1Writer):
- """
- Note: Support for OpenPyxl v2 is currently EXPERIMENTAL (GH7565).
- """
- engine = 'openpyxl20'
- openpyxl_majorver = 2
-
- def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0,
- freeze_panes=None):
- # Write the frame cells using openpyxl.
- from openpyxl.cell import get_column_letter
-
- sheet_name = self._get_sheet_name(sheet_name)
-
- if sheet_name in self.sheets:
- wks = self.sheets[sheet_name]
- else:
- wks = self.book.create_sheet()
- wks.title = sheet_name
- self.sheets[sheet_name] = wks
-
- for cell in cells:
- colletter = get_column_letter(startcol + cell.col + 1)
- xcell = wks["{col}{row}"
- .format(col=colletter, row=startrow + cell.row + 1)]
- xcell.value = _conv_value(cell.val)
- style_kwargs = {}
-
- # Apply format codes before cell.style to allow override
- if isinstance(cell.val, datetime):
- style_kwargs.update(self._convert_to_style_kwargs({
- 'number_format': {'format_code': self.datetime_format}}))
- elif isinstance(cell.val, date):
- style_kwargs.update(self._convert_to_style_kwargs({
- 'number_format': {'format_code': self.date_format}}))
-
- if cell.style:
- style_kwargs.update(self._convert_to_style_kwargs(cell.style))
-
- if style_kwargs:
- xcell.style = xcell.style.copy(**style_kwargs)
-
- if cell.mergestart is not None and cell.mergeend is not None:
- cletterstart = get_column_letter(startcol + cell.col + 1)
- cletterend = get_column_letter(startcol + cell.mergeend + 1)
-
- wks.merge_cells('{start}{row}:{end}{mergestart}'
- .format(start=cletterstart,
- row=startrow + cell.row + 1,
- end=cletterend,
- mergestart=startrow +
- cell.mergestart + 1))
-
- # Excel requires that the format of the first cell in a merged
- # range is repeated in the rest of the merged range.
- if style_kwargs:
- first_row = startrow + cell.row + 1
- last_row = startrow + cell.mergestart + 1
- first_col = startcol + cell.col + 1
- last_col = startcol + cell.mergeend + 1
-
- for row in range(first_row, last_row + 1):
- for col in range(first_col, last_col + 1):
- if row == first_row and col == first_col:
- # Ignore first cell. It is already handled.
- continue
- colletter = get_column_letter(col)
- xcell = wks["{col}{row}"
- .format(col=colletter, row=row)]
- xcell.style = xcell.style.copy(**style_kwargs)
-
@classmethod
def _convert_to_style_kwargs(cls, style_dict):
"""
@@ -1341,13 +1171,7 @@ def _convert_to_number_format(cls, number_format_dict):
-------
number_format : str
"""
- try:
- # >= 2.0.0 < 2.1.0
- from openpyxl.styles import NumberFormat
- return NumberFormat(**number_format_dict)
- except:
- # >= 2.1.0
- return number_format_dict['format_code']
+ return number_format_dict['format_code']
@classmethod
def _convert_to_protection(cls, protection_dict):
@@ -1367,17 +1191,6 @@ def _convert_to_protection(cls, protection_dict):
return Protection(**protection_dict)
-
-register_writer(_Openpyxl20Writer)
-
-
-class _Openpyxl22Writer(_Openpyxl20Writer):
- """
- Note: Support for OpenPyxl v2.2 is currently EXPERIMENTAL (GH7565).
- """
- engine = 'openpyxl22'
- openpyxl_majorver = 2
-
def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0,
freeze_panes=None):
# Write the frame cells using openpyxl.
@@ -1443,7 +1256,7 @@ def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0,
setattr(xcell, k, v)
-register_writer(_Openpyxl22Writer)
+register_writer(_OpenpyxlWriter)
class _XlwtWriter(ExcelWriter):
diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py
index a1287c3102b77..6c72e65b1021c 100644
--- a/pandas/tests/indexes/datetimes/test_tools.py
+++ b/pandas/tests/indexes/datetimes/test_tools.py
@@ -1160,9 +1160,9 @@ class TestDatetimeParsingWrappers(object):
@pytest.mark.parametrize('cache', [True, False])
def test_parsers(self, cache):
+ # dateutil >= 2.5.0 defaults to yearfirst=True
# https://github.com/dateutil/dateutil/issues/217
- import dateutil
- yearfirst = dateutil.__version__ >= LooseVersion('2.5.0')
+ yearfirst = True
cases = {'2011-01-01': datetime(2011, 1, 1),
'2Q2005': datetime(2005, 4, 1),
diff --git a/pandas/tests/io/test_excel.py b/pandas/tests/io/test_excel.py
index d33136a86faad..96117b3c21a9b 100644
--- a/pandas/tests/io/test_excel.py
+++ b/pandas/tests/io/test_excel.py
@@ -1,6 +1,4 @@
# pylint: disable=E1101
-import functools
-import operator
import os
import sys
import warnings
@@ -17,12 +15,12 @@
import pandas as pd
import pandas.util.testing as tm
from pandas import DataFrame, Index, MultiIndex
-from pandas.compat import u, range, map, openpyxl_compat, BytesIO, iteritems
+from pandas.compat import u, range, map, BytesIO, iteritems
from pandas.core.config import set_option, get_option
from pandas.io.common import URLError
from pandas.io.excel import (
- ExcelFile, ExcelWriter, read_excel, _XlwtWriter, _Openpyxl1Writer,
- _Openpyxl20Writer, _Openpyxl22Writer, register_writer, _XlsxWriter
+ ExcelFile, ExcelWriter, read_excel, _XlwtWriter, _OpenpyxlWriter,
+ register_writer, _XlsxWriter
)
from pandas.io.formats.excel import ExcelFormatter
from pandas.io.parsers import read_csv
@@ -1926,207 +1924,10 @@ def test_path_localpath(self):
tm.assert_frame_equal(df, result)
-def raise_wrapper(major_ver):
- def versioned_raise_wrapper(orig_method):
- @functools.wraps(orig_method)
- def wrapped(self, *args, **kwargs):
- _skip_if_no_openpyxl()
- if openpyxl_compat.is_compat(major_ver=major_ver):
- orig_method(self, *args, **kwargs)
- else:
- msg = (r'Installed openpyxl is not supported at this '
- r'time\. Use.+')
- with tm.assert_raises_regex(ValueError, msg):
- orig_method(self, *args, **kwargs)
- return wrapped
- return versioned_raise_wrapper
-
-
-def raise_on_incompat_version(major_ver):
- def versioned_raise_on_incompat_version(cls):
- methods = filter(operator.methodcaller(
- 'startswith', 'test_'), dir(cls))
- for method in methods:
- setattr(cls, method, raise_wrapper(
- major_ver)(getattr(cls, method)))
- return cls
- return versioned_raise_on_incompat_version
-
-
-@raise_on_incompat_version(1)
class TestOpenpyxlTests(ExcelWriterBase):
+ engine_name = 'openpyxl'
ext = '.xlsx'
- engine_name = 'openpyxl1'
- check_skip = staticmethod(lambda *args, **kwargs: None)
-
- def test_to_excel_styleconverter(self):
- _skip_if_no_openpyxl()
- if not openpyxl_compat.is_compat(major_ver=1):
- pytest.skip('incompatible openpyxl version')
-
- import openpyxl
-
- hstyle = {"font": {"bold": True},
- "borders": {"top": "thin",
- "right": "thin",
- "bottom": "thin",
- "left": "thin"},
- "alignment": {"horizontal": "center", "vertical": "top"}}
-
- xlsx_style = _Openpyxl1Writer._convert_to_style(hstyle)
- assert xlsx_style.font.bold
- assert (openpyxl.style.Border.BORDER_THIN ==
- xlsx_style.borders.top.border_style)
- assert (openpyxl.style.Border.BORDER_THIN ==
- xlsx_style.borders.right.border_style)
- assert (openpyxl.style.Border.BORDER_THIN ==
- xlsx_style.borders.bottom.border_style)
- assert (openpyxl.style.Border.BORDER_THIN ==
- xlsx_style.borders.left.border_style)
- assert (openpyxl.style.Alignment.HORIZONTAL_CENTER ==
- xlsx_style.alignment.horizontal)
- assert (openpyxl.style.Alignment.VERTICAL_TOP ==
- xlsx_style.alignment.vertical)
-
-
-def skip_openpyxl_gt21(cls):
- """Skip test case if openpyxl >= 2.2"""
-
- @classmethod
- def setup_class(cls):
- _skip_if_no_openpyxl()
- import openpyxl
- ver = openpyxl.__version__
- if (not (LooseVersion(ver) >= LooseVersion('2.0.0') and
- LooseVersion(ver) < LooseVersion('2.2.0'))):
- pytest.skip("openpyxl %s >= 2.2" % str(ver))
-
- cls.setup_class = setup_class
- return cls
-
-
-@raise_on_incompat_version(2)
-@skip_openpyxl_gt21
-class TestOpenpyxl20Tests(ExcelWriterBase):
- ext = '.xlsx'
- engine_name = 'openpyxl20'
- check_skip = staticmethod(lambda *args, **kwargs: None)
-
- def test_to_excel_styleconverter(self):
- import openpyxl
- from openpyxl import styles
-
- hstyle = {
- "font": {
- "color": '00FF0000',
- "bold": True,
- },
- "borders": {
- "top": "thin",
- "right": "thin",
- "bottom": "thin",
- "left": "thin",
- },
- "alignment": {
- "horizontal": "center",
- "vertical": "top",
- },
- "fill": {
- "patternType": 'solid',
- 'fgColor': {
- 'rgb': '006666FF',
- 'tint': 0.3,
- },
- },
- "number_format": {
- "format_code": "0.00"
- },
- "protection": {
- "locked": True,
- "hidden": False,
- },
- }
-
- font_color = styles.Color('00FF0000')
- font = styles.Font(bold=True, color=font_color)
- side = styles.Side(style=styles.borders.BORDER_THIN)
- border = styles.Border(top=side, right=side, bottom=side, left=side)
- alignment = styles.Alignment(horizontal='center', vertical='top')
- fill_color = styles.Color(rgb='006666FF', tint=0.3)
- fill = styles.PatternFill(patternType='solid', fgColor=fill_color)
-
- # ahh openpyxl API changes
- ver = openpyxl.__version__
- if ver >= LooseVersion('2.0.0') and ver < LooseVersion('2.1.0'):
- number_format = styles.NumberFormat(format_code='0.00')
- else:
- number_format = '0.00' # XXX: Only works with openpyxl-2.1.0
-
- protection = styles.Protection(locked=True, hidden=False)
-
- kw = _Openpyxl20Writer._convert_to_style_kwargs(hstyle)
- assert kw['font'] == font
- assert kw['border'] == border
- assert kw['alignment'] == alignment
- assert kw['fill'] == fill
- assert kw['number_format'] == number_format
- assert kw['protection'] == protection
-
- def test_write_cells_merge_styled(self):
- from pandas.io.formats.excel import ExcelCell
- from openpyxl import styles
-
- sheet_name = 'merge_styled'
-
- sty_b1 = {'font': {'color': '00FF0000'}}
- sty_a2 = {'font': {'color': '0000FF00'}}
-
- initial_cells = [
- ExcelCell(col=1, row=0, val=42, style=sty_b1),
- ExcelCell(col=0, row=1, val=99, style=sty_a2),
- ]
-
- sty_merged = {'font': {'color': '000000FF', 'bold': True}}
- sty_kwargs = _Openpyxl20Writer._convert_to_style_kwargs(sty_merged)
- openpyxl_sty_merged = styles.Style(**sty_kwargs)
- merge_cells = [
- ExcelCell(col=0, row=0, val='pandas',
- mergestart=1, mergeend=1, style=sty_merged),
- ]
-
- with ensure_clean('.xlsx') as path:
- writer = _Openpyxl20Writer(path)
- writer.write_cells(initial_cells, sheet_name=sheet_name)
- writer.write_cells(merge_cells, sheet_name=sheet_name)
-
- wks = writer.sheets[sheet_name]
- xcell_b1 = wks['B1']
- xcell_a2 = wks['A2']
- assert xcell_b1.style == openpyxl_sty_merged
- assert xcell_a2.style == openpyxl_sty_merged
-
-
-def skip_openpyxl_lt22(cls):
- """Skip test case if openpyxl < 2.2"""
-
- @classmethod
- def setup_class(cls):
- _skip_if_no_openpyxl()
- import openpyxl
- ver = openpyxl.__version__
- if LooseVersion(ver) < LooseVersion('2.2.0'):
- pytest.skip("openpyxl %s < 2.2" % str(ver))
-
- cls.setup_class = setup_class
- return cls
-
-
-@raise_on_incompat_version(2)
-@skip_openpyxl_lt22
-class TestOpenpyxl22Tests(ExcelWriterBase):
- ext = '.xlsx'
- engine_name = 'openpyxl22'
- check_skip = staticmethod(lambda *args, **kwargs: None)
+ check_skip = staticmethod(_skip_if_no_openpyxl)
def test_to_excel_styleconverter(self):
from openpyxl import styles
@@ -2174,7 +1975,7 @@ def test_to_excel_styleconverter(self):
protection = styles.Protection(locked=True, hidden=False)
- kw = _Openpyxl22Writer._convert_to_style_kwargs(hstyle)
+ kw = _OpenpyxlWriter._convert_to_style_kwargs(hstyle)
assert kw['font'] == font
assert kw['border'] == border
assert kw['alignment'] == alignment
@@ -2183,9 +1984,6 @@ def test_to_excel_styleconverter(self):
assert kw['protection'] == protection
def test_write_cells_merge_styled(self):
- if not openpyxl_compat.is_compat(major_ver=2):
- pytest.skip('incompatible openpyxl version')
-
from pandas.io.formats.excel import ExcelCell
sheet_name = 'merge_styled'
@@ -2199,7 +1997,7 @@ def test_write_cells_merge_styled(self):
]
sty_merged = {'font': {'color': '000000FF', 'bold': True}}
- sty_kwargs = _Openpyxl22Writer._convert_to_style_kwargs(sty_merged)
+ sty_kwargs = _OpenpyxlWriter._convert_to_style_kwargs(sty_merged)
openpyxl_sty_merged = sty_kwargs['font']
merge_cells = [
ExcelCell(col=0, row=0, val='pandas',
@@ -2207,7 +2005,7 @@ def test_write_cells_merge_styled(self):
]
with ensure_clean('.xlsx') as path:
- writer = _Openpyxl22Writer(path)
+ writer = _OpenpyxlWriter(path)
writer.write_cells(initial_cells, sheet_name=sheet_name)
writer.write_cells(merge_cells, sheet_name=sheet_name)
@@ -2322,7 +2120,7 @@ def test_column_format(self):
try:
read_num_format = cell.number_format
- except:
+ except Exception:
read_num_format = cell.style.number_format._format_code
assert read_num_format == num_format
@@ -2366,9 +2164,7 @@ def test_ExcelWriter_dispatch(self):
writer_klass = _XlsxWriter
except ImportError:
_skip_if_no_openpyxl()
- if not openpyxl_compat.is_compat(major_ver=1):
- pytest.skip('incompatible openpyxl version')
- writer_klass = _Openpyxl1Writer
+ writer_klass = _OpenpyxlWriter
with ensure_clean('.xlsx') as path:
writer = ExcelWriter(path)
@@ -2461,10 +2257,6 @@ def custom_converter(css):
pytest.importorskip('jinja2')
pytest.importorskip(engine)
- if engine == 'openpyxl' and openpyxl_compat.is_compat(major_ver=1):
- pytest.xfail('openpyxl1 does not support some openpyxl2-compatible '
- 'style dicts')
-
# Prepare spreadsheets
df = DataFrame(np.random.randn(10, 3))
@@ -2482,9 +2274,6 @@ def custom_converter(css):
# For other engines, we only smoke test
return
openpyxl = pytest.importorskip('openpyxl')
- if not openpyxl_compat.is_compat(major_ver=2):
- pytest.skip('incompatible openpyxl version')
-
wb = openpyxl.load_workbook(path)
# (1) compare DataFrame.to_excel and Styler.to_excel when unstyled
diff --git a/pandas/tests/scalar/test_timestamp.py b/pandas/tests/scalar/test_timestamp.py
index dab508de335c4..e23911e8d2003 100644
--- a/pandas/tests/scalar/test_timestamp.py
+++ b/pandas/tests/scalar/test_timestamp.py
@@ -16,7 +16,7 @@
import pandas.util.testing as tm
from pandas.tseries import offsets, frequencies
-from pandas._libs.tslibs.timezones import get_timezone
+from pandas._libs.tslibs.timezones import get_timezone, dateutil_gettz as gettz
from pandas._libs.tslibs import conversion, period
from pandas.compat import long, PY3
@@ -359,9 +359,7 @@ def test_conversion(self):
'2014-01-01 00:00:00.000000001'])
def test_repr(self, date, freq):
# dateutil zone change (only matters for repr)
- if (dateutil.__version__ >= LooseVersion('2.3') and
- (dateutil.__version__ <= LooseVersion('2.4.0') or
- dateutil.__version__ >= LooseVersion('2.6.0'))):
+ if dateutil.__version__ >= LooseVersion('2.6.0'):
timezones = ['UTC', 'Asia/Tokyo', 'US/Eastern',
'dateutil/US/Pacific']
else:
@@ -1381,7 +1379,6 @@ def test_timestamp_to_datetime_explicit_pytz(self):
def test_timestamp_to_datetime_explicit_dateutil(self):
tm._skip_if_windows_python_3()
- from pandas._libs.tslibs.timezones import dateutil_gettz as gettz
stamp = Timestamp('20090415', tz=gettz('US/Eastern'), freq='D')
dtval = stamp.to_pydatetime()
assert stamp == dtval
diff --git a/setup.py b/setup.py
index ba948abf4302b..57131255884de 100755
--- a/setup.py
+++ b/setup.py
@@ -19,8 +19,6 @@
import versioneer
cmdclass = versioneer.get_cmdclass()
-PY3 = sys.version_info[0] >= 3
-
def is_platform_windows():
return sys.platform == 'win32' or sys.platform == 'cygwin'
@@ -46,7 +44,7 @@ def is_platform_mac():
min_numpy_ver = '1.9.0'
setuptools_kwargs = {
'install_requires': [
- 'python-dateutil >= 2' if PY3 else 'python-dateutil',
+ 'python-dateutil >= 2.5.0',
'pytz >= 2011k',
'numpy >= {numpy_ver}'.format(numpy_ver=min_numpy_ver),
],