24
24
_writer_extensions = ["xlsx" , "xls" , "xlsm" ]
25
25
_writers = {}
26
26
27
+
27
28
def register_writer (klass ):
28
29
"""Adds engine to the excel writer registry. You must use this method to
29
30
integrate with ``to_excel``. Also adds config options for any new
@@ -40,12 +41,14 @@ def register_writer(klass):
40
41
engine_name , validator = str )
41
42
_writer_extensions .append (ext )
42
43
44
+
43
45
def get_writer (engine_name ):
44
46
try :
45
47
return _writers [engine_name ]
46
48
except KeyError :
47
49
raise ValueError ("No Excel writer '%s'" % engine_name )
48
50
51
+
49
52
def read_excel (io , sheetname , ** kwds ):
50
53
"""Read an Excel table into a pandas DataFrame
51
54
@@ -80,7 +83,7 @@ def read_excel(io, sheetname, **kwds):
80
83
engine: string, default None
81
84
If io is not a buffer or path, this must be set to identify io.
82
85
Acceptable values are None or xlrd
83
-
86
+
84
87
Returns
85
88
-------
86
89
parsed : DataFrame
@@ -90,9 +93,9 @@ def read_excel(io, sheetname, **kwds):
90
93
kwds .pop ('kind' )
91
94
warn ("kind keyword is no longer supported in read_excel and may be "
92
95
"removed in a future version" , FutureWarning )
93
-
94
- engine = kwds .pop ('engine' , None )
95
-
96
+
97
+ engine = kwds .pop ('engine' , None )
98
+
96
99
return ExcelFile (io , engine = engine ).parse (sheetname = sheetname , ** kwds )
97
100
98
101
@@ -119,9 +122,9 @@ def __init__(self, io, **kwds):
119
122
"support, current version " + xlrd .__VERSION__ )
120
123
121
124
self .io = io
122
-
125
+
123
126
engine = kwds .pop ('engine' , None )
124
-
127
+
125
128
if engine is not None and engine != 'xlrd' :
126
129
raise ValueError ("Unknown engine: %s" % engine )
127
130
@@ -133,7 +136,8 @@ def __init__(self, io, **kwds):
133
136
data = io .read ()
134
137
self .book = xlrd .open_workbook (file_contents = data )
135
138
else :
136
- raise ValueError ('Must explicitly set engine if not passing in buffer or path for io.' )
139
+ raise ValueError ('Must explicitly set engine if not passing in'
140
+ ' buffer or path for io.' )
137
141
138
142
def parse (self , sheetname , header = 0 , skiprows = None , skip_footer = 0 ,
139
143
index_col = None , parse_cols = None , parse_dates = False ,
@@ -291,13 +295,15 @@ def __enter__(self):
291
295
def __exit__ (self , exc_type , exc_value , traceback ):
292
296
self .close ()
293
297
298
+
294
299
def _trim_excel_header (row ):
295
300
# trim header row so auto-index inference works
296
301
# xlrd uses '' , openpyxl None
297
302
while len (row ) > 0 and (row [0 ] == '' or row [0 ] is None ):
298
303
row = row [1 :]
299
304
return row
300
305
306
+
301
307
def _conv_value (val ):
302
308
# Convert numpy types to Python types for the Excel writers.
303
309
if com .is_integer (val ):
@@ -312,34 +318,45 @@ def _conv_value(val):
312
318
return val
313
319
314
320
315
- class ExcelWriterMeta (abc .ABCMeta ):
321
+ @add_metaclass (abc .ABCMeta )
322
+ class ExcelWriter (object ):
316
323
"""
317
- Metaclass that dynamically chooses the ExcelWriter to use.
318
-
319
- If you directly instantiate a subclass, it skips the engine lookup.
320
-
321
- Defining an ExcelWriter implementation (see abstract methods on ExcelWriter for more...).
322
-
323
- - Mandatory (but not checked at run time):
324
- - ``write_cells(self, cells, sheet_name=None, startrow=0, startcol=0)``
325
- --> called to write additional DataFrames to disk
326
- - ``supported_extensions`` (tuple of supported extensions), used to check
327
- that engine supports the given extension.
328
- - ``engine`` - string that gives the engine name. Necessary to
329
- instantiate class directly and bypass ``ExcelWriterMeta`` engine lookup.
330
- - ``save(self)`` --> called to save file to disk
331
- - Optional:
332
- - ``__init__(self, path, **kwargs)`` --> always called with path as first
333
- argument.
324
+ Class for writing DataFrame objects into excel sheets, default is to use
325
+ xlwt for xls, openpyxl for xlsx. See DataFrame.to_excel for typical usage.
334
326
335
- You also need to register the class with ``register_writer()``.
327
+ Parameters
328
+ ----------
329
+ path : string
330
+ Path to xls or xlsx file.
331
+ engine : string (optional)
332
+ Engine to use for writing. If None, defaults to
333
+ ``io.excel.<extension>.writer``. NOTE: can only be passed as a keyword
334
+ argument.
336
335
"""
337
-
338
- def __call__ (cls , path , ** kwargs ):
339
- engine = kwargs .pop ('engine' , None )
340
- # if it's not an ExcelWriter baseclass, dont' do anything (you've
341
- # probably made an explicit choice here)
342
- if not isinstance (getattr (cls , 'engine' , None ), compat .string_types ):
336
+ # Defining an ExcelWriter implementation (see abstract methods for more...)
337
+
338
+ # - Mandatory
339
+ # - ``write_cells(self, cells, sheet_name=None, startrow=0, startcol=0)``
340
+ # --> called to write additional DataFrames to disk
341
+ # - ``supported_extensions`` (tuple of supported extensions), used to
342
+ # check that engine supports the given extension.
343
+ # - ``engine`` - string that gives the engine name. Necessary to
344
+ # instantiate class directly and bypass ``ExcelWriterMeta`` engine
345
+ # lookup.
346
+ # - ``save(self)`` --> called to save file to disk
347
+ # - Mostly mandatory (i.e. should at least exist)
348
+ # - book, cur_sheet, path
349
+
350
+ # - Optional:
351
+ # - ``__init__(self, path, engine=None, **kwargs)`` --> always called
352
+ # with path as first argument.
353
+
354
+ # You also need to register the class with ``register_writer()``.
355
+ # Technically, ExcelWriter implementations don't need to subclass
356
+ # ExcelWriter.
357
+ def __new__ (cls , path , engine = None , ** kwargs ):
358
+ # only switch class if generic(ExcelWriter)
359
+ if cls == ExcelWriter :
343
360
if engine is None :
344
361
ext = os .path .splitext (path )[- 1 ][1 :]
345
362
try :
@@ -348,31 +365,14 @@ def __call__(cls, path, **kwargs):
348
365
error = ValueError ("No engine for filetype: '%s'" % ext )
349
366
raise error
350
367
cls = get_writer (engine )
351
- writer = cls .__new__ (cls , path , ** kwargs )
352
- writer .__init__ (path , ** kwargs )
353
- return writer
354
-
355
368
356
- @add_metaclass (ExcelWriterMeta )
357
- class ExcelWriter (object ):
358
- """
359
- Class for writing DataFrame objects into excel sheets, default is to use
360
- xlwt for xls, openpyxl for xlsx. See DataFrame.to_excel for typical usage.
369
+ return object .__new__ (cls )
361
370
362
- Parameters
363
- ----------
364
- path : string
365
- Path to xls or xlsx file.
366
- engine : string (optional)
367
- Engine to use for writing. If None, defaults to ``io.excel.<extension>.writer``.
368
- NOTE: can only be passed as a keyword argument.
369
- """
370
371
# declare external properties you can count on
371
372
book = None
372
373
curr_sheet = None
373
374
path = None
374
375
375
-
376
376
@abc .abstractproperty
377
377
def supported_extensions (self ):
378
378
"extensions that writer engine supports"
@@ -407,9 +407,6 @@ def save(self):
407
407
pass
408
408
409
409
def __init__ (self , path , engine = None , ** engine_kwargs ):
410
- # note that subclasses will *never* get anything for engine
411
- # included here so that it's visible as part of the public signature.
412
-
413
410
# validate that this engine can handle the extnesion
414
411
ext = os .path .splitext (path )[- 1 ]
415
412
self .check_extension (ext )
@@ -455,7 +452,7 @@ class _OpenpyxlWriter(ExcelWriter):
455
452
engine = 'openpyxl'
456
453
supported_extensions = ('.xlsx' , '.xlsm' )
457
454
458
- def __init__ (self , path , ** engine_kwargs ):
455
+ def __init__ (self , path , engine = None , ** engine_kwargs ):
459
456
# Use the openpyxl module as the Excel writer.
460
457
from openpyxl .workbook import Workbook
461
458
@@ -511,6 +508,7 @@ def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0):
511
508
startrow + cell .row + 1 ,
512
509
cletterend ,
513
510
startrow + cell .mergestart + 1 ))
511
+
514
512
@classmethod
515
513
def _convert_to_style (cls , style_dict ):
516
514
"""
@@ -539,7 +537,7 @@ class _XlwtWriter(ExcelWriter):
539
537
engine = 'xlwt'
540
538
supported_extensions = ('.xls' ,)
541
539
542
- def __init__ (self , path , ** engine_kwargs ):
540
+ def __init__ (self , path , engine = None , ** engine_kwargs ):
543
541
# Use the xlwt module as the Excel writer.
544
542
import xlwt
545
543
@@ -599,7 +597,8 @@ def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0):
599
597
val , style )
600
598
601
599
@classmethod
602
- def _style_to_xlwt (cls , item , firstlevel = True , field_sep = ',' , line_sep = ';' ):
600
+ def _style_to_xlwt (cls , item , firstlevel = True , field_sep = ',' ,
601
+ line_sep = ';' ):
603
602
"""helper which recursively generate an xlwt easy style string
604
603
for example:
605
604
@@ -617,12 +616,12 @@ def _style_to_xlwt(cls, item, firstlevel=True, field_sep=',', line_sep=';'):
617
616
if hasattr (item , 'items' ):
618
617
if firstlevel :
619
618
it = ["%s: %s" % (key , cls ._style_to_xlwt (value , False ))
620
- for key , value in item .items ()]
619
+ for key , value in item .items ()]
621
620
out = "%s " % (line_sep ).join (it )
622
621
return out
623
622
else :
624
623
it = ["%s %s" % (key , cls ._style_to_xlwt (value , False ))
625
- for key , value in item .items ()]
624
+ for key , value in item .items ()]
626
625
out = "%s " % (field_sep ).join (it )
627
626
return out
628
627
else :
@@ -659,11 +658,11 @@ class _XlsxWriter(ExcelWriter):
659
658
engine = 'xlsxwriter'
660
659
supported_extensions = ('.xlsx' ,)
661
660
662
- def __init__ (self , path , ** engine_kwargs ):
661
+ def __init__ (self , path , engine = None , ** engine_kwargs ):
663
662
# Use the xlsxwriter module as the Excel writer.
664
663
import xlsxwriter
665
664
666
- super (_XlsxWriter , self ).__init__ (path , ** engine_kwargs )
665
+ super (_XlsxWriter , self ).__init__ (path , engine = engine , ** engine_kwargs )
667
666
668
667
self .book = xlsxwriter .Workbook (path , ** engine_kwargs )
669
668
0 commit comments