Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/pandas/io/excel/_xlsxwriter.py: 21%

89 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from __future__ import annotations 

2 

3from typing import ( 

4 TYPE_CHECKING, 

5 Any, 

6) 

7 

8import pandas._libs.json as json 

9from pandas._typing import ( 

10 FilePath, 

11 StorageOptions, 

12 WriteExcelBuffer, 

13) 

14 

15from pandas.io.excel._base import ExcelWriter 

16from pandas.io.excel._util import ( 

17 combine_kwargs, 

18 validate_freeze_panes, 

19) 

20 

21if TYPE_CHECKING: 21 ↛ 22line 21 didn't jump to line 22, because the condition on line 21 was never true

22 from xlsxwriter import Workbook 

23 

24 

25class _XlsxStyler: 

26 # Map from openpyxl-oriented styles to flatter xlsxwriter representation 

27 # Ordering necessary for both determinism and because some are keyed by 

28 # prefixes of others. 

29 STYLE_MAPPING: dict[str, list[tuple[tuple[str, ...], str]]] = { 

30 "font": [ 

31 (("name",), "font_name"), 

32 (("sz",), "font_size"), 

33 (("size",), "font_size"), 

34 (("color", "rgb"), "font_color"), 

35 (("color",), "font_color"), 

36 (("b",), "bold"), 

37 (("bold",), "bold"), 

38 (("i",), "italic"), 

39 (("italic",), "italic"), 

40 (("u",), "underline"), 

41 (("underline",), "underline"), 

42 (("strike",), "font_strikeout"), 

43 (("vertAlign",), "font_script"), 

44 (("vertalign",), "font_script"), 

45 ], 

46 "number_format": [(("format_code",), "num_format"), ((), "num_format")], 

47 "protection": [(("locked",), "locked"), (("hidden",), "hidden")], 

48 "alignment": [ 

49 (("horizontal",), "align"), 

50 (("vertical",), "valign"), 

51 (("text_rotation",), "rotation"), 

52 (("wrap_text",), "text_wrap"), 

53 (("indent",), "indent"), 

54 (("shrink_to_fit",), "shrink"), 

55 ], 

56 "fill": [ 

57 (("patternType",), "pattern"), 

58 (("patterntype",), "pattern"), 

59 (("fill_type",), "pattern"), 

60 (("start_color", "rgb"), "fg_color"), 

61 (("fgColor", "rgb"), "fg_color"), 

62 (("fgcolor", "rgb"), "fg_color"), 

63 (("start_color",), "fg_color"), 

64 (("fgColor",), "fg_color"), 

65 (("fgcolor",), "fg_color"), 

66 (("end_color", "rgb"), "bg_color"), 

67 (("bgColor", "rgb"), "bg_color"), 

68 (("bgcolor", "rgb"), "bg_color"), 

69 (("end_color",), "bg_color"), 

70 (("bgColor",), "bg_color"), 

71 (("bgcolor",), "bg_color"), 

72 ], 

73 "border": [ 

74 (("color", "rgb"), "border_color"), 

75 (("color",), "border_color"), 

76 (("style",), "border"), 

77 (("top", "color", "rgb"), "top_color"), 

78 (("top", "color"), "top_color"), 

79 (("top", "style"), "top"), 

80 (("top",), "top"), 

81 (("right", "color", "rgb"), "right_color"), 

82 (("right", "color"), "right_color"), 

83 (("right", "style"), "right"), 

84 (("right",), "right"), 

85 (("bottom", "color", "rgb"), "bottom_color"), 

86 (("bottom", "color"), "bottom_color"), 

87 (("bottom", "style"), "bottom"), 

88 (("bottom",), "bottom"), 

89 (("left", "color", "rgb"), "left_color"), 

90 (("left", "color"), "left_color"), 

91 (("left", "style"), "left"), 

92 (("left",), "left"), 

93 ], 

94 } 

95 

96 @classmethod 

97 def convert(cls, style_dict, num_format_str=None): 

98 """ 

99 converts a style_dict to an xlsxwriter format dict 

100 

101 Parameters 

102 ---------- 

103 style_dict : style dictionary to convert 

104 num_format_str : optional number format string 

105 """ 

106 # Create a XlsxWriter format object. 

107 props = {} 

108 

109 if num_format_str is not None: 

110 props["num_format"] = num_format_str 

111 

112 if style_dict is None: 

113 return props 

114 

115 if "borders" in style_dict: 

116 style_dict = style_dict.copy() 

117 style_dict["border"] = style_dict.pop("borders") 

118 

119 for style_group_key, style_group in style_dict.items(): 

120 for src, dst in cls.STYLE_MAPPING.get(style_group_key, []): 

121 # src is a sequence of keys into a nested dict 

122 # dst is a flat key 

123 if dst in props: 

124 continue 

125 v = style_group 

126 for k in src: 

127 try: 

128 v = v[k] 

129 except (KeyError, TypeError): 

130 break 

131 else: 

132 props[dst] = v 

