![](https://seccdn.libravatar.org/avatar/e2145bc5cf53dda95c308a3c75e8fef3.jpg?s=120&d=mm&r=g)
Hello community, here is the log from the commit of package python-openpyxl for openSUSE:Factory checked in at 2018-01-10 23:38:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-openpyxl (Old) and /work/SRC/openSUSE:Factory/.python-openpyxl.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-openpyxl" Wed Jan 10 23:38:14 2018 rev:7 rq:563264 version:2.4.9 Changes: -------- --- /work/SRC/openSUSE:Factory/python-openpyxl/python-openpyxl.changes 2017-08-28 15:15:00.918851304 +0200 +++ /work/SRC/openSUSE:Factory/.python-openpyxl.new/python-openpyxl.changes 2018-01-10 23:38:14.823355564 +0100 @@ -1,0 +2,22 @@ +Wed Jan 10 10:13:52 UTC 2018 - kbabioch@suse.com + +- update to 2.4.9: + + Bugfixes + * #809 Incomplete documentation of copy_worksheet method + * #811 Scoped definedNames not removed when worksheet is deleted + * #824 Raise an exception if a chart is used in multiple sheets + * #842 Non-ASCII table column headings cause an exception in Python 2 + * #846 Conditional formats not supported in write-only mode + * #849 Conditional formats with no sqref cause an exception + * #859 Headers that start with a number conflict with font size + * #902 TableStyleElements don’t always have a condtional format + * #908 Read-only mode sometimes returns too many cells + + Pull requests + * #179 Cells kept in a set + * #180 Support for Workbook protection + * #182 Read support for page breaks + * #183 Improve documentation of copy_worksheet method + * #198 Fix for #908 +- updated summary + +------------------------------------------------------------------- @@ -61 +83 @@ - * Potential XXE vulerability + * Potential XXE vulerability (CVE-2017-5992 boo#1025592) Old: ---- openpyxl-2.4.8.tar.gz New: ---- openpyxl-2.4.9.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-openpyxl.spec ++++++ --- /var/tmp/diff_new_pack.8CEmUm/_old 2018-01-10 23:38:15.347330982 +0100 +++ /var/tmp/diff_new_pack.8CEmUm/_new 2018-01-10 23:38:15.351330795 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-openpyxl # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,9 +18,9 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-openpyxl -Version: 2.4.8 +Version: 2.4.9 Release: 0 -Summary: A Python library to read/write Excel 2007 xlsx/xlsm files +Summary: A Python library to read/write Excel 2010 xlsx/xlsm files License: MIT and Python-2.0 Group: Development/Languages/Python Url: http://openpyxl.readthedocs.org ++++++ openpyxl-2.4.8.tar.gz -> openpyxl-2.4.9.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/AUTHORS.rst new/openpyxl-2.4.9/AUTHORS.rst --- old/openpyxl-2.4.8/AUTHORS.rst 2017-03-16 19:05:43.000000000 +0100 +++ new/openpyxl-2.4.9/AUTHORS.rst 2017-10-19 14:24:20.000000000 +0200 @@ -61,6 +61,7 @@ * James Smagala * Wolfgane Scherer * Joseph Tate +* Gar Thompson * Dieter Vandenbussche * Paul Van Der Linden * Gerald Van Huffelen diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/PKG-INFO new/openpyxl-2.4.9/PKG-INFO --- old/openpyxl-2.4.8/PKG-INFO 2017-05-30 19:33:09.000000000 +0200 +++ new/openpyxl-2.4.9/PKG-INFO 2017-10-19 14:24:46.000000000 +0200 @@ -1,11 +1,12 @@ Metadata-Version: 1.1 Name: openpyxl -Version: 2.4.8 +Version: 2.4.9 Summary: A Python library to read/write Excel 2010 xlsx/xlsm files Home-page: https://openpyxl.readthedocs.io Author: See AUTHORS Author-email: charlie.clark@clark-consulting.eu License: MIT/Expat +Description-Content-Type: UNKNOWN Description: openpyxl ======== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/.constants.json new/openpyxl-2.4.9/openpyxl/.constants.json --- old/openpyxl-2.4.8/openpyxl/.constants.json 2017-05-30 19:32:53.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl/.constants.json 2017-10-19 14:24:20.000000000 +0200 @@ -4,5 +4,5 @@ "__license__": "MIT/Expat", "__maintainer_email__": "openpyxl-users@googlegroups.com", "__url__": "https://openpyxl.readthedocs.io", - "__version__": "2.4.8" + "__version__": "2.4.9" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/cell/read_only.py new/openpyxl-2.4.9/openpyxl/cell/read_only.py --- old/openpyxl-2.4.8/openpyxl/cell/read_only.py 2017-03-16 19:05:43.000000000 +0100 +++ new/openpyxl-2.4.9/openpyxl/cell/read_only.py 2017-10-19 14:24:20.000000000 +0200 @@ -45,6 +45,10 @@ def __ne__(self, other): return not self.__eq__(other) + + def __repr__(self): + return "<ReadOnlyCell {0!r}.{1}>".format(self.parent.title, self.coordinate) + @property def shared_strings(self): return self.parent.shared_strings @@ -145,4 +149,7 @@ data_type = 'n' + def __repr__(self): + return "<EmptyCell>" + EMPTY_CELL = EmptyCell() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/formatting/formatting.py new/openpyxl-2.4.9/openpyxl/formatting/formatting.py --- old/openpyxl-2.4.8/openpyxl/formatting/formatting.py 2017-05-30 19:20:53.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl/formatting/formatting.py 2017-10-19 14:24:20.000000000 +0200 @@ -18,7 +18,7 @@ tagname = "conditionalFormatting" - sqref = String() + sqref = String(allow_none=True) pivot = Bool(allow_none=True) cfRule = Sequence(expected_type=Rule) rules = Alias("cfRule") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/packaging/workbook.py new/openpyxl-2.4.9/openpyxl/packaging/workbook.py --- old/openpyxl-2.4.8/openpyxl/packaging/workbook.py 2017-05-30 19:20:53.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl/packaging/workbook.py 2017-10-19 14:24:20.000000000 +0200 @@ -66,6 +66,8 @@ package.definedNames._cleanup() self.wb.defined_names = package.definedNames + self.wb.security = package.workbookProtection + def find_sheets(self): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/reader/worksheet.py new/openpyxl-2.4.9/openpyxl/reader/worksheet.py --- old/openpyxl-2.4.8/openpyxl/reader/worksheet.py 2017-05-30 19:20:53.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl/reader/worksheet.py 2017-10-19 14:24:20.000000000 +0200 @@ -23,6 +23,7 @@ from openpyxl.worksheet.hyperlink import Hyperlink from openpyxl.worksheet.merge import MergeCells from openpyxl.worksheet.page import PageMargins, PrintOptions, PrintPageSetup +from openpyxl.worksheet.pagebreak import PageBreak from openpyxl.worksheet.protection import SheetProtection from openpyxl.worksheet.views import SheetViewList from openpyxl.worksheet.datavalidation import DataValidationList @@ -112,7 +113,8 @@ #'{%s}sheet/{%s}sortState' % (SHEET_MAIN_NS, SHEET_MAIN_NS): ('sort_state', SortState), '{%s}sheetPr' % SHEET_MAIN_NS: ('sheet_properties', WorksheetProperties), '{%s}sheetViews' % SHEET_MAIN_NS: ('views', SheetViewList), - '{%s}sheetFormatPr' % SHEET_MAIN_NS: ('sheet_format', SheetFormatProperties) + '{%s}sheetFormatPr' % SHEET_MAIN_NS: ('sheet_format', SheetFormatProperties), + '{%s}rowBreaks' % SHEET_MAIN_NS: ('page_breaks', PageBreak), } tags = dispatcher.keys() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/styles/table.py new/openpyxl-2.4.9/openpyxl/styles/table.py --- old/openpyxl-2.4.8/openpyxl/styles/table.py 2017-05-30 19:14:29.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl/styles/table.py 2017-10-19 14:24:20.000000000 +0200 @@ -15,6 +15,8 @@ class TableStyleElement(Serialisable): + tagname = "tableStyleElement" + type = Set(values=(['wholeTable', 'headerRow', 'totalRow', 'firstColumn', 'lastColumn', 'firstRowStripe', 'secondRowStripe', 'firstColumnStripe', 'secondColumnStripe', 'firstHeaderCell', 'lastHeaderCell', @@ -25,7 +27,7 @@ 'thirdColumnSubheading', 'firstRowSubheading', 'secondRowSubheading', 'thirdRowSubheading', 'pageFieldLabels', 'pageFieldValues'])) size = Integer(allow_none=True) - dxfId = Integer() + dxfId = Integer(allow_none=True) def __init__(self, type=None, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/utils/cell.py new/openpyxl-2.4.9/openpyxl/utils/cell.py --- old/openpyxl-2.4.8/openpyxl/utils/cell.py 2017-05-30 19:20:53.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl/utils/cell.py 2017-10-19 14:24:20.000000000 +0200 @@ -57,13 +57,19 @@ def absolute_coordinate(coord_string): """Convert a coordinate to an absolute coordinate string (B12 -> $B$12)""" m = ABSOLUTE_RE.match(coord_string.upper()) - if m: - if all(m.groups()[-2:]): - fmt = "${0}${1}:${3}${4}" - else: - fmt = "${0}${1}" - return fmt.format(*m.groups()) - raise ValueError("Value is not a valid coordinate range") + if not m: + raise ValueError("Value is not a valid coordinate range") + + d = m.groupdict('') + for k, v in d.items(): + if v: + d[k] = "${0}".format(v) + + if d['max_col'] or d['max_row']: + fmt = "{min_col}{min_row}:{max_col}{max_row}" + else: + fmt = "{min_col}{min_row}" + return fmt.format(**d) def _get_column_letter(col_idx): @@ -198,7 +204,10 @@ """ Add quotes around sheetnames if they contain spaces. """ + if "'" in sheetname: + sheetname = sheetname.replace("'", "''") if (" " in sheetname - or "-" in sheetname): + or "-" in sheetname + or "'" in sheetname): sheetname = u"'{0}'".format(sheetname) return sheetname diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/utils/exceptions.py new/openpyxl-2.4.9/openpyxl/utils/exceptions.py --- old/openpyxl-2.4.8/openpyxl/utils/exceptions.py 2017-03-16 19:05:43.000000000 +0100 +++ new/openpyxl-2.4.9/openpyxl/utils/exceptions.py 2017-10-19 14:24:20.000000000 +0200 @@ -8,25 +8,32 @@ class CellCoordinatesException(Exception): """Error for converting between numeric and A1-style cell references.""" + class IllegalCharacterError(Exception): """The data submitted which cannot be used directly in Excel files. It must be removed or escaped.""" + class NamedRangeException(Exception): """Error for badly formatted named ranges.""" + class SheetTitleException(Exception): """Error for bad sheet names.""" + class InsufficientCoordinatesException(Exception): """Error for partially specified cell coordinates.""" + class InvalidFileException(Exception): """Error for trying to open a non-ooxml file.""" + class ReadOnlyWorkbookException(Exception): """Error for trying to modify a read-only workbook""" + class WorkbookAlreadySaved(Exception): """Error when attempting to perform operations on a dump workbook while it has already been dumped once""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/utils/protection.py new/openpyxl-2.4.9/openpyxl/utils/protection.py --- old/openpyxl-2.4.8/openpyxl/utils/protection.py 1970-01-01 01:00:00.000000000 +0100 +++ new/openpyxl-2.4.9/openpyxl/utils/protection.py 2017-10-19 14:24:20.000000000 +0200 @@ -0,0 +1,23 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2017 openpyxl + + +def hash_password(plaintext_password=''): + """ + Create a password hash from a given string for protecting a worksheet + only. This will not work for encrypting a workbook. + + This method is based on the algorithm provided by + Daniel Rentz of OpenOffice and the PEAR package + Spreadsheet_Excel_Writer by Xavier Noguer <xnoguer@rezebra.com>. + See also http://blogs.msdn.com/b/ericwhite/archive/2008/02/23/the-legacy-hashing-algo... + """ + password = 0x0000 + for idx, char in enumerate(plaintext_password, 1): + value = ord(char) << idx + rotated_bits = value >> 15 + value &= 0x7fff + password ^= (value | rotated_bits) + password ^= len(plaintext_password) + password ^= 0xCE4B + return str(hex(password)).upper()[2:] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/workbook/defined_name.py new/openpyxl-2.4.9/openpyxl/workbook/defined_name.py --- old/openpyxl-2.4.8/openpyxl/workbook/defined_name.py 2017-03-16 19:05:43.000000000 +0100 +++ new/openpyxl-2.4.9/openpyxl/workbook/defined_name.py 2017-10-19 14:24:20.000000000 +0200 @@ -252,3 +252,10 @@ if defn.name == name and defn.localSheetId == scope: del self.definedName[idx] return True + + + def localnames(self, scope): + """ + Provide a list of all names for a particular worksheet + """ + return [defn.name for defn in self.definedName if defn.localSheetId == scope] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/workbook/protection.py new/openpyxl-2.4.9/openpyxl/workbook/protection.py --- old/openpyxl-2.4.8/openpyxl/workbook/protection.py 2017-03-16 19:05:43.000000000 +0100 +++ new/openpyxl-2.4.9/openpyxl/workbook/protection.py 2017-10-19 14:24:20.000000000 +0200 @@ -19,16 +19,17 @@ Relation, Base64Binary, ) +from openpyxl.utils.protection import hash_password class WorkbookProtection(Serialisable): + _workbook_password, _revisions_password = None, None + tagname = "workbookPr" - workbookPassword = HexBinary(allow_none=True) workbook_password = Alias("workbookPassword") workbookPasswordCharacterSet = String(allow_none=True) - revisionsPassword = HexBinary(allow_none=True) revision_password = Alias("revisionsPassword") revisionsPasswordCharacterSet = String(allow_none=True) lockStructure = Bool(allow_none=True) @@ -46,6 +47,12 @@ workbookSaltValue = Base64Binary(allow_none=True) workbookSpinCount = Integer(allow_none=True) + __attrs__ = ('workbookPassword', 'workbookPasswordCharacterSet', 'revisionsPassword', + 'revisionsPasswordCharacterSet', 'lockStructure', 'lockWindows', 'lockRevision', + 'revisionsAlgorithmName', 'revisionsHashValue', 'revisionsSaltValue', + 'revisionsSpinCount', 'workbookAlgorithmName', 'workbookHashValue', + 'workbookSaltValue', 'workbookSpinCount') + def __init__(self, workbookPassword=None, workbookPasswordCharacterSet=None, @@ -63,9 +70,11 @@ workbookSaltValue=None, workbookSpinCount=None, ): - self.workbookPassword = workbookPassword + if workbookPassword is not None: + self.workbookPassword = workbookPassword self.workbookPasswordCharacterSet = workbookPasswordCharacterSet - self.revisionsPassword = revisionsPassword + if revisionsPassword is not None: + self.revisionsPassword = revisionsPassword self.revisionsPasswordCharacterSet = revisionsPasswordCharacterSet self.lockStructure = lockStructure self.lockWindows = lockWindows @@ -79,6 +88,47 @@ self.workbookSaltValue = workbookSaltValue self.workbookSpinCount = workbookSpinCount + def set_workbook_password(self, value='', already_hashed=False): + """Set a password on this workbook.""" + if not already_hashed: + value = hash_password(value) + self._workbook_password = value + + @property + def workbookPassword(self): + """Return the workbook password value, regardless of hash.""" + return self._workbook_password + + @workbookPassword.setter + def workbookPassword(self, value): + """Set a workbook password directly, forcing a hash step.""" + self.set_workbook_password(value) + + def set_revisions_password(self, value='', already_hashed=False): + """Set a revision password on this workbook.""" + if not already_hashed: + value = hash_password(value) + self._revisions_password = value + + @property + def revisionsPassword(self): + """Return the revisions password value, regardless of hash.""" + return self._revisions_password + + @revisionsPassword.setter + def revisionsPassword(self, value): + """Set a revisions password directly, forcing a hash step.""" + self.set_revisions_password(value) + + @classmethod + def from_tree(cls, node): + """Don't hash passwords when deserialising from XML""" + self = super(WorkbookProtection, cls).from_tree(node) + if self.workbookPassword: + self.set_workbook_password(node.get('workbookPassword'), already_hashed=True) + if self.revisionsPassword: + self.set_revisions_password(node.get('revisionsPassword'), already_hashed=True) + return self # Backwards compatibility DocumentSecurity = WorkbookProtection diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/workbook/workbook.py new/openpyxl-2.4.9/openpyxl/workbook/workbook.py --- old/openpyxl-2.4.8/openpyxl/workbook/workbook.py 2017-05-30 19:20:53.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl/workbook/workbook.py 2017-10-19 14:24:20.000000000 +0200 @@ -178,6 +178,10 @@ def remove(self, worksheet): """Remove a worksheet from this workbook.""" + idx = self._sheets.index(worksheet) + localnames = self.defined_names.localnames(scope=idx) + for name in localnames: + self.defined_names.delete(name, scope=idx) self._sheets.remove(worksheet) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/worksheet/datavalidation.py new/openpyxl-2.4.9/openpyxl/worksheet/datavalidation.py --- old/openpyxl-2.4.8/openpyxl/worksheet/datavalidation.py 2017-05-30 19:20:53.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl/worksheet/datavalidation.py 2017-10-19 14:24:20.000000000 +0200 @@ -65,7 +65,7 @@ cells = [] for rs in range_string.split(): cells.extend(rows_from_range(rs)) - return list(chain.from_iterable(cells)) + return set(chain.from_iterable(cells)) class DataValidation(Serialisable): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/worksheet/header_footer.py new/openpyxl-2.4.9/openpyxl/worksheet/header_footer.py --- old/openpyxl-2.4.8/openpyxl/worksheet/header_footer.py 2017-03-16 19:05:43.000000000 +0100 +++ new/openpyxl-2.4.9/openpyxl/worksheet/header_footer.py 2017-10-19 14:24:20.000000000 +0200 @@ -24,7 +24,7 @@ FONT_PATTERN = '&"(?P<font>.+)"' COLOR_PATTERN = "&K(?P<color>[A-F0-9]{6})" -SIZE_REGEX = r"&(?P<size>\d+)" +SIZE_REGEX = r"&(?P<size>\d+\s?)" FORMAT_REGEX = re.compile("{0}|{1}|{2}".format(FONT_PATTERN, COLOR_PATTERN, SIZE_REGEX) ) @@ -106,7 +106,7 @@ if self.font: fmt.append(u'&"{0}"'.format(self.font)) if self.size: - fmt.append("&{0}".format(self.size)) + fmt.append("&{0} ".format(self.size)) if self.color: fmt.append("&K{0}".format(self.color)) return u"".join(fmt + [self.text]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/worksheet/protection.py new/openpyxl-2.4.9/openpyxl/worksheet/protection.py --- old/openpyxl-2.4.8/openpyxl/worksheet/protection.py 2017-03-16 19:05:43.000000000 +0100 +++ new/openpyxl-2.4.9/openpyxl/worksheet/protection.py 2017-10-19 14:24:20.000000000 +0200 @@ -12,27 +12,7 @@ HexBinary, Base64Binary, ) - - -def hash_password(plaintext_password=''): - """ - Create a password hash from a given string for protecting a worksheet - only. This will not work for encrypting a workbook. - - This method is based on the algorithm provided by - Daniel Rentz of OpenOffice and the PEAR package - Spreadsheet_Excel_Writer by Xavier Noguer <xnoguer@rezebra.com>. - See also http://blogs.msdn.com/b/ericwhite/archive/2008/02/23/the-legacy-hashing-algo... - """ - password = 0x0000 - for idx, char in enumerate(plaintext_password, 1): - value = ord(char) << idx - rotated_bits = value >> 15 - value &= 0x7fff - password ^= (value | rotated_bits) - password ^= len(plaintext_password) - password ^= 0xCE4B - return str(hex(password)).upper()[2:] +from openpyxl.utils.protection import hash_password class _Protected(object): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/worksheet/read_only.py new/openpyxl-2.4.9/openpyxl/worksheet/read_only.py --- old/openpyxl-2.4.8/openpyxl/worksheet/read_only.py 2017-03-16 19:05:43.000000000 +0100 +++ new/openpyxl-2.4.9/openpyxl/worksheet/read_only.py 2017-10-19 14:24:20.000000000 +0200 @@ -180,7 +180,7 @@ col_counter = column + 1 if max_col is not None: - for _ in range(col_counter, max_col+1): + for _ in range(max(min_col, col_counter), max_col+1): yield EMPTY_CELL diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/writer/excel.py new/openpyxl-2.4.9/openpyxl/writer/excel.py --- old/openpyxl-2.4.8/openpyxl/writer/excel.py 2017-05-30 19:20:53.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl/writer/excel.py 2017-10-19 14:24:20.000000000 +0200 @@ -9,6 +9,7 @@ from zipfile import ZipFile, ZIP_DEFLATED # package imports +from openpyxl.utils.exceptions import InvalidFileException from openpyxl.xml.constants import ( ARC_SHARED_STRINGS, ARC_CONTENT_TYPES, @@ -122,6 +123,8 @@ def _write_charts(self): # delegate to object + if len(self._charts) != len(set(self._charts)): + raise InvalidFileException("The same chart cannot be used in more than one worksheet") for chart in self._charts: self._archive.writestr(chart.path[1:], tostring(chart._write())) self.manifest.append(chart) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/writer/workbook.py new/openpyxl-2.4.9/openpyxl/writer/workbook.py --- old/openpyxl-2.4.8/openpyxl/writer/workbook.py 2017-05-30 19:20:53.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl/writer/workbook.py 2017-10-19 14:24:20.000000000 +0200 @@ -87,6 +87,9 @@ props.date1904 = True root.workbookPr = props + # workbook protection + root.workbookProtection = wb.security + # book views active = get_active_sheet(wb) view = BookView(activeTab=active) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/writer/worksheet.py new/openpyxl-2.4.9/openpyxl/writer/worksheet.py --- old/openpyxl-2.4.8/openpyxl/writer/worksheet.py 2017-03-16 19:05:43.000000000 +0100 +++ new/openpyxl-2.4.9/openpyxl/writer/worksheet.py 2017-10-19 14:24:20.000000000 +0200 @@ -10,6 +10,7 @@ # package imports from openpyxl.xml.functions import xmlfile from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.compat import unicode from openpyxl.styles.differential import DifferentialStyle from openpyxl.packaging.relationship import Relationship, RelationshipList @@ -185,7 +186,7 @@ for cell, col in zip(row, table.tableColumns): if cell.data_type != "s": warn("File may not be readable: column headings must be strings.") - col.name = str(cell.value) + col.name = unicode(cell.value) rel = Relationship(type=table._rel_type, Target="") ws._rels.append(rel) table._rel_id = rel.Id diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl/writer/write_only.py new/openpyxl-2.4.9/openpyxl/writer/write_only.py --- old/openpyxl-2.4.8/openpyxl/writer/write_only.py 2017-03-16 19:05:43.000000000 +0100 +++ new/openpyxl-2.4.9/openpyxl/writer/write_only.py 2017-10-19 14:24:20.000000000 +0200 @@ -21,7 +21,7 @@ from .etree_worksheet import write_cell from .excel import ExcelWriter -from .worksheet import write_drawing +from .worksheet import write_drawing, write_conditional_formatting from openpyxl.xml.constants import SHEET_MAIN_NS from openpyxl.xml.functions import xmlfile, Element @@ -191,6 +191,11 @@ if self.sort_state.ref: xf.write(self.sort_state.to_tree()) + if self.conditional_formatting: + cfs = write_conditional_formatting(self) + for cf in cfs: + xf.write(cf) + if self.data_validations.count: xf.write(self.data_validations.to_tree()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl.egg-info/PKG-INFO new/openpyxl-2.4.9/openpyxl.egg-info/PKG-INFO --- old/openpyxl-2.4.8/openpyxl.egg-info/PKG-INFO 2017-05-30 19:33:08.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl.egg-info/PKG-INFO 2017-10-19 14:24:45.000000000 +0200 @@ -1,11 +1,12 @@ Metadata-Version: 1.1 Name: openpyxl -Version: 2.4.8 +Version: 2.4.9 Summary: A Python library to read/write Excel 2010 xlsx/xlsm files Home-page: https://openpyxl.readthedocs.io Author: See AUTHORS Author-email: charlie.clark@clark-consulting.eu License: MIT/Expat +Description-Content-Type: UNKNOWN Description: openpyxl ======== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.8/openpyxl.egg-info/SOURCES.txt new/openpyxl-2.4.9/openpyxl.egg-info/SOURCES.txt --- old/openpyxl-2.4.8/openpyxl.egg-info/SOURCES.txt 2017-05-30 19:33:08.000000000 +0200 +++ new/openpyxl-2.4.9/openpyxl.egg-info/SOURCES.txt 2017-10-19 14:24:45.000000000 +0200 @@ -131,6 +131,7 @@ openpyxl/utils/exceptions.py openpyxl/utils/formulas.py openpyxl/utils/indexed_list.py +openpyxl/utils/protection.py openpyxl/utils/units.py openpyxl/workbook/__init__.py openpyxl/workbook/child.py