Skip to content

Commit

Permalink
DEPS: require openpyxl >= 2.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
jreback committed Nov 10, 2017
1 parent 34749e9 commit 07ad7af
Show file tree
Hide file tree
Showing 7 changed files with 22 additions and 453 deletions.
2 changes: 1 addition & 1 deletion ci/requirements-2.7.run
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ xlwt=0.7.5
numexpr
pytables
matplotlib
openpyxl=1.6.2
openpyxl=2.3.0
xlrd=0.9.2
sqlalchemy=0.9.6
lxml
Expand Down
2 changes: 1 addition & 1 deletion ci/requirements-2.7_LOCALE.run
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ python-dateutil
pytz=2013b
numpy=1.9.2
xlwt=0.7.5
openpyxl=1.6.2
openpyxl=2.5.0
xlsxwriter=0.5.2
xlrd=0.9.2
bottleneck=1.0.0
Expand Down
4 changes: 2 additions & 2 deletions doc/source/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@ Optional Dependencies
* For Excel I/O:

* `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 (but lower than 2.0.0), or version 2.2 or higher, for writing .xlsx files (xlrd >= 0.9.0)
* `openpyxl <http://https://openpyxl.readthedocs.io/en/default/>`__: openpyxl version 2.3.0
for writing .xlsx files (xlrd >= 0.9.0)
* `XlsxWriter <https://pypi.python.org/pypi/XlsxWriter>`__: Alternative Excel writer

* `Jinja2 <http://jinja.pocoo.org/>`__: Template engine for conditional HTML formatting.
Expand Down
4 changes: 3 additions & 1 deletion doc/source/whatsnew/v0.22.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ Backwards incompatible API changes
Dependencies have increased minimum versions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

We have updated our minimum supported versions of dependencies ().
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.3.0 | |
+-----------------+-----------------+----------+

-
-
Expand Down
35 changes: 0 additions & 35 deletions pandas/compat/openpyxl_compat.py

This file was deleted.

197 changes: 5 additions & 192 deletions pandas/io/excel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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()
Expand All @@ -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):
"""
Expand All @@ -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):
"""
Expand Down Expand Up @@ -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):
Expand All @@ -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.
Expand Down Expand Up @@ -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):
Expand Down
Loading

0 comments on commit 07ad7af

Please sign in to comment.