133 

134 if isinstance(props.get("pattern"), str): 

135 # TODO: support other fill patterns 

136 props["pattern"] = 0 if props["pattern"] == "none" else 1 

137 

138 for k in ["border", "top", "right", "bottom", "left"]: 

139 if isinstance(props.get(k), str): 

140 try: 

141 props[k] = [ 

142 "none", 

143 "thin", 

144 "medium", 

145 "dashed", 

146 "dotted", 

147 "thick", 

148 "double", 

149 "hair", 

150 "mediumDashed", 

151 "dashDot", 

152 "mediumDashDot", 

153 "dashDotDot", 

154 "mediumDashDotDot", 

155 "slantDashDot", 

156 ].index(props[k]) 

157 except ValueError: 

158 props[k] = 2 

159 

160 if isinstance(props.get("font_script"), str): 

161 props["font_script"] = ["baseline", "superscript", "subscript"].index( 

162 props["font_script"] 

163 ) 

164 

165 if isinstance(props.get("underline"), str): 

166 props["underline"] = { 

167 "none": 0, 

168 "single": 1, 

169 "double": 2, 

170 "singleAccounting": 33, 

171 "doubleAccounting": 34, 

172 }[props["underline"]] 

173 

174 # GH 30107 - xlsxwriter uses different name 

175 if props.get("valign") == "center": 

176 props["valign"] = "vcenter" 

177 

178 return props 

179 

180 

181class XlsxWriter(ExcelWriter): 

182 _engine = "xlsxwriter" 

183 _supported_extensions = (".xlsx",) 

184 

185 def __init__( 

186 self, 

187 path: FilePath | WriteExcelBuffer | ExcelWriter, 

188 engine: str | None = None, 

189 date_format: str | None = None, 

190 datetime_format: str | None = None, 

191 mode: str = "w", 

192 storage_options: StorageOptions = None, 

193 if_sheet_exists: str | None = None, 

194 engine_kwargs: dict[str, Any] | None = None, 

195 **kwargs, 

196 ) -> None: 

197 # Use the xlsxwriter module as the Excel writer. 

198 from xlsxwriter import Workbook 

199 

200 engine_kwargs = combine_kwargs(engine_kwargs, kwargs) 

201 

202 if mode == "a": 

203 raise ValueError("Append mode is not supported with xlsxwriter!") 

204 

205 super().__init__( 

206 path, 

207 engine=engine, 

208 date_format=date_format, 

209 datetime_format=datetime_format, 

210 mode=mode, 

211 storage_options=storage_options, 

212 if_sheet_exists=if_sheet_exists, 

213 engine_kwargs=engine_kwargs, 

214 ) 

215 

216 self._book = Workbook(self._handles.handle, **engine_kwargs) 

217 

218 @property 

219 def book(self): 

220 """ 

221 Book instance of class xlsxwriter.Workbook. 

222 

223 This attribute can be used to access engine-specific features. 

224 """ 

225 return self._book 

226 

227 @book.setter 

228 def book(self, other: Workbook) -> None: 

229 """ 

230 Set book instance. Class type will depend on the engine used. 

231 """ 

232 self._deprecate_set_book() 

233 self._book = other 

234 

235 @property 

236 def sheets(self) -> dict[str, Any]: 

237 result = self.book.sheetnames 

238 return result 

239 

240 def _save(self) -> None: 

241 """ 

242 Save workbook to disk. 

243 """ 

244 self.book.close() 

245 

246 def _write_cells( 

247 self, 

248 cells, 

249 sheet_name: str | None = None, 

250 startrow: int = 0, 

251 startcol: int = 0, 

252 freeze_panes: tuple[int, int] | None = None, 

253 ) -> None: 

254 # Write the frame cells using xlsxwriter. 

255 sheet_name = self._get_sheet_name(sheet_name) 

256 

257 wks = self.book.get_worksheet_by_name(sheet_name) 

258 if wks is None: 

259 wks = self.book.add_worksheet(sheet_name) 

260 

261 style_dict = {"null": None} 

262 

263 if validate_freeze_panes(freeze_panes): 

264 wks.freeze_panes(*(freeze_panes)) 

265 

266 for cell in cells: 

267 val, fmt = self._value_with_fmt(cell.val) 

268 

269 stylekey = json.dumps(cell.style) 

270 if fmt: 

271 stylekey += fmt 

272 

273 if stylekey in style_dict: 

274 style = style_dict[stylekey] 

275 else: 

276 style = self.book.add_format(_XlsxStyler.convert(cell.style, fmt)) 

277 style_dict[stylekey] = style 

278 

279 if cell.mergestart is not None and cell.mergeend is not None: 

280 wks.merge_range( 

281 startrow + cell.row, 

282 startcol + cell.col, 

283 startrow + cell.mergestart, 

284 startcol + cell.mergeend, 

285 val, 

286 style, 

287 ) 

288 else: 

289 wks.write(startrow + cell.row, startcol + cell.col, val, style)