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