commit python3-openpyxl for openSUSE:Factory
Hello community, here is the log from the commit of package python3-openpyxl for openSUSE:Factory checked in at 2016-11-28 15:09:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-openpyxl (Old) and /work/SRC/openSUSE:Factory/.python3-openpyxl.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python3-openpyxl" Changes: -------- --- /work/SRC/openSUSE:Factory/python3-openpyxl/python3-openpyxl.changes 2016-09-28 15:04:59.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python3-openpyxl.new/python3-openpyxl.changes 2016-11-28 15:10:12.000000000 +0100 @@ -1,0 +2,28 @@ +Sat Nov 26 18:25:12 UTC 2016 - arun@gmx.de + +- update to version 2.4.1: + * Bug fixes + + #643 Make checking for duplicate sheet titles case insensitive + + #647 Trouble handling LibreOffice files with named styles + + #687 Directly assigned new named styles always refer to “Normal” + + #690 Cannot parse print titles with multiple sheet names + + #691 Cannot work with macro files created by LibreOffice + + Prevent duplicate differential styles + + #694 Allow sheet titles longer than 31 characters + + #697 Cannot unset hyperlinks + + #699 Exception raised when format objects use cell references + + #703 Copy height and width when copying comments + + #705 Incorrect content type for VBA macros + + #707 IndexError raised in read-only mode when accessing + individual cells + + #711 Files with external links become corrupted + + #715 Cannot read files containing macro sheets + + #717 Details from named styles not preserved when reading files + + #722 Remove broken Print Title and Print Area definitions + * Minor changes + + Add support for Python 3.6 + + Correct documentation for headers and footers + * Deprecations + + Worksheet methods get_named_range() and get_sqaured_range() + +------------------------------------------------------------------- Old: ---- openpyxl-2.4.0.tar.gz New: ---- openpyxl-2.4.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-openpyxl.spec ++++++ --- /var/tmp/diff_new_pack.9XZIY6/_old 2016-11-28 15:10:13.000000000 +0100 +++ /var/tmp/diff_new_pack.9XZIY6/_new 2016-11-28 15:10:13.000000000 +0100 @@ -17,7 +17,7 @@ Name: python3-openpyxl -Version: 2.4.0 +Version: 2.4.1 Release: 0 Summary: A Python library to read/write Excel 2007 xlsx/xlsm files License: MIT and Python-2.0 ++++++ openpyxl-2.4.0.tar.gz -> openpyxl-2.4.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/AUTHORS.rst new/openpyxl-2.4.1/AUTHORS.rst --- old/openpyxl-2.4.0/AUTHORS.rst 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/AUTHORS.rst 2016-11-23 15:13:45.000000000 +0100 @@ -23,6 +23,7 @@ * Brice Gelineau * Alex Gronholm * Yaroslav Halchenko +* Fumito Hamamura * Khchine Hamza * Josh Haywood * Jeff Holman @@ -34,6 +35,7 @@ * Chi Ho Kwok * Yingjie Lan * Detlef Lannert +* Laurent Laporte * Nicholas Laver * Greg Lehmann * Adam Lofts @@ -60,6 +62,7 @@ * Dieter Vandenbussche * Paul Van Der Linden * Gerald Van Huffelen +* Koert van der Veer * Laurent Vasseur * Kay Webber * Shibukawa Yoshiki diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/PKG-INFO new/openpyxl-2.4.1/PKG-INFO --- old/openpyxl-2.4.0/PKG-INFO 2016-09-15 10:25:11.000000000 +0200 +++ new/openpyxl-2.4.1/PKG-INFO 2016-11-23 15:16:29.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: openpyxl -Version: 2.4.0 +Version: 2.4.1 Summary: A Python library to read/write Excel 2010 xlsx/xlsm files Home-page: http://openpyxl.readthedocs.org Author: See AUTHORS @@ -70,4 +70,5 @@ Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 Requires: python (>=2.6.0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/.constants.json new/openpyxl-2.4.1/openpyxl/.constants.json --- old/openpyxl-2.4.0/openpyxl/.constants.json 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/.constants.json 2016-11-23 15:13:45.000000000 +0100 @@ -4,5 +4,5 @@ "__license__": "MIT/Expat", "__maintainer_email__": "openpyxl-users@googlegroups.com", "__url__": "http://openpyxl.readthedocs.org", - "__version__": "2.4.0" + "__version__": "2.4.1" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/cell/cell.py new/openpyxl-2.4.1/openpyxl/cell/cell.py --- old/openpyxl-2.4.0/openpyxl/cell/cell.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/cell/cell.py 2016-11-23 15:13:45.000000000 +0100 @@ -12,6 +12,7 @@ __docformat__ = "restructuredtext en" # Python stdlib imports +from copy import copy import datetime import re @@ -306,13 +307,17 @@ """Set value and display for hyperlinks in a cell. Automatically sets the `value` of the cell with link text, but you can modify it afterwards by setting the `value` - property, and the hyperlink will remain.""" - if not isinstance(val, Hyperlink): - val = Hyperlink(ref="", target=val) - val.ref = self.coordinate - self._hyperlink = val - if self._value is None: - self.value = val.target or val.location + property, and the hyperlink will remain. + Hyperlink is removed if set to ``None``.""" + if val is None: + self._hyperlink = None + else: + if not isinstance(val, Hyperlink): + val = Hyperlink(ref="", target=val) + val.ref = self.coordinate + self._hyperlink = val + if self._value is None: + self.value = val.target or val.location @property @@ -384,13 +389,19 @@ """ return self._comment + @comment.setter def comment(self, value): + """ + Assign a comment to a cell + """ if value is not None: - value.parent = self + if value.parent: + value = copy(value) + value.bind(self) elif value is None and self._comment: - self._comment.parent = None + self._comment.unbind() self._comment = value diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/comments/comments.py new/openpyxl-2.4.1/openpyxl/comments/comments.py --- old/openpyxl-2.4.0/openpyxl/comments/comments.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/comments/comments.py 2016-11-23 15:13:45.000000000 +0100 @@ -1,8 +1,6 @@ from __future__ import absolute_import # Copyright (c) 2010-2016 openpyxl -from openpyxl.compat import deprecated - class Comment(object): @@ -29,13 +27,31 @@ return "Comment: {0} by {1}".format(self.content, self.author) - @parent.setter - def parent(self, cell): + def __copy__(self): + """Create a detached copy of this comment.""" + clone = self.__class__(self.content, self.author) + clone.width = self.width + clone.height = self.height + return clone + + + def bind(self, cell): + """ + Bind comment to a particular cell + """ if cell is not None and self._parent is not None and self._parent != cell: - raise AttributeError("Comment already assigned to %s in worksheet %s. Cannot assign a comment to more than one cell" % (cell.coordinate, cell.parent.title)) + fmt = "Comment already assigned to {0} in worksheet {1}. Cannot assign a comment to more than one cell" + raise AttributeError(fmt.format(cell.coordinate, cell.parent.title)) self._parent = cell + def unbind(self): + """ + Unbind a comment from a cell + """ + self._parent = None + + @property def text(self): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/drawing/text.py new/openpyxl-2.4.1/openpyxl/drawing/text.py --- old/openpyxl-2.4.0/openpyxl/drawing/text.py 2016-03-16 10:34:54.000000000 +0100 +++ new/openpyxl-2.4.1/openpyxl/drawing/text.py 2016-11-23 15:13:45.000000000 +0100 @@ -118,7 +118,7 @@ kumimoji = Bool(allow_none=True) lang = String(allow_none=True) altLang = String(allow_none=True) - sz = Integer(allow_none=True) + sz = MinMax(allow_none=True, min=100, max=400000) # 100ths of a point b = Bool(allow_none=True) i = Bool(allow_none=True) u = NoneSet(values=(['words', 'sng', 'dbl', 'heavy', 'dotted', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/formatting/rule.py new/openpyxl-2.4.1/openpyxl/formatting/rule.py --- old/openpyxl-2.4.0/openpyxl/formatting/rule.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/formatting/rule.py 2016-11-23 15:13:45.000000000 +0100 @@ -18,14 +18,21 @@ from openpyxl.styles.colors import Color, ColorDescriptor from openpyxl.styles.differential import DifferentialStyle +from openpyxl.utils.cell import COORD_RE + class ValueDescriptor(Float): """ Expected type depends upon type attribue of parent :-( + + Most values should be numeric BUT they can also be cell references """ def __set__(self, instance, value): - if instance.type == "formula": + ref = None + if value is not None and isinstance(value, basestring): + ref = COORD_RE.match(value) + if instance.type == "formula" or ref: self.expected_type = basestring else: self.expected_type = float diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/packaging/manifest.py new/openpyxl-2.4.1/openpyxl/packaging/manifest.py --- old/openpyxl-2.4.0/openpyxl/packaging/manifest.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/packaging/manifest.py 2016-11-23 15:13:45.000000000 +0100 @@ -40,7 +40,7 @@ mimetypes.init() mimetypes.add_type('application/xml', ".xml") mimetypes.add_type('application/vnd.openxmlformats-package.relationships+xml', ".rels") -mimetypes.add_type("application/vnd.ms-office.activeX", ".bin") +mimetypes.add_type("application/vnd.ms-office.vbaProject", ".bin") mimetypes.add_type("application/vnd.openxmlformats-officedocument.vmlDrawing", ".vml") mimetypes.add_type("image/x-emf", ".emf") @@ -112,8 +112,12 @@ @property def extensions(self): + """ + Map content types to file extensions + Skip parts without extensions + """ exts = set([os.path.splitext(part.PartName)[-1] for part in self.Override]) - return [(ext[1:], mimetypes.types_map[ext]) for ext in sorted(exts)] + return [(ext[1:], mimetypes.types_map[ext]) for ext in sorted(exts) if ext] def to_tree(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/packaging/relationship.py new/openpyxl-2.4.1/openpyxl/packaging/relationship.py --- old/openpyxl-2.4.0/openpyxl/packaging/relationship.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/packaging/relationship.py 2016-11-23 15:13:45.000000000 +0100 @@ -30,7 +30,7 @@ target = Alias("Target") TargetMode = String(allow_none=True) Id = String(allow_none=True) - id = Alias("id") + id = Alias("Id") def __init__(self, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/packaging/workbook.py new/openpyxl-2.4.1/openpyxl/packaging/workbook.py --- old/openpyxl-2.4.0/openpyxl/packaging/workbook.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/packaging/workbook.py 2016-11-23 15:13:45.000000000 +0100 @@ -6,6 +6,7 @@ """ import posixpath +from warnings import warn from openpyxl.xml.constants import ( ARC_WORKBOOK, @@ -57,12 +58,23 @@ ) if package.definedNames: + package.definedNames._cleanup() self.wb.defined_names = package.definedNames def find_sheets(self): + """ + Find all sheets in the workbook and return the link to the source file. + + Older XLSM files sometimes contain invalid sheet elements. + Warn user when these are removed. + """ for sheet in self.sheets: + if not sheet.id: + msg = "File contains an invalid specification for {0}. This will be removed".format(sheet.name) + warn(msg) + continue yield sheet, self.rels[sheet.id] @@ -71,6 +83,7 @@ Bind reserved names to parsed worksheets """ defns = [] + for defn in self.wb.defined_names.definedName: reserved = defn.is_reserved if reserved in ("Print_Titles", "Print_Area"): @@ -81,7 +94,6 @@ sheet.print_title_cols = cols elif reserved == "Print_Area": sheet.print_area = _unpack_print_area(defn) - continue else: defns.append(defn) self.wb.defined_names.definedName = defns diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/styles/differential.py new/openpyxl-2.4.1/openpyxl/styles/differential.py --- old/openpyxl-2.4.0/openpyxl/styles/differential.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/styles/differential.py 2016-11-23 15:13:45.000000000 +0100 @@ -53,6 +53,9 @@ class DifferentialStyleList(Serialisable): + """ + Deduping container for differential styles. + """ tagname = "dxfs" @@ -65,14 +68,22 @@ def append(self, dxf): - styles = self.styles - styles.append(dxf) - self.styles = styles + """ + Check to see whether style already exists and append it if does not. + """ + if not isinstance(dxf, DifferentialStyle): + raise TypeError('expected ' + str(DifferentialStyle)) + if dxf in self.styles: + return + self.styles.append(dxf) def add(self, dxf): + """ + Add a differential style and return its index + """ self.append(dxf) - return len(self.styles) - 1 + return self.styles.index(dxf) def __bool__(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/styles/named_styles.py new/openpyxl-2.4.1/openpyxl/styles/named_styles.py --- old/openpyxl-2.4.0/openpyxl/styles/named_styles.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/styles/named_styles.py 2016-11-23 15:13:45.000000000 +0100 @@ -69,10 +69,10 @@ self.protection = protection self.builtinId = builtinId self.hidden = hidden - self.xfId = xfId # index self._wb = None self._style = StyleArray() + def __setattr__(self, attr, value): super(NamedStyle, self).__setattr__(attr, value) if getattr(self, '_wb', None) and attr in ( @@ -88,6 +88,21 @@ yield key, safe_string(value) + @property + def xfId(self): + """ + Index of the style in the list of named styles + """ + return self._style.xfId + + + def _set_index(self, idx): + """ + Allow the containing list to set the index + """ + self._style.xfId = idx + + def bind(self, wb): """ Bind a named style to a workbook @@ -143,6 +158,9 @@ class NamedStyleList(list): """ Named styles are editable and can be applied to multiple objects + + As only the index is stored in referencing objects the order mus + be preserved. """ @property @@ -168,17 +186,10 @@ raise TypeError("""Only NamedStyle instances can be added""") elif style.name in self.names: raise ValueError("""Style {0} exists already""".format(style.name)) + style._set_index(len(self)) super(NamedStyleList, self).append(style) - def add(self, style): - """ - Add a style and return index - """ - self.append(style) - return self.index(style) - - class _NamedCellStyle(Serialisable): """ @@ -247,19 +258,28 @@ @property def names(self): """ - Convert to NamedStyle objects and remove duplicates + Convert to NamedStyle objects and remove duplicates. + + In theory the highest xfId wins but in practice they are duplicates + so it doesn't matter. """ def sort_fn(v): return v.xfId - styles = OrderedDict() + styles = NamedStyleList() + names = set() + for ns in sorted(self.cellStyle, key=sort_fn): + if ns.name in names: + continue + style = NamedStyle( name=ns.name, - hidden=ns.hidden + hidden=ns.hidden, + builtinId = ns.builtinId ) - style.builtinId = ns.builtinId - style.xfId = ns.xfId - styles[ns.name] = style - return NamedStyleList(styles.values()) + names.add(ns.name) + styles.append(style) # assign xfId + + return styles diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/styles/styleable.py new/openpyxl-2.4.1/openpyxl/styles/styleable.py --- old/openpyxl-2.4.0/openpyxl/styles/styleable.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/styles/styleable.py 2016-11-23 15:13:45.000000000 +0100 @@ -72,7 +72,7 @@ if style not in coll: instance.parent.parent.add_named_style(style) elif value not in coll.names: - if value in styles: + if value in styles: # is it builtin? style = styles[value] if style not in coll: instance.parent.parent.add_named_style(style) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/styles/stylesheet.py new/openpyxl-2.4.1/openpyxl/styles/stylesheet.py --- old/openpyxl-2.4.0/openpyxl/styles/stylesheet.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/styles/stylesheet.py 2016-11-23 15:13:45.000000000 +0100 @@ -87,7 +87,7 @@ self.alignments = self.cellXfs.alignments self.protections = self.cellXfs.prots self._normalise_numbers() - self.named_styles = self._merge_named_styles() + self.named_styles = self._merge_named_styles() @classmethod @@ -96,30 +96,39 @@ attrs = dict(node.attrib) for k in attrs: del node.attrib[k] - return super(Stylesheet, cls).from_tree(node) + return super(Stylesheet, cls).from_tree(node) def _merge_named_styles(self): """ - Merge named style names "cellStyles" with their associated styles "cellStyleXfs" + Merge named style names "cellStyles" with their associated styles + "cellStyleXfs" """ named_styles = self.cellStyles.names - custom = self.custom_formats - formats = self.number_formats + for style in named_styles: - xf = self.cellStyleXfs[style.xfId] - style.font = self.fonts[xf.fontId] - style.fill = self.fills[xf.fillId] - style.border = self.borders[xf.borderId] - if xf.numFmtId in custom: - style.number_format = custom[xf.numFmtId] - if xf.alignment: - style.alignment = xf.alignment - if xf.protection: - style.protection = xf.protection + self._expand_named_style(style) + return named_styles + def _expand_named_style(self, named_style): + """ + Bind format definitions for a named style from the associated style + record + """ + xf = self.cellStyleXfs[named_style.xfId] + named_style.font = self.fonts[xf.fontId] + named_style.fill = self.fills[xf.fillId] + named_style.border = self.borders[xf.borderId] + if xf.numFmtId in self.custom_formats: + named_style.number_format = self.custom_formats[xf.numFmtId] + if xf.alignment: + named_style.alignment = xf.alignment + if xf.protection: + named_style.protection = xf.protection + + def _split_named_styles(self, wb): """ Convert NamedStyle into separate CellStyle and Xf objects @@ -165,6 +174,9 @@ wb._cell_styles = stylesheet.cell_styles wb._named_styles = stylesheet.named_styles + for ns in wb._named_styles: + ns.bind(wb) + wb._borders = IndexedList(stylesheet.borders) wb._fonts = IndexedList(stylesheet.fonts) wb._fills = IndexedList(stylesheet.fills) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/utils/cell.py new/openpyxl-2.4.1/openpyxl/utils/cell.py --- old/openpyxl-2.4.0/openpyxl/utils/cell.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/utils/cell.py 2016-11-23 15:13:45.000000000 +0100 @@ -21,7 +21,7 @@ """ ABSOLUTE_RE = re.compile('^' + RANGE_EXPR +'$', re.VERBOSE) SHEET_TITLE = """ -^(('(?P<quoted>([^']|'')*)')|(?P<notquoted>[^']*))!""" +(('(?P<quoted>([^']|'')*)')|(?P<notquoted>[^']*))!""" SHEETRANGE_RE = re.compile("""{0}(?P<cells>{1})$""".format( SHEET_TITLE, RANGE_EXPR), re.VERBOSE) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/workbook/child.py new/openpyxl-2.4.1/openpyxl/workbook/child.py --- old/openpyxl-2.4.0/openpyxl/workbook/child.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/workbook/child.py 2016-11-23 15:13:45.000000000 +0100 @@ -2,6 +2,8 @@ # Copyright (c) 2010-2016 openpyxl import re +import warnings + from openpyxl.compat import unicode from openpyxl.worksheet.header_footer import HeaderFooter @@ -17,10 +19,13 @@ """ Naive check to see whether name already exists. If name does exist suggest a name using an incrementer + Duplicates are case insensitive """ - if value in names: + # Check for an absolute match in which case we need to find an alternative + match = [n for n in names if n.lower() == value.lower()] + if match: names = ",".join(names) - sheet_title_regex = re.compile("(?P<title>%s)(?P<count>\d*),?" % re.escape(value)) + sheet_title_regex = re.compile("(?P<title>%s)(?P<count>\d*),?" % re.escape(value), re.I) matches = sheet_title_regex.findall(names) if matches: # use name, but append with the next highest integer @@ -90,7 +95,7 @@ value = avoid_duplicate_name(self.parent.sheetnames, value) if len(value) > 31: - raise ValueError('Maximum 31 characters allowed in sheet title') + warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file") self.__title = value diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/workbook/defined_name.py new/openpyxl-2.4.1/openpyxl/workbook/defined_name.py --- old/openpyxl-2.4.0/openpyxl/workbook/defined_name.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/workbook/defined_name.py 2016-11-23 15:13:45.000000000 +0100 @@ -33,7 +33,7 @@ COL_RANGE_RE = re.compile(COL_RANGE) ROW_RANGE = r"""(?P<rows>[$]?\d+:[$]?\d+)""" ROW_RANGE_RE = re.compile(ROW_RANGE) -TITLES_REGEX = re.compile("""^{0}{1}?,?{2}?$""".format(SHEET_TITLE, ROW_RANGE, COL_RANGE), +TITLES_REGEX = re.compile("""{0}{1}?,?{2}?""".format(SHEET_TITLE, ROW_RANGE, COL_RANGE), re.VERBOSE) @@ -44,8 +44,11 @@ Extract rows and or columns from print titles so that they can be assigned to a worksheet """ - m = TITLES_REGEX.match(defn.value) - return m.group('rows'), m.group('cols') + scanner = TITLES_REGEX.finditer(defn.value) + kw = dict((k, v) for match in scanner + for k, v in match.groupdict().items() if v) + + return kw.get('rows'), kw.get('cols') def _unpack_print_area(defn): @@ -137,7 +140,8 @@ for part in tok.items: if part.subtype == "RANGE": m = SHEETRANGE_RE.match(part.value) - yield m.group('notquoted'), m.group('cells') + sheetname = m.group('notquoted') or m.group('quoted') + yield sheetname, m.group('cells') @property @@ -174,6 +178,14 @@ self.definedName = definedName + def _cleanup(self): + """ + Strip broken or unknown definitions + """ + self.delete("_xlnm.Print_Titles") + self.delete("_xlnm.Print_Area") + + def _duplicate(self, defn): """ Check for whether DefinedName with the same name and scope already @@ -199,20 +211,46 @@ def __contains__(self, name): + """ + See if a globaly defined name exists + """ for defn in self.definedName: - if defn.name == name: + if defn.name == name and defn.localSheetId is None: return True def __getitem__(self, name): + """ + Get globally defined name + """ + defn = self.get(name) + if not defn: + raise KeyError("No definition called {0}".format(name)) + return defn + + + def get(self, name, scope=None): + """ + Get the name assigned to a specicic sheet or global + """ for defn in self.definedName: - if defn.name == name: + if defn.name == name and defn.localSheetId == scope: return defn - raise KeyError("No definition called {0}".format(name)) def __delitem__(self, name): + """ + Delete a globally defined name + """ + if not self.delete(name): + raise KeyError("No globally defined name {0}".format(name)) + + + def delete(self, name, scope=None): + """ + Delete a name assigned to a specific or global + """ for idx, defn in enumerate(self.definedName): - if defn.name == name: + if defn.name == name and defn.localSheetId == scope: del self.definedName[idx] - break + return True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/workbook/workbook.py new/openpyxl-2.4.1/openpyxl/workbook/workbook.py --- old/openpyxl-2.4.0/openpyxl/workbook/workbook.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/workbook/workbook.py 2016-11-23 15:13:45.000000000 +0100 @@ -272,7 +272,7 @@ """ Add a named style """ - style.xfId = self._named_styles.add(style) + self._named_styles.append(style) style.bind(self) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/worksheet/copier.py new/openpyxl-2.4.1/openpyxl/worksheet/copier.py --- old/openpyxl-2.4.0/openpyxl/worksheet/copier.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/worksheet/copier.py 2016-11-23 15:13:45.000000000 +0100 @@ -57,7 +57,7 @@ target_cell._hyperlink = copy(source_cell.hyperlink) if source_cell.comment: - target_cell.comment = Comment(source_cell.comment.text, source_cell.comment.author) + target_cell.comment = copy(source_cell.comment) def _copy_dimensions(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/worksheet/header_footer.py new/openpyxl-2.4.1/openpyxl/worksheet/header_footer.py --- old/openpyxl-2.4.0/openpyxl/worksheet/header_footer.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/worksheet/header_footer.py 2016-11-23 15:13:45.000000000 +0100 @@ -7,6 +7,7 @@ from warnings import warn from openpyxl.descriptors import ( + Alias, Bool, Strict, String, @@ -138,6 +139,7 @@ left = Typed(expected_type=_HeaderFooterPart) center = Typed(expected_type=_HeaderFooterPart) + centre = Alias("center") right = Typed(expected_type=_HeaderFooterPart) __keys = ('L', 'C', 'R') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/worksheet/read_only.py new/openpyxl-2.4.1/openpyxl/worksheet/read_only.py --- old/openpyxl-2.4.0/openpyxl/worksheet/read_only.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/worksheet/read_only.py 2016-11-23 15:13:45.000000000 +0100 @@ -74,7 +74,6 @@ # Methods from Worksheet self.cell = Worksheet.cell.__get__(self) self.iter_rows = Worksheet.iter_rows.__get__(self) - self.rows = Worksheet.rows.__get__(self) def __getitem__(self, key): @@ -179,12 +178,17 @@ def _get_cell(self, row, column): """Cells are returned by a generator which can be empty""" - cell = tuple(self.get_squared_range(column, row, column, row))[0] - if cell: - return cell[0] + for row in self.get_squared_range(column, row, column, row): + if row: + return row[0] return EMPTY_CELL + @property + def rows(self): + return self.iter_rows() + + def calculate_dimension(self, force=False): if not all([self.max_column, self.max_row]): if force: @@ -202,8 +206,11 @@ Loop through all the cells to get the size of a worksheet. Do this only if it is explicitly requested. """ + max_col = 0 for r in self.rows: + if not r: + continue cell = r[-1] max_col = max(max_col, cell.column) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/worksheet/worksheet.py new/openpyxl-2.4.1/openpyxl/worksheet/worksheet.py --- old/openpyxl-2.4.0/openpyxl/worksheet/worksheet.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/worksheet/worksheet.py 2016-11-23 15:13:45.000000000 +0100 @@ -285,9 +285,12 @@ :param coordinate: coordinates of the cell (e.g. 'B12') :type coordinate: string + :param value: value of the cell (e.g. 5) + :type value: numeric or time or string or bool or none + :raise: InsufficientCoordinatesException when neither row nor column are not given - :rtype: :class:openpyxl.cell.Cell + :rtype: openpyxl.cell.Cell """ @@ -464,6 +467,9 @@ Additional rows and columns can be created using offsets. + :param range_string: range string (e.g. 'A1:B2') *deprecated* + :type range_string: string + :param min_col: smallest column index (1-based index) :type min_col: int @@ -477,10 +483,10 @@ :type max_row: int :param row_offset: additional rows (e.g. 4) - :type row: int + :type row_offset: int - :param column_offset: additonal columns (e.g. 3) - :type column: int + :param column_offset: additional columns (e.g. 3) + :type column_offset: int :rtype: generator """ @@ -573,6 +579,10 @@ return self.iter_cols() + @deprecated(""" + Use ws.iter_rows() or ws.iter_cols() depending whether you + want rows or columns returned. + """) def get_squared_range(self, min_col, min_row, max_col, max_row): """Returns a 2D array of cells. Will create any cells within the boundaries that do not already exist @@ -597,6 +607,7 @@ for column in range(min_col, max_col + 1)) + @deprecated("""Ranges are workbook objects. Use wb.defined_names[range_name]""") def get_named_range(self, range_name): """ Returns a 2D array of cells, with optional row and column offsets. @@ -604,7 +615,7 @@ :param range_name: `named range` name :type range_name: string - :rtype: tuples of tuples of :class:`openpyxl.cell.Cell` + :rtype: tuple[tuple[openpyxl.cell.Cell]] """ defn = self.parent.defined_names[range_name] if defn.localSheetId and defn.localSheetId != self.parent.get_index(self): @@ -747,7 +758,7 @@ * If it's a dict: values are assigned to the columns indicated by the keys (numbers or letters) :param iterable: list, range or generator, or dict containing values to append - :type iterable: list/tuple/range/generator or dict + :type iterable: list|tuple|range|generator or dict Usage: @@ -799,6 +810,10 @@ """ tells which cell is under the given coordinates (in pixels) counting from the top-left corner of the sheet. Can be used to locate images and charts on the worksheet """ + + if left < 0 or top < 0: + raise ValueError("Coordinates must be positive") + current_col = 1 current_row = 1 column_dimensions = self.column_dimensions diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/writer/workbook.py new/openpyxl-2.4.1/openpyxl/writer/workbook.py --- old/openpyxl-2.4.0/openpyxl/writer/workbook.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/writer/workbook.py 2016-11-23 15:13:45.000000000 +0100 @@ -104,11 +104,11 @@ # external references for link in wb._external_links: # need to match a counter with a workbook's relations - rId = len(wb.rels) - ext = ExternalReference(id="rId{0}".format(link._id)) + rId = len(wb.rels) + 1 rel = Relationship(type=link._rel_type, Target=link.path) - root.externalReferences.append(ext) wb.rels.append(rel) + ext = ExternalReference(id=rel.id) + root.externalReferences.append(ext) # Defined names defined_names = copy(wb.defined_names) # don't add special defns to workbook itself. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl/writer/worksheet.py new/openpyxl-2.4.1/openpyxl/writer/worksheet.py --- old/openpyxl-2.4.0/openpyxl/writer/worksheet.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl/writer/worksheet.py 2016-11-23 15:13:45.000000000 +0100 @@ -42,10 +42,11 @@ def write_conditional_formatting(worksheet): """Write conditional formatting to xml.""" + df = DifferentialStyle() wb = worksheet.parent for cf in worksheet.conditional_formatting: for rule in cf.rules: - if rule.dxf and rule.dxf != DifferentialStyle(): + if rule.dxf and rule.dxf != df: rule.dxfId = wb._differential_styles.add(rule.dxf) yield cf.to_tree() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/openpyxl.egg-info/PKG-INFO new/openpyxl-2.4.1/openpyxl.egg-info/PKG-INFO --- old/openpyxl-2.4.0/openpyxl.egg-info/PKG-INFO 2016-09-15 10:25:11.000000000 +0200 +++ new/openpyxl-2.4.1/openpyxl.egg-info/PKG-INFO 2016-11-23 15:16:25.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: openpyxl -Version: 2.4.0 +Version: 2.4.1 Summary: A Python library to read/write Excel 2010 xlsx/xlsm files Home-page: http://openpyxl.readthedocs.org Author: See AUTHORS @@ -70,4 +70,5 @@ Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 Requires: python (>=2.6.0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openpyxl-2.4.0/setup.py new/openpyxl-2.4.1/setup.py --- old/openpyxl-2.4.0/setup.py 2016-09-15 10:23:12.000000000 +0200 +++ new/openpyxl-2.4.1/setup.py 2016-11-23 15:13:45.000000000 +0100 @@ -75,5 +75,6 @@ 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', ], )
participants (1)
-
root@hilbert.suse.de