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
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1from __future__ import annotations
3from typing import (
4 TYPE_CHECKING,
5 Any,
6)
8import pandas._libs.json as json
9from pandas._typing import (
10 FilePath,
11 StorageOptions,
12 WriteExcelBuffer,
13)
15from pandas.io.excel._base import ExcelWriter
16from pandas.io.excel._util import (
17 combine_kwargs,
18 validate_freeze_panes,
19)
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
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 }
96 @classmethod
97 def convert(cls, style_dict, num_format_str=None):
98 """
99 converts a style_dict to an xlsxwriter format dict
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 = {}
109 if num_format_str is not None:
110 props["num_format"] = num_format_str
112 if style_dict is None:
113 return props
115 if "borders" in style_dict:
116 style_dict = style_dict.copy()
117 style_dict["border"] = style_dict.pop("borders")
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
134 if isinstance(props.get("pattern"), str):
135 # TODO: support other fill patterns
136 props["pattern"] = 0 if props["pattern"] == "none" else 1
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
160 if isinstance(props.get("font_script"), str):
161 props["font_script"] = ["baseline", "superscript", "subscript"].index(
162 props["font_script"]
163 )
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"]]
174 # GH 30107 - xlsxwriter uses different name
175 if props.get("valign") == "center":
176 props["valign"] = "vcenter"
178 return props
181class XlsxWriter(ExcelWriter):
182 _engine = "xlsxwriter"
183 _supported_extensions = (".xlsx",)
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
200 engine_kwargs = combine_kwargs(engine_kwargs, kwargs)
202 if mode == "a":
203 raise ValueError("Append mode is not supported with xlsxwriter!")
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 )
216 self._book = Workbook(self._handles.handle, **engine_kwargs)
218 @property
219 def book(self):
220 """
221 Book instance of class xlsxwriter.Workbook.
223 This attribute can be used to access engine-specific features.
224 """
225 return self._book
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
235 @property
236 def sheets(self) -> dict[str, Any]:
237 result = self.book.sheetnames
238 return result
240 def _save(self) -> None:
241 """
242 Save workbook to disk.
243 """
244 self.book.close()
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)
257 wks = self.book.get_worksheet_by_name(sheet_name)
258 if wks is None:
259 wks = self.book.add_worksheet(sheet_name)
261 style_dict = {"null": None}
263 if validate_freeze_panes(freeze_panes):
264 wks.freeze_panes(*(freeze_panes))
266 for cell in cells:
267 val, fmt = self._value_with_fmt(cell.val)
269 stylekey = json.dumps(cell.style)
270 if fmt:
271 stylekey += fmt
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
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)