1
- """Utilities for conversion to writer-agnostic Excel representation
1
+ """
2
+ Utilities for conversion to writer-agnostic Excel representation.
2
3
"""
3
4
4
5
from functools import reduce
5
6
import itertools
6
7
import re
8
+ from typing import Any , Callable , Dict , List , Optional , Sequence , Union
7
9
import warnings
8
10
9
11
import numpy as np
@@ -25,7 +27,9 @@ class ExcelCell:
25
27
__fields__ = ("row" , "col" , "val" , "style" , "mergestart" , "mergeend" )
26
28
__slots__ = __fields__
27
29
28
- def __init__ (self , row , col , val , style = None , mergestart = None , mergeend = None ):
30
+ def __init__ (
31
+ self , row : int , col : int , val , style = None , mergestart = None , mergeend = None
32
+ ):
29
33
self .row = row
30
34
self .col = col
31
35
self .val = val
@@ -35,7 +39,8 @@ def __init__(self, row, col, val, style=None, mergestart=None, mergeend=None):
35
39
36
40
37
41
class CSSToExcelConverter :
38
- """A callable for converting CSS declarations to ExcelWriter styles
42
+ """
43
+ A callable for converting CSS declarations to ExcelWriter styles.
39
44
40
45
Supports parts of CSS 2.2, with minimal CSS 3.0 support (e.g. text-shadow),
41
46
focusing on font styling, backgrounds, borders and alignment.
@@ -56,15 +61,15 @@ class CSSToExcelConverter:
56
61
# instancemethods so that users can easily experiment with extensions
57
62
# without monkey-patching.
58
63
59
- def __init__ (self , inherited = None ):
64
+ def __init__ (self , inherited : Optional [ str ] = None ):
60
65
if inherited is not None :
61
66
inherited = self .compute_css (inherited )
62
67
63
68
self .inherited = inherited
64
69
65
70
compute_css = CSSResolver ()
66
71
67
- def __call__ (self , declarations_str : str ):
72
+ def __call__ (self , declarations_str : str ) -> Dict [ str , Dict ] :
68
73
"""
69
74
Convert CSS declarations to ExcelWriter style.
70
75
@@ -76,15 +81,23 @@ def __call__(self, declarations_str: str):
76
81
77
82
Returns
78
83
-------
79
- xlstyle : dict
80
- A style as interpreted by ExcelWriter when found in
81
- ExcelCell.style.
84
+ Dict
85
+ A style as interpreted by ExcelWriter when found in ExcelCell.style.
82
86
"""
83
87
# TODO: memoize?
84
88
properties = self .compute_css (declarations_str , self .inherited )
85
89
return self .build_xlstyle (properties )
86
90
87
- def build_xlstyle (self , props ):
91
+ def build_xlstyle (self , props : Dict ) -> Dict [str , Dict ]:
92
+ """
93
+ Parameters
94
+ ----------
95
+ props : Dict
96
+
97
+ Returns
98
+ -------
99
+ Dict
100
+ """
88
101
out = {
89
102
"alignment" : self .build_alignment (props ),
90
103
"border" : self .build_border (props ),
@@ -95,8 +108,14 @@ def build_xlstyle(self, props):
95
108
96
109
# TODO: handle cell width and height: needs support in pandas.io.excel
97
110
98
- def remove_none (d ):
99
- """Remove key where value is None, through nested dicts"""
111
+ def remove_none (d : Dict [str , Any ]) -> None :
112
+ """
113
+ Remove key where value is None, through nested dicts.
114
+
115
+ Parameters
116
+ ----------
117
+ d : Dict
118
+ """
100
119
for k , v in list (d .items ()):
101
120
if v is None :
102
121
del d [k ]
@@ -118,7 +137,12 @@ def remove_none(d):
118
137
# OpenXML also has 'justify', 'distributed'
119
138
}
120
139
121
- def build_alignment (self , props ):
140
+ def build_alignment (self , props ) -> Dict [str , Any ]:
141
+ """
142
+ Returns
143
+ -------
144
+ Dict
145
+ """
122
146
# TODO: text-indent, padding-left -> alignment.indent
123
147
return {
124
148
"horizontal" : props .get ("text-align" ),
@@ -130,7 +154,16 @@ def build_alignment(self, props):
130
154
),
131
155
}
132
156
133
- def build_border (self , props ):
157
+ def build_border (self , props : Dict ) -> Dict [str , Any ]:
158
+ """
159
+ Parameters
160
+ ----------
161
+ props : Dict
162
+
163
+ Returns
164
+ -------
165
+ Dict
166
+ """
134
167
return {
135
168
side : {
136
169
"style" : self ._border_style (
@@ -142,7 +175,7 @@ def build_border(self, props):
142
175
for side in ["top" , "right" , "bottom" , "left" ]
143
176
}
144
177
145
- def _border_style (self , style , width ):
178
+ def _border_style (self , style : Optional [ str ] , width ):
146
179
# convert styles and widths to openxml, one of:
147
180
# 'dashDot'
148
181
# 'dashDotDot'
@@ -191,7 +224,12 @@ def _border_style(self, style, width):
191
224
return "dashed"
192
225
return "mediumDashed"
193
226
194
- def build_fill (self , props ):
227
+ def build_fill (self , props : Dict ):
228
+ """
229
+ Parameters
230
+ ----------
231
+ props : Dict
232
+ """
195
233
# TODO: perhaps allow for special properties
196
234
# -excel-pattern-bgcolor and -excel-pattern-type
197
235
fill_color = props .get ("background-color" )
@@ -215,7 +253,12 @@ def build_fill(self, props):
215
253
}
216
254
ITALIC_MAP = {"normal" : False , "italic" : True , "oblique" : True }
217
255
218
- def build_font (self , props ):
256
+ def build_font (self , props ) -> Dict [str , Union [bool , int , str , None ]]:
257
+ """
258
+ Returns
259
+ -------
260
+ Dict
261
+ """
219
262
size = props .get ("font-size" )
220
263
if size is not None :
221
264
assert size .endswith ("pt" )
@@ -311,7 +354,12 @@ def build_font(self, props):
311
354
"white" : "FFFFFF" ,
312
355
}
313
356
314
- def color_to_excel (self , val ):
357
+ def color_to_excel (self , val : Optional [str ]):
358
+ """
359
+ Parameters
360
+ ----------
361
+ val : str, optional
362
+ """
315
363
if val is None :
316
364
return None
317
365
if val .startswith ("#" ) and len (val ) == 7 :
@@ -323,37 +371,39 @@ def color_to_excel(self, val):
323
371
except KeyError :
324
372
warnings .warn (f"Unhandled color format: { repr (val )} " , CSSWarning )
325
373
326
- def build_number_format (self , props ) :
374
+ def build_number_format (self , props : Dict ) -> Dict [ str , Any ] :
327
375
return {"format_code" : props .get ("number-format" )}
328
376
329
377
330
378
class ExcelFormatter :
331
379
"""
332
- Class for formatting a DataFrame to a list of ExcelCells,
380
+ Class for formatting a DataFrame to a list of ExcelCells.
333
381
334
382
Parameters
335
383
----------
336
384
df : DataFrame or Styler
337
- na_rep: na representation
338
- float_format : string, default None
339
- Format string for floating point numbers
340
- cols : sequence, optional
341
- Columns to write
342
- header : boolean or list of string, default True
343
- Write out column names. If a list of string is given it is
344
- assumed to be aliases for the column names
345
- index : boolean, default True
346
- output row names (index)
347
- index_label : string or sequence, default None
348
- Column label for index column(s) if desired. If None is given, and
349
- `header` and `index` are True, then the index names are used. A
350
- sequence should be given if the DataFrame uses MultiIndex.
351
- merge_cells : boolean, default False
385
+ na_rep: str
386
+ An na representation.
387
+ float_format : str, optional
388
+ Format string for floating point numbers.
389
+ cols : Sequence, optional
390
+ Columns to write.
391
+ header : Union[bool, List[str]], default True
392
+ Write out column names.
393
+ If a list of string is given it is assumed to be aliases for the column names
394
+ index : bool, default True
395
+ Output row names (index).
396
+ index_label : Union[str, Sequence, None], default None
397
+ Column label for index column(s) if desired.
398
+ If None is given, and `header` and `index` are True,
399
+ then the index names are used.
400
+ A Sequence should be given if the DataFrame uses MultiIndex.
401
+ merge_cells : bool, default False
352
402
Format MultiIndex and Hierarchical Rows as merged cells.
353
- inf_rep : string , default `'inf'`
403
+ inf_rep : str , default `'inf'`
354
404
representation for np.inf values (which aren't representable in Excel)
355
405
A `'-'` sign will be added in front of -inf.
356
- style_converter : callable , optional
406
+ style_converter : Callable , optional
357
407
This translates Styler styles (CSS) into ExcelWriter styles.
358
408
Defaults to ``CSSToExcelConverter()``.
359
409
It should have signature css_declarations string -> excel style.
@@ -366,15 +416,15 @@ class ExcelFormatter:
366
416
def __init__ (
367
417
self ,
368
418
df ,
369
- na_rep = "" ,
370
- float_format = None ,
371
- cols = None ,
372
- header = True ,
373
- index = True ,
374
- index_label = None ,
375
- merge_cells = False ,
376
- inf_rep = "inf" ,
377
- style_converter = None ,
419
+ na_rep : str = "" ,
420
+ float_format : Optional [ str ] = None ,
421
+ cols : Optional [ Sequence ] = None ,
422
+ header : Union [ bool , List [ str ]] = True ,
423
+ index : bool = True ,
424
+ index_label : Union [ str , Sequence , None ] = None ,
425
+ merge_cells : bool = False ,
426
+ inf_rep : str = "inf" ,
427
+ style_converter : Optional [ Callable ] = None ,
378
428
):
379
429
self .rowcounter = 0
380
430
self .na_rep = na_rep
@@ -442,10 +492,8 @@ def _format_header_mi(self):
442
492
if self .columns .nlevels > 1 :
443
493
if not self .index :
444
494
raise NotImplementedError (
445
- "Writing to Excel with MultiIndex"
446
- " columns and no index "
447
- "('index'=False) is not yet "
448
- "implemented."
495
+ "Writing to Excel with MultiIndex columns and no "
496
+ "index ('index'=False) is not yet implemented."
449
497
)
450
498
451
499
has_aliases = isinstance (self .header , (tuple , list , np .ndarray , Index ))
@@ -540,7 +588,6 @@ def _format_header(self):
540
588
return itertools .chain (gen , gen2 )
541
589
542
590
def _format_body (self ):
543
-
544
591
if isinstance (self .df .index , ABCMultiIndex ):
545
592
return self ._format_hierarchical_rows ()
546
593
else :
@@ -706,7 +753,7 @@ def write(
706
753
freeze_panes : tuple of integer (length 2), default None
707
754
Specifies the one-based bottommost row and rightmost column that
708
755
is to be frozen
709
- engine : string , default None
756
+ engine : str , default None
710
757
write engine to use if writer is a path - you can also set this
711
758
via the options ``io.excel.xlsx.writer``, ``io.excel.xls.writer``,
712
759
and ``io.excel.xlsm.writer``.
@@ -716,8 +763,7 @@ def write(
716
763
num_rows , num_cols = self .df .shape
717
764
if num_rows > self .max_rows or num_cols > self .max_cols :
718
765
raise ValueError (
719
- "This sheet is too large! Your sheet size is: "
720
- f"{ num_rows } , { num_cols } "
766
+ f"This sheet is too large! Your sheet size is: { num_rows } , { num_cols } "
721
767
f"Max sheet size is: { self .max_rows } , { self .max_cols } "
722
768
)
723
769
0 commit comments