_write_only.py 5.0 KB

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