Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/openpyxl/worksheet/_read_only.py: 30%

114 statements  

« prev     ^ index     » next       coverage.py v7.3.3, created at 2023-12-20 06:34 +0000

1# Copyright (c) 2010-2023 openpyxl 

2 

3""" Read worksheets on-demand 

4""" 

5 

6from .worksheet import Worksheet 

7from openpyxl.cell.read_only import ReadOnlyCell, EMPTY_CELL 

8from openpyxl.utils import get_column_letter 

9 

10from ._reader import WorkSheetParser 

11from openpyxl.workbook.defined_name import DefinedNameDict 

12 

13 

14def read_dimension(source): 

15 parser = WorkSheetParser(source, []) 

16 return parser.parse_dimensions() 

17 

18 

19class ReadOnlyWorksheet(object): 

20 

21 _min_column = 1 

22 _min_row = 1 

23 _max_column = _max_row = None 

24 

25 # from Standard Worksheet 

26 # Methods from Worksheet 

27 cell = Worksheet.cell 

28 iter_rows = Worksheet.iter_rows 

29 values = Worksheet.values 

30 rows = Worksheet.rows 

31 __getitem__ = Worksheet.__getitem__ 

32 __iter__ = Worksheet.__iter__ 

33 

34 

35 def __init__(self, parent_workbook, title, worksheet_path, shared_strings): 

36 self.parent = parent_workbook 

37 self.title = title 

38 self.sheet_state = 'visible' 

39 self._current_row = None 

40 self._worksheet_path = worksheet_path 

41 self._shared_strings = shared_strings 

42 self._get_size() 

43 self.defined_names = DefinedNameDict() 

44 

45 

46 def _get_size(self): 

47 src = self._get_source() 

48 parser = WorkSheetParser(src, []) 

49 dimensions = parser.parse_dimensions() 

50 src.close() 

51 if dimensions is not None: 

52 self._min_column, self._min_row, self._max_column, self._max_row = dimensions 

53 

54 

55 def _get_source(self): 

56 """Parse xml source on demand, must close after use""" 

57 return self.parent._archive.open(self._worksheet_path) 

58 

59 

60 def _cells_by_row(self, min_col, min_row, max_col, max_row, values_only=False): 

61 """ 

62 The source worksheet file may have columns or rows missing. 

63 Missing cells will be created. 

64 """ 

65 filler = EMPTY_CELL 

66 if values_only: 

67 filler = None 

68 

69 max_col = max_col or self.max_column 

70 max_row = max_row or self.max_row 

71 empty_row = [] 

72 if max_col is not None: 

73 empty_row = (filler,) * (max_col + 1 - min_col) 

74 

75 counter = min_row 

76 idx = 1 

77 src = self._get_source() 

78 parser = WorkSheetParser(src, self._shared_strings, 

79 data_only=self.parent.data_only, epoch=self.parent.epoch, 

80 date_formats=self.parent._date_formats) 

81 for idx, row in parser.parse(): 

82 if max_row is not None and idx > max_row: 

83 break 

84 

85 # some rows are missing 

86 for _ in range(counter, idx): 

87 counter += 1 

88 yield empty_row 

89 

90 # return cells from a row 

91 if counter <= idx: 

92 row = self._get_row(row, min_col, max_col, values_only) 

93 counter += 1 

94 yield row 

95 

96 src.close() # make sure source is always closed 

97 

98 if max_row is not None and max_row < idx: 

99 for _ in range(counter, max_row+1): 

100 yield empty_row 

101 

102 

103 def _get_row(self, row, min_col=1, max_col=None, values_only=False): 

104 """ 

105 Make sure a row contains always the same number of cells or values 

106 """ 

107 if not row and not max_col: # in case someone wants to force rows where there aren't any 

108 return () 

109 

110 max_col = max_col or row[-1]['column'] 

111 row_width = max_col + 1 - min_col 

112 

113 new_row = [EMPTY_CELL] * row_width 

114 if values_only: 

115 new_row = [None] * row_width 

116 

117 for cell in row: 

118 counter = cell['column'] 

119 if min_col <= counter <= max_col: 

120 idx = counter - min_col # position in list of cells returned 

121 new_row[idx] = cell['value'] 

122 if not values_only: 

123 new_row[idx] = ReadOnlyCell(self, **cell) 

124 

125 return tuple(new_row) 

126 

127 

128 def _get_cell(self, row, column): 

129 """Cells are returned by a generator which can be empty""" 

130 for row in self._cells_by_row(column, row, column, row): 

131 if row: 

132 return row[0] 

133 return EMPTY_CELL 

134 

135 

136 def calculate_dimension(self, force=False): 

137 if not all([self.max_column, self.max_row]): 

138 if force: 

139 self._calculate_dimension() 

140 else: 

141 raise ValueError("Worksheet is unsized, use calculate_dimension(force=True)") 

142 return f"{get_column_letter(self.min_column)}{self.min_row}:{get_column_letter(self.max_column)}{self.max_row}" 

143 

144 

145 def _calculate_dimension(self): 

146 """ 

147 Loop through all the cells to get the size of a worksheet. 

148 Do this only if it is explicitly requested. 

149 """ 

150 

151 max_col = 0 

152 for r in self.rows: 

153 if not r: 

154 continue 

155 cell = r[-1] 

156 max_col = max(max_col, cell.column) 

157 

158 self._max_row = cell.row 

159 self._max_column = max_col 

160 

161 

162 def reset_dimensions(self): 

163 """ 

164 Remove worksheet dimensions if these are incorrect in the worksheet source. 

165 NB. This probably indicates a bug in the library or application that created 

166 the workbook. 

167 """ 

168 self._max_row = self._max_column = None 

169 

170 

171 @property 

172 def min_row(self): 

173 return self._min_row 

174 

175 

176 @property 

177 def max_row(self): 

178 return self._max_row 

179 

180 

181 @property 

182 def min_column(self): 

183 return self._min_column 

184 

185 

186 @property 

187 def max_column(self): 

188 return self._max_column