Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from pandas.compat._optional import import_optional_dependency 

2 

3from pandas.core.dtypes.common import is_integer, is_list_like 

4 

5_writers = {} 

6 

7 

8def register_writer(klass): 

9 """ 

10 Add engine to the excel writer registry.io.excel. 

11 

12 You must use this method to integrate with ``to_excel``. 

13 

14 Parameters 

15 ---------- 

16 klass : ExcelWriter 

17 """ 

18 if not callable(klass): 

19 raise ValueError("Can only register callables as engines") 

20 engine_name = klass.engine 

21 _writers[engine_name] = klass 

22 

23 

24def _get_default_writer(ext): 

25 """ 

26 Return the default writer for the given extension. 

27 

28 Parameters 

29 ---------- 

30 ext : str 

31 The excel file extension for which to get the default engine. 

32 

33 Returns 

34 ------- 

35 str 

36 The default engine for the extension. 

37 """ 

38 _default_writers = {"xlsx": "openpyxl", "xlsm": "openpyxl", "xls": "xlwt"} 

39 xlsxwriter = import_optional_dependency( 

40 "xlsxwriter", raise_on_missing=False, on_version="warn" 

41 ) 

42 if xlsxwriter: 

43 _default_writers["xlsx"] = "xlsxwriter" 

44 return _default_writers[ext] 

45 

46 

47def get_writer(engine_name): 

48 try: 

49 return _writers[engine_name] 

50 except KeyError: 

51 raise ValueError(f"No Excel writer '{engine_name}'") 

52 

53 

54def _excel2num(x): 

55 """ 

56 Convert Excel column name like 'AB' to 0-based column index. 

57 

58 Parameters 

59 ---------- 

60 x : str 

61 The Excel column name to convert to a 0-based column index. 

62 

63 Returns 

64 ------- 

65 num : int 

66 The column index corresponding to the name. 

67 

68 Raises 

69 ------ 

70 ValueError 

71 Part of the Excel column name was invalid. 

72 """ 

73 index = 0 

74 

75 for c in x.upper().strip(): 

76 cp = ord(c) 

77 

78 if cp < ord("A") or cp > ord("Z"): 

79 raise ValueError(f"Invalid column name: {x}") 

80 

81 index = index * 26 + cp - ord("A") + 1 

82 

83 return index - 1 

84 

85 

86def _range2cols(areas): 

87 """ 

88 Convert comma separated list of column names and ranges to indices. 

89 

90 Parameters 

91 ---------- 

92 areas : str 

93 A string containing a sequence of column ranges (or areas). 

94 

95 Returns 

96 ------- 

97 cols : list 

98 A list of 0-based column indices. 

99 

100 Examples 

101 -------- 

102 >>> _range2cols('A:E') 

103 [0, 1, 2, 3, 4] 

104 >>> _range2cols('A,C,Z:AB') 

105 [0, 2, 25, 26, 27] 

106 """ 

107 cols = [] 

108 

109 for rng in areas.split(","): 

110 if ":" in rng: 

111 rng = rng.split(":") 

112 cols.extend(range(_excel2num(rng[0]), _excel2num(rng[1]) + 1)) 

113 else: 

114 cols.append(_excel2num(rng)) 

115 

116 return cols 

117 

118 

119def _maybe_convert_usecols(usecols): 

120 """ 

121 Convert `usecols` into a compatible format for parsing in `parsers.py`. 

122 

123 Parameters 

124 ---------- 

125 usecols : object 

126 The use-columns object to potentially convert. 

127 

128 Returns 

129 ------- 

130 converted : object 

131 The compatible format of `usecols`. 

132 """ 

133 if usecols is None: 

134 return usecols 

135 

136 if is_integer(usecols): 

137 raise ValueError( 

138 "Passing an integer for `usecols` is no longer supported. " 

139 "Please pass in a list of int from 0 to `usecols` " 

140 "inclusive instead." 

141 ) 

142 

143 if isinstance(usecols, str): 

144 return _range2cols(usecols) 

145 

146 return usecols 

147 

148 

149def _validate_freeze_panes(freeze_panes): 

150 if freeze_panes is not None: 

151 if len(freeze_panes) == 2 and all( 

152 isinstance(item, int) for item in freeze_panes 

153 ): 

154 return True 

155 

156 raise ValueError( 

157 "freeze_panes must be of form (row, column) " 

158 "where row and column are integers" 

159 ) 

160 

161 # freeze_panes wasn't specified, return False so it won't be applied 

162 # to output sheet 

163 return False 

164 

165 

166def _trim_excel_header(row): 

167 # trim header row so auto-index inference works 

168 # xlrd uses '' , openpyxl None 

169 while len(row) > 0 and (row[0] == "" or row[0] is None): 

170 row = row[1:] 

171 return row 

172 

173 

174def _fill_mi_header(row, control_row): 

175 """Forward fill blank entries in row but only inside the same parent index. 

176 

177 Used for creating headers in Multiindex. 

178 Parameters 

179 ---------- 

180 row : list 

181 List of items in a single row. 

182 control_row : list of bool 

183 Helps to determine if particular column is in same parent index as the 

184 previous value. Used to stop propagation of empty cells between 

185 different indexes. 

186 

187 Returns 

188 ------- 

189 Returns changed row and control_row 

190 """ 

191 last = row[0] 

192 for i in range(1, len(row)): 

193 if not control_row[i]: 

194 last = row[i] 

195 

196 if row[i] == "" or row[i] is None: 

197 row[i] = last 

198 else: 

199 control_row[i] = False 

200 last = row[i] 

201 

202 return row, control_row 

203 

204 

205def _pop_header_name(row, index_col): 

206 """ 

207 Pop the header name for MultiIndex parsing. 

208 

209 Parameters 

210 ---------- 

211 row : list 

212 The data row to parse for the header name. 

213 index_col : int, list 

214 The index columns for our data. Assumed to be non-null. 

215 

216 Returns 

217 ------- 

218 header_name : str 

219 The extracted header name. 

220 trimmed_row : list 

221 The original data row with the header name removed. 

222 """ 

223 # Pop out header name and fill w/blank. 

224 i = index_col if not is_list_like(index_col) else max(index_col) 

225 

226 header_name = row[i] 

227 header_name = None if header_name == "" else header_name 

228 

229 return header_name, row[:i] + [""] + row[i + 1 :]