1from __future__ import absolute_import 2# Copyright (c) 2010-2019 openpyxl 3 4 5"""Write worksheets to xml representations in an optimized way""" 6 7from inspect import isgenerator 8 9from openpyxl.cell import Cell, WriteOnlyCell 10from openpyxl.workbook.child import _WorkbookChild 11from .worksheet import Worksheet 12from openpyxl.utils.exceptions import WorkbookAlreadySaved 13 14from ._writer import WorksheetWriter 15 16 17class WriteOnlyWorksheet(_WorkbookChild): 18 """ 19 Streaming worksheet. Optimised to reduce memory by writing rows just in 20 time. 21 Cells can be styled and have comments Styles for rows and columns 22 must be applied before writing cells 23 """ 24 25 __saved = False 26 _writer = None 27 _rows = None 28 _rel_type = Worksheet._rel_type 29 _path = Worksheet._path 30 mime_type = Worksheet.mime_type 31 32 33 def __init__(self, parent, title): 34 super(WriteOnlyWorksheet, self).__init__(parent, title) 35 self._max_col = 0 36 self._max_row = 0 37 38 # Methods from Worksheet 39 self._add_row = Worksheet._add_row.__get__(self) 40 self._add_column = Worksheet._add_column.__get__(self) 41 self.add_chart = Worksheet.add_chart.__get__(self) 42 self.add_image = Worksheet.add_image.__get__(self) 43 self.add_table = Worksheet.add_table.__get__(self) 44 45 setup = Worksheet._setup.__get__(self) 46 setup() 47 48 self.print_titles = Worksheet.print_titles.__get__(self) 49 self.sheet_view = Worksheet.sheet_view.__get__(self) 50 51 52 @property 53 def freeze_panes(self): 54 return Worksheet.freeze_panes.__get__(self) 55 56 57 @freeze_panes.setter 58 def freeze_panes(self, value): 59 Worksheet.freeze_panes.__set__(self, value) 60 61 62 @property 63 def print_title_cols(self): 64 return Worksheet.print_title_cols.__get__(self) 65 66 67 @print_title_cols.setter 68 def print_title_cols(self, value): 69 Worksheet.print_title_cols.__set__(self, value) 70 71 72 @property 73 def print_title_rows(self): 74 return Worksheet.print_title_rows.__get__(self) 75 76 77 @print_title_rows.setter 78 def print_title_rows(self, value): 79 Worksheet.print_title_rows.__set__(self, value) 80 81 82 @property 83 def print_area(self): 84 return Worksheet.print_area.__get__(self) 85 86 87 @print_area.setter 88 def print_area(self, value): 89 Worksheet.print_area.__set__(self, value) 90 91 92 @property 93 def closed(self): 94 return self.__saved 95 96 97 def _write_rows(self): 98 """ 99 Send rows to the writer's stream 100 """ 101 try: 102 xf = self._writer.xf.send(True) 103 except StopIteration: 104 self._already_saved() 105 106 with xf.element("sheetData"): 107 row_idx = 1 108 try: 109 while True: 110 row = (yield) 111 row = self._values_to_row(row, row_idx) 112 self._writer.write_row(xf, row, row_idx) 113 row_idx += 1 114 except GeneratorExit: 115 pass 116 117 self._writer.xf.send(None) 118 119 120 def _get_writer(self): 121 if self._writer is None: 122 self._writer = WorksheetWriter(self) 123 self._writer.write_top() 124 125 126 def close(self): 127 if self.__saved: 128 self._already_saved() 129 130 self._get_writer() 131 132 if self._rows is None: 133 self._writer.write_rows() 134 else: 135 self._rows.close() 136 137 self._writer.write_tail() 138 139 self._writer.close() 140 self.__saved = True 141 142 143 def append(self, row): 144 """ 145 :param row: iterable containing values to append 146 :type row: iterable 147 """ 148 149 if (not isgenerator(row) and 150 not isinstance(row, (list, tuple, range)) 151 ): 152 self._invalid_row(row) 153 154 self._get_writer() 155 156 if self._rows is None: 157 self._rows = self._write_rows() 158 next(self._rows) 159 160 self._rows.send(row) 161 162 163 def _values_to_row(self, values, row_idx): 164 """ 165 Convert whatever has been appended into a form suitable for work_rows 166 """ 167 cell = WriteOnlyCell(self) 168 169 for col_idx, value in enumerate(values, 1): 170 if value is None: 171 continue 172 try: 173 cell.value = value 174 except ValueError: 175 if isinstance(value, Cell): 176 cell = value 177 else: 178 raise ValueError 179 180 cell.column = col_idx 181 cell.row = row_idx 182 183 if cell.hyperlink is not None: 184 cell.hyperlink.ref = cell.coordinate 185 186 yield cell 187 188 # reset cell if style applied 189 if cell.has_style or cell.hyperlink: 190 cell = WriteOnlyCell(self) 191 192 193 def _already_saved(self): 194 raise WorkbookAlreadySaved('Workbook has already been saved and cannot be modified or saved anymore.') 195 196 197 def _invalid_row(self, iterable): 198 raise TypeError('Value must be a list, tuple, range or a generator Supplied value is {0}'.format( 199 type(iterable)) 200 ) 